https://issues.shibboleth.net/jira/browse/SSPCPP-420
authorScott Cantor <cantor.2@osu.edu>
Fri, 10 Feb 2012 18:41:30 +0000 (18:41 +0000)
committerScott Cantor <cantor.2@osu.edu>
Fri, 10 Feb 2012 18:41:30 +0000 (18:41 +0000)
configure.ac
memcache-store/memcache-store.cpp
shibboleth.spec.in

index c72f7a5..be7693c 100644 (file)
@@ -511,6 +511,9 @@ if test "$WANT_MEMCACHED" != "no"; then
     fi
     AC_CHECK_HEADER([libmemcached/memcached.h],,
         AC_MSG_ERROR([unable to find Memcached header files]))
+    AC_CHECK_DECL([memcached_last_error_message],
+        [AC_DEFINE([HAVE_MEMCACHED_LAST_ERROR_MESSAGE],[1],[Define to 1 if libmemcached supports error handling function.])],,
+        [#include <libmemcached/memcached.h>])
     MEMCACHED_LIBS="-lmemcached"
 fi
 
index b5c8d81..e10ad8b 100644 (file)
@@ -42,6 +42,7 @@
 
 #include <list>
 #include <iostream> 
+#include <boost/scoped_ptr.hpp>
 #include <libmemcached/memcached.h>
 #include <xercesc/util/XMLUniDefs.hpp>
 
 using namespace xmltooling::logging;
 using namespace xmltooling;
 using namespace xercesc;
+using namespace boost;
 using namespace std;
 
-namespace xmltooling {
-  static const XMLCh Hosts[] = UNICODE_LITERAL_5(H,o,s,t,s);
-  static const XMLCh prefix[] = UNICODE_LITERAL_6(p,r,e,f,i,x);
-  static const XMLCh buildMap[] = UNICODE_LITERAL_8(b,u,i,l,d,M,a,p);
-  static const XMLCh sendTimeout[] = UNICODE_LITERAL_11(s,e,n,d,T,i,m,e,o,u,t);
-  static const XMLCh recvTimeout[] = UNICODE_LITERAL_11(r,e,c,v,T,i,m,e,o,u,t);
-  static const XMLCh pollTimeout[] = UNICODE_LITERAL_11(p,o,l,l,T,i,m,e,o,u,t);
-  static const XMLCh failLimit[] = UNICODE_LITERAL_9(f,a,i,l,L,i,m,i,t);
-  static const XMLCh retryTimeout[] = UNICODE_LITERAL_12(r,e,t,r,y,T,i,m,e,o,u,t);
-  static const XMLCh nonBlocking[] = UNICODE_LITERAL_11(n,o,n,B,l,o,c,k,i,n,g);
+namespace {
+    static const XMLCh Hosts[] = UNICODE_LITERAL_5(H,o,s,t,s);
+    static const XMLCh prefix[] = UNICODE_LITERAL_6(p,r,e,f,i,x);
+    static const XMLCh buildMap[] = UNICODE_LITERAL_8(b,u,i,l,d,M,a,p);
+    static const XMLCh sendTimeout[] = UNICODE_LITERAL_11(s,e,n,d,T,i,m,e,o,u,t);
+    static const XMLCh recvTimeout[] = UNICODE_LITERAL_11(r,e,c,v,T,i,m,e,o,u,t);
+    static const XMLCh pollTimeout[] = UNICODE_LITERAL_11(p,o,l,l,T,i,m,e,o,u,t);
+    static const XMLCh failLimit[] = UNICODE_LITERAL_9(f,a,i,l,L,i,m,i,t);
+    static const XMLCh retryTimeout[] = UNICODE_LITERAL_12(r,e,t,r,y,T,i,m,e,o,u,t);
+    static const XMLCh nonBlocking[] = UNICODE_LITERAL_11(n,o,n,B,l,o,c,k,i,n,g);
   
-  class mc_record {
-  public:
-    string value;
-    time_t expiration;
-    mc_record(){};
-    mc_record(string _v, time_t _e) :
-      value(_v), expiration(_e)
-    {}
-  };
-
-  class MemcacheBase {
-  public:
-    MemcacheBase(const DOMElement* e);
-    ~MemcacheBase();
+    class mc_record {
+    public:
+        string value;
+        time_t expiration;
+        mc_record() {};
+        mc_record(string _v, time_t _e) : value(_v), expiration(_e) {}
+    };
+
+    class MemcacheBase {
+    public:
+        MemcacheBase(const DOMElement* e);
+        ~MemcacheBase();
         
-    bool addMemcache(const char *key,
-                     string &value,
-                     time_t timeout,
-                     uint32_t flags,
-                     bool use_prefix = true);
-    bool setMemcache(const char *key,
-                     string &value,
-                     time_t timeout,
-                     uint32_t flags,
-                     bool use_prefix = true);
-    bool replaceMemcache(const char *key,
-                         string &value,
-                         time_t timeout,
-                         uint32_t flags,
-                         bool use_prefix = true);
-    bool getMemcache(const char *key,
-                     string &dest,
-                     uint32_t *flags,
-                     bool use_prefix = true);
-    bool deleteMemcache(const char *key,
-                        time_t timeout,
-                        bool use_prefix = true);
-
-    void serialize(mc_record &source, string &dest);
-    void serialize(list<string> &source, string &dest);
-    void deserialize(string &source, mc_record &dest);
-    void deserialize(string &source, list<string> &dest);
-
-    bool addLock(string what, bool use_prefix = true);
-    void deleteLock(string what, bool use_prefix = true);
-
-  protected:
-    const DOMElement* m_root; // can only use this during initialization
-    Category& log;
-    memcached_st *memc;
-    string m_prefix;
-    Mutex* m_lock;
-  };
+        bool addMemcache(const char* key, string &value, time_t timeout, uint32_t flags, bool use_prefix = true);
+        bool setMemcache(const char* key, string &value, time_t timeout, uint32_t flags, bool use_prefix = true);
+        bool replaceMemcache(const char* key, string &value, time_t timeout, uint32_t flags, bool use_prefix = true);
+        bool getMemcache(const char* key, string &dest, uint32_t *flags, bool use_prefix = true);
+        bool deleteMemcache(const char* key, time_t timeout, bool use_prefix = true);
+
+        void serialize(mc_record &source, string &dest);
+        void serialize(list<string> &source, string &dest);
+        void deserialize(string &source, mc_record &dest);
+        void deserialize(string &source, list<string> &dest);
+
+        bool addLock(string what, bool use_prefix = true);
+        void deleteLock(string what, bool use_prefix = true);
+
+    protected:
+        Category& m_log;
+        memcached_st* memc;
+        string m_prefix;
+        scoped_ptr<Mutex> m_lock;
+
+    private:
+        bool handleError(const char*, memcached_return) const;
+    };
   
-  class MemcacheStorageService : public StorageService, public MemcacheBase {
+    class MemcacheStorageService : public StorageService, public MemcacheBase {
 
-  public:
-    MemcacheStorageService(const DOMElement* e);
-    ~MemcacheStorageService();
+    public:
+        MemcacheStorageService(const DOMElement* e);
+        ~MemcacheStorageService() {}
     
-    const Capabilities& getCapabilities() const {
-        return m_caps;
-    }
-
-    bool createString(const char* context, const char* key, const char* value, time_t expiration);
-    int readString(const char* context, const char* key, string* pvalue=nullptr, time_t* pexpiration=nullptr, int version=0);
-    int updateString(const char* context, const char* key, const char* value=nullptr, time_t expiration=0, int version=0);
-    bool deleteString(const char* context, const char* key);
+        const Capabilities& getCapabilities() const {
+            return m_caps;
+        }
+
+        bool createString(const char* context, const char* key, const char* value, time_t expiration);
+        int readString(const char* context, const char* key, string* pvalue=nullptr, time_t* pexpiration=nullptr, int version=0);
+        int updateString(const char* context, const char* key, const char* value=nullptr, time_t expiration=0, int version=0);
+        bool deleteString(const char* context, const char* key);
     
-    bool createText(const char* context, const char* key, const char* value, time_t expiration) {
-      return createString(context, key, value, expiration);
-    }
-    int readText(const char* context, const char* key, string* pvalue=nullptr, time_t* pexpiration=nullptr, int version=0) {
-      return readString(context, key, pvalue, pexpiration, version);
-    }
-    int updateText(const char* context, const char* key, const char* value=nullptr, time_t expiration=0, int version=0) {
-      return updateString(context, key, value, expiration, version);
-    }
-    bool deleteText(const char* context, const char* key) {
-      return deleteString(context, key);
-    }
+        bool createText(const char* context, const char* key, const char* value, time_t expiration) {
+            return createString(context, key, value, expiration);
+        }
+        int readText(const char* context, const char* key, string* pvalue=nullptr, time_t* pexpiration=nullptr, int version=0) {
+            return readString(context, key, pvalue, pexpiration, version);
+        }
+        int updateText(const char* context, const char* key, const char* value=nullptr, time_t expiration=0, int version=0) {
+            return updateString(context, key, value, expiration, version);
+        }
+        bool deleteText(const char* context, const char* key) {
+            return deleteString(context, key);
+        }
     
-    void reap(const char* context) {}
+        void reap(const char* context) {}
 
-    void updateContext(const char* context, time_t expiration);
-    void deleteContext(const char* context);
+        void updateContext(const char* context, time_t expiration);
+        void deleteContext(const char* context);
 
     private:
+        Capabilities m_caps;
+        bool m_buildMap;
+    };
 
-    Category& m_log;
-    Capabilities m_caps;
-    bool m_buildMap;
-  };
+    StorageService* MemcacheStorageServiceFactory(const DOMElement* const & e) {
+        return new MemcacheStorageService(e);
+    }
+};
 
-  StorageService* MemcacheStorageServiceFactory(const DOMElement* const & e) {
-    return new MemcacheStorageService(e);
-  }
+MemcacheBase::MemcacheBase(const DOMElement* e)
+    : m_log(Category::getInstance("XMLTooling.StorageService.MEMCACHE")), memc(nullptr),
+        m_prefix(XMLHelper::getAttrString(e, nullptr, prefix)), m_lock(Mutex::create())
+{
+    memc = memcached_create(nullptr);
+    if (!memc)
+        throw XMLToolingException("MemcacheBase::Memcache(): memcached_create() failed");
+    m_log.debug("Memcache created");
+
+    memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, MEMCACHED_HASH_CRC);
+    m_log.debug("CRC hash set");
+
+    int prop = XMLHelper::getAttrInt(e, 999999, sendTimeout);
+    m_log.debug("MEMCACHED_BEHAVIOR_SND_TIMEOUT will be set to %d", prop);
+    memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SND_TIMEOUT, prop);
+
+    prop = XMLHelper::getAttrInt(e, 999999, recvTimeout);
+    m_log.debug("MEMCACHED_BEHAVIOR_RCV_TIMEOUT will be set to %d", prop);
+    memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, prop);
+
+    prop = XMLHelper::getAttrInt(e, 1000, pollTimeout);
+    m_log.debug("MEMCACHED_BEHAVIOR_POLL_TIMEOUT will be set to %d", prop);
+    memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, prop);
+
+    prop = XMLHelper::getAttrInt(e, 5, failLimit);
+    m_log.debug("MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT will be set to %d", prop);
+    memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, prop);
+
+    prop = XMLHelper::getAttrInt(e, 30, retryTimeout);
+    m_log.debug("MEMCACHED_BEHAVIOR_RETRY_TIMEOUT will be set to %d", prop);
+    memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, prop);
+
+    prop = XMLHelper::getAttrInt(e, 1, nonBlocking);
+    m_log.debug("MEMCACHED_BEHAVIOR_NO_BLOCK will be set to %d", prop);
+    memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, prop);
+
+    // Grab hosts from the configuration.
+    e = e ? XMLHelper::getFirstChildElement(e, Hosts) : nullptr;
+    if (!e || !e->hasChildNodes()) {
+        memcached_free(memc);
+        throw XMLToolingException("Memcache StorageService requires Hosts element in configuration.");
+    }
+    auto_ptr_char h(e->getTextContent());
+    m_log.debug("INIT: GOT Hosts: %s", h.get());
+    memcached_server_st* servers;
+    servers = memcached_servers_parse(const_cast<char*>(h.get()));
+    m_log.debug("Got %u hosts.",  memcached_server_list_count(servers));
+    if (memcached_server_push(memc, servers) != MEMCACHED_SUCCESS) {
+        memcached_server_list_free(servers);
+        memcached_free(memc);
+        throw IOException("MemcacheBase: memcached_server_push() failed");
+    }
+    memcached_server_list_free(servers);
+
+    m_log.debug("Memcache object initialized");
+}
+
+MemcacheBase::~MemcacheBase()
+{
+    memcached_free(memc);
+    m_log.debug("Base object destroyed");
+}
 
-};
 
-bool MemcacheBase::addLock(string what, bool use_prefix) {
-  string lock_name = what + ":LOCK";
-  string set_val = "1";
-  unsigned tries = 5;
-  while (!addMemcache(lock_name.c_str(), set_val, 5, 0, use_prefix)) {
-    if (tries-- == 0) {
-      log.debug("Unable to get lock %s... FAILED.", lock_name.c_str());
-      return false;
+bool MemcacheBase::handleError(const char* fn, memcached_return rv) const
+{
+#ifdef HAVE_MEMCACHED_LAST_ERROR_MESSAGE
+    string error = string("Memcache::") + fn + ": " + memcached_last_error_message(memc);
+#else
+    string error;
+    if (rv == MEMCACHED_ERRNO) {
+        // System error
+        error = string("Memcache::") + fn + "SYSTEM ERROR: " + strerror(memc->cached_errno);
+    }
+    else {
+        error = string("Memcache::") + fn + " Problems: " + memcached_strerror(memc, rv);
     }
-    log.debug("Unable to get lock %s... Retrying.", lock_name.c_str());
+#endif
+    m_log.error(error);
+    throw IOException(error);
+}
+
+bool MemcacheBase::addLock(string what, bool use_prefix)
+{
+    string lock_name = what + ":LOCK";
+    string set_val = "1";
+    unsigned tries = 5;
+    while (!addMemcache(lock_name.c_str(), set_val, 5, 0, use_prefix)) {
+        if (tries-- == 0) {
+            m_log.debug("Unable to get lock %s... FAILED.", lock_name.c_str());
+            return false;
+        }
+        m_log.debug("Unable to get lock %s... Retrying.", lock_name.c_str());
     
-    // sleep 100ms
+        // sleep 100ms
 #ifdef WIN32
-    Sleep(100);
+        Sleep(100);
 #else
-    struct timeval tv = { 0, 100000 };
-    select(0, 0, 0, 0, &tv);
+        struct timeval tv = { 0, 100000 };
+        select(0, 0, 0, 0, &tv);
 #endif
-  }
-  return true;
+    }
+    return true;
 }
 
-void MemcacheBase::deleteLock(string what, bool use_prefix) {
-
-  string lock_name = what + ":LOCK";
-  deleteMemcache(lock_name.c_str(), 0, use_prefix);
-  return;
+void MemcacheBase::deleteLock(string what, bool use_prefix)
+{
+    string lock_name = what + ":LOCK";
+    deleteMemcache(lock_name.c_str(), 0, use_prefix);
+    return;
 
 }  
 
-void MemcacheBase::deserialize(string &source, mc_record &dest) {
-  istringstream is(source, stringstream::in | stringstream::out);
-  is >> dest.expiration;
-  is.ignore(1); // ignore delimiter
-  dest.value = is.str().c_str() + is.tellg();
+void MemcacheBase::deserialize(string& source, mc_record& dest)
+{
+    istringstream is(source, stringstream::in | stringstream::out);
+    is >> dest.expiration;
+    is.ignore(1); // ignore delimiter
+    dest.value = is.str().c_str() + is.tellg();
 }
 
-void MemcacheBase::deserialize(string &source, list<string> &dest) {
-  istringstream is(source, stringstream::in | stringstream::out);
-  while (!is.eof()) {
-    string s;
-    is >> s;
-    dest.push_back(s);
-  }  
+void MemcacheBase::deserialize(string& source, list<string>& dest)
+{
+    istringstream is(source, stringstream::in | stringstream::out);
+    while (!is.eof()) {
+        string s;
+        is >> s;
+        dest.push_back(s);
+    }
 }
 
-void MemcacheBase::serialize(mc_record &source, string &dest) {
-  ostringstream os(stringstream::in | stringstream::out);
-  os << source.expiration;
-  os << "-"; // delimiter
-  os << source.value;
-  dest = os.str();
+void MemcacheBase::serialize(mc_record& source, string& dest)
+{
+    ostringstream os(stringstream::in | stringstream::out);
+    os << source.expiration;
+    os << "-"; // delimiter
+    os << source.value;
+    dest = os.str();
 }
 
-void MemcacheBase::serialize(list<string> &source, string &dest) {  
-  ostringstream os(stringstream::in | stringstream::out);
-  for(list<string>::iterator iter = source.begin(); iter != source.end(); iter++) {
-    if (iter != source.begin()) {
-      os << endl;
+void MemcacheBase::serialize(list<string>& source, string& dest)
+{
+    ostringstream os(stringstream::in | stringstream::out);
+    for(list<string>::iterator iter = source.begin(); iter != source.end(); iter++) {
+        if (iter != source.begin()) {
+            os << endl;
+        }
+        os << *iter;
     }
-    os << *iter;
-  }
-  dest = os.str();
+    dest = os.str();
 }
 
-bool MemcacheBase::deleteMemcache(const char *key,
-                                  time_t timeout,
-                                  bool use_prefix) {
-  memcached_return rv;
-  string final_key;
-  bool success;
-
-  if (use_prefix) {
-    final_key = m_prefix + key;
-  } else {
-    final_key = key;
-  }
-
-  m_lock->lock();
-  rv = memcached_delete(memc, (char *)final_key.c_str(), final_key.length(), timeout);
-  m_lock->unlock();
-
-  if (rv == MEMCACHED_SUCCESS) {
-    success = true;
-  } else if (rv == MEMCACHED_NOTFOUND) {
-    // Key wasn't there... No biggie.
-    success = false;
-  } else if (rv == MEMCACHED_ERRNO) {
-    // System error
-    string error = string("Memcache::deleteMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));
-    log.error(error);
-    throw IOException(error);
-  } else {
-    string error = string("Memcache::deleteMemcache() Problems: ") + memcached_strerror(memc, rv);
-    log.error(error);
-    throw IOException(error);
-  }
-
-  return success;
+bool MemcacheBase::deleteMemcache(const char* key, time_t timeout, bool use_prefix)
+{
+    string final_key;
+    if (use_prefix)
+        final_key = m_prefix + key;
+    else
+        final_key = key;
+
+    Lock lock(m_lock);
+    memcached_return rv = memcached_delete(memc, const_cast<char*>(final_key.c_str()), final_key.length(), timeout);
+
+    switch (rv) {
+        case MEMCACHED_SUCCESS:
+            return true;
+        case MEMCACHED_NOTFOUND:
+            // Key wasn't there... No biggie.
+            return false;
+        default:
+            return handleError("deleteMemcache", rv);
+    }
 }
 
-bool MemcacheBase::getMemcache(const char *key,
-                               string &dest,
-                               uint32_t *flags,
-                               bool use_prefix) {
-  memcached_return rv;
-  size_t len;
-  char *result;
-  string final_key;
-  bool success;
-  
-  if (use_prefix) {
-    final_key = m_prefix + key;
-  } else {
-    final_key = key;
-  }
-
-  m_lock->lock();
-  result = memcached_get(memc, (char *)final_key.c_str(), final_key.length(), &len, flags, &rv);
-  m_lock->unlock();
-
-  if (rv == MEMCACHED_SUCCESS) {
-    dest = result;
-    free(result);
-    success = true;
-  } else if (rv == MEMCACHED_NOTFOUND) {
-    log.debug("Key %s not found in memcache...", key);
-    success = false;
-  } else if (rv == MEMCACHED_ERRNO) {
-    // System error
-    string error = string("Memcache::getMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));
-    log.error(error);
-    throw IOException(error);
-  } else {
-    string error = string("Memcache::getMemcache() Problems: ") + memcached_strerror(memc, rv);
-    log.error(error);
-    throw IOException(error);
-  }
-
-  return success;
+bool MemcacheBase::getMemcache(const char* key, string& dest, uint32_t* flags, bool use_prefix)
+{
+    string final_key;
+    if (use_prefix)
+        final_key = m_prefix + key;
+    else
+        final_key = key;
+
+    Lock lock(m_lock);
+    size_t len;
+    memcached_return rv;
+    char* result = memcached_get(memc, const_cast<char*>(final_key.c_str()), final_key.length(), &len, flags, &rv);
+
+    switch (rv) {
+        case MEMCACHED_SUCCESS:
+            dest = result;
+            free(result);
+            return true;
+        case MEMCACHED_NOTFOUND:
+            m_log.debug("Key %s not found in memcache...", key);
+            return false;
+        default:
+            return handleError("getMemcache", rv);
+    }
 }
 
-bool MemcacheBase::addMemcache(const char *key,
-                               string &value,
-                               time_t timeout,
-                               uint32_t flags,
-                               bool use_prefix) {
-
-  memcached_return rv;
-  string final_key;
-  bool success;
-
-  if (use_prefix) {
-    final_key = m_prefix + key;
-  } else {
-    final_key = key;
-  }
-
-  m_lock->lock();
-  rv = memcached_add(memc, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);
-  m_lock->unlock();
-
-  if (rv == MEMCACHED_SUCCESS) {
-    success = true;
-  } else if (rv == MEMCACHED_NOTSTORED) {
-    // already there
-    success = false;
-  } else if (rv == MEMCACHED_ERRNO) {
-    // System error
-    string error = string("Memcache::addMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));
-    log.error(error);
-    throw IOException(error);
-  } else {
-    string error = string("Memcache::addMemcache() Problems: ") + memcached_strerror(memc, rv);
-    log.error(error);
-    throw IOException(error);
-  }
-
-  return success;
+bool MemcacheBase::addMemcache(const char* key, string& value, time_t timeout, uint32_t flags, bool use_prefix)
+{
+    string final_key;
+    if (use_prefix)
+        final_key = m_prefix + key;
+    else
+        final_key = key;
+
+    Lock lock(m_lock);
+    memcached_return rv = memcached_add(
+        memc, const_cast<char*>(final_key.c_str()), final_key.length(), const_cast<char*>(value.c_str()), value.length(), timeout, flags
+        );
+
+    switch (rv) {
+        case MEMCACHED_SUCCESS:
+            return true;
+        case MEMCACHED_NOTSTORED:
+            return false;
+        default:
+            return handleError("addMemcache", rv);
+    }
 }
 
-bool MemcacheBase::setMemcache(const char *key,
-                               string &value,
-                               time_t timeout,
-                               uint32_t flags,
-                               bool use_prefix) {
-
-  memcached_return rv;
-  string final_key;
-  bool success;
-
-  if (use_prefix) {
-    final_key = m_prefix + key;
-  } else {
-    final_key = key;
-  }
-
-  m_lock->lock();
-  rv = memcached_set(memc, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);
-  m_lock->unlock();
-
-  if (rv == MEMCACHED_SUCCESS) {
-    success = true;
-  } else if (rv == MEMCACHED_ERRNO) {
-    // System error
-    string error = string("Memcache::setMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));
-    log.error(error);
-    throw IOException(error);
-  } else {
-    string error = string("Memcache::setMemcache() Problems: ") + memcached_strerror(memc, rv);
-    log.error(error);
-    throw IOException(error);
-  }
-
-  return success;
+bool MemcacheBase::setMemcache(const char* key, string& value, time_t timeout, uint32_t flags, bool use_prefix)
+{
+    string final_key;
+    if (use_prefix)
+        final_key = m_prefix + key;
+    else
+        final_key = key;
+
+    Lock lock(m_lock);
+    memcached_return rv = memcached_set(
+        memc, const_cast<char*>(final_key.c_str()), final_key.length(), const_cast<char*>(value.c_str()), value.length(), timeout, flags
+        );
+
+    if (rv == MEMCACHED_SUCCESS)
+        return true;
+    return handleError("setMemcache", rv);
 }
 
-bool MemcacheBase::replaceMemcache(const char *key,
-                                   string &value,
-                                   time_t timeout,
-                                   uint32_t flags,
-                                   bool use_prefix) {
+bool MemcacheBase::replaceMemcache(const char* key, string& value, time_t timeout, uint32_t flags, bool use_prefix)
+{
   
-  memcached_return rv;
-  string final_key;
-  bool success;
-
-  if (use_prefix) {
-    final_key = m_prefix + key;
-  } else {
-    final_key = key;
-  }
-
-  m_lock->lock();
-  rv = memcached_replace(memc, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);
-  m_lock->unlock();
-
-  if (rv == MEMCACHED_SUCCESS) {
-    success = true;
-  } else if (rv == MEMCACHED_NOTSTORED) {
-    // not there
-    success = false;
-  } else if (rv == MEMCACHED_ERRNO) {
-    // System error
-    string error = string("Memcache::replaceMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));
-    log.error(error);
-    throw IOException(error);
-  } else {
-    string error = string("Memcache::replaceMemcache() Problems: ") + memcached_strerror(memc, rv);
-    log.error(error);
-    throw IOException(error);
-  }
-
-  return success;
-}
-
-MemcacheBase::MemcacheBase(const DOMElement* e) : m_root(e), log(Category::getInstance("XMLTooling.StorageService.MEMCACHE")), m_prefix("") {
-
-  auto_ptr_char p(e ? e->getAttributeNS(nullptr,prefix) : nullptr);
-  if (p.get() && *p.get()) {
-    log.debug("INIT: GOT key prefix: %s", p.get());
-    m_prefix = p.get();
-  }
-
-  m_lock = Mutex::create();
-  log.debug("Lock created");
-
-  memc = memcached_create(nullptr);
-  if (memc == nullptr) {
-    throw XMLToolingException("MemcacheBase::Memcache(): memcached_create() failed");
-  }
-
-  log.debug("Memcache created");
-
-  unsigned int hash = MEMCACHED_HASH_CRC;
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, hash);
-  log.debug("CRC hash set");
-
-  int32_t send_timeout = 999999;
-  const XMLCh* tag = e ? e->getAttributeNS(nullptr, sendTimeout) : nullptr;
-  if (tag && *tag) {
-    send_timeout = XMLString::parseInt(tag);
-  }
-  log.debug("MEMCACHED_BEHAVIOR_SND_TIMEOUT will be set to %d", send_timeout);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SND_TIMEOUT, send_timeout);
-
-  int32_t recv_timeout = 999999;
-  tag = e ? e->getAttributeNS(nullptr, sendTimeout) : nullptr;
-  if (tag && *tag) {
-    recv_timeout = XMLString::parseInt(tag);
-  }
-  log.debug("MEMCACHED_BEHAVIOR_RCV_TIMEOUT will be set to %d", recv_timeout);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, recv_timeout);
-
-  int32_t poll_timeout = 1000;
-  tag = e ? e->getAttributeNS(nullptr, pollTimeout) : nullptr;
-  if (tag && *tag) {
-    poll_timeout = XMLString::parseInt(tag);
-  }
-  log.debug("MEMCACHED_BEHAVIOR_POLL_TIMEOUT will be set to %d", poll_timeout);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, poll_timeout);
-
-  int32_t fail_limit = 5;
-  tag = e ? e->getAttributeNS(nullptr, failLimit) : nullptr;
-  if (tag && *tag) {
-    fail_limit = XMLString::parseInt(tag);
-  }
-  log.debug("MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT will be set to %d", fail_limit);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, fail_limit);
-
-  int32_t retry_timeout = 30;
-  tag = e ? e->getAttributeNS(nullptr, retryTimeout) : nullptr;
-  if (tag && *tag) {
-    retry_timeout = XMLString::parseInt(tag);
-  }
-  log.debug("MEMCACHED_BEHAVIOR_RETRY_TIMEOUT will be set to %d", retry_timeout);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, retry_timeout);
-
-  int32_t nonblock_set = 1;
-  tag = e ? e->getAttributeNS(nullptr, nonBlocking) : nullptr;
-  if (tag && *tag) {
-    nonblock_set = XMLString::parseInt(tag);
-  }
-  log.debug("MEMCACHED_BEHAVIOR_NO_BLOCK will be set to %d", nonblock_set);
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_NO_BLOCK, nonblock_set);
-
-  // Grab hosts from the configuration.
-  e = e ? XMLHelper::getFirstChildElement(e,Hosts) : nullptr;
-  if (!e || !e->hasChildNodes()) {
-    throw XMLToolingException("Memcache StorageService requires Hosts element in configuration.");
-  }
-  auto_ptr_char h(e->getFirstChild()->getNodeValue());
-  log.debug("INIT: GOT Hosts: %s", h.get());
-  memcached_server_st *servers;
-  servers = memcached_servers_parse(const_cast<char*>(h.get()));
-  log.debug("Got %u hosts.",  memcached_server_list_count(servers));
-  if (memcached_server_push(memc, servers) != MEMCACHED_SUCCESS) {
-    throw IOException("MemcacheBase::Memcache(): memcached_server_push() failed");    
-  }
-  memcached_server_list_free(servers);
-
-  log.debug("Memcache object initialized");
+    string final_key;
+    if (use_prefix)
+        final_key = m_prefix + key;
+    else
+        final_key = key;
+
+    Lock lock(m_lock);
+    memcached_return rv = memcached_replace(
+        memc, const_cast<char*>(final_key.c_str()), final_key.length(), const_cast<char*>(value.c_str()), value.length(), timeout, flags
+        );
+
+    switch (rv) {
+        case MEMCACHED_SUCCESS:
+            return true;
+        case MEMCACHED_NOTSTORED:
+            // not there
+            return false;
+        default:
+            return handleError("replaceMemcache", rv);
+    }
 }
 
-MemcacheBase::~MemcacheBase() {
-  memcached_free(memc);
-  delete m_lock;
-  log.debug("Base object destroyed");
-}
 
 MemcacheStorageService::MemcacheStorageService(const DOMElement* e)
-    : MemcacheBase(e), m_log(Category::getInstance("XMLTooling.StorageService.MEMCACHE")),
-        m_caps(80, 250 - m_prefix.length() - 1 - 80, 255),
-        m_buildMap(XMLHelper::getAttrBool(e, false, buildMap)) {
+    : MemcacheBase(e), m_caps(80, 250 - m_prefix.length() - 1 - 80, 255),
+        m_buildMap(XMLHelper::getAttrBool(e, false, buildMap))
+{
     if (m_buildMap)
         m_log.debug("Cache built with buildMap ON");
 }
 
-MemcacheStorageService::~MemcacheStorageService() {
-}
-
-bool MemcacheStorageService::createString(const char* context, const char* key, const char* value, time_t expiration) {
-
-  log.debug("createString ctx: %s - key: %s", context, key);
+bool MemcacheStorageService::createString(const char* context, const char* key, const char* value, time_t expiration)
+{
+    m_log.debug("createString ctx: %s - key: %s", context, key);
 
-  string final_key = string(context) + ":" + string(key);
+    string final_key = string(context) + ":" + string(key);
 
-  mc_record rec(value, expiration);
-  string final_value;
-  serialize(rec, final_value);
+    mc_record rec(value, expiration);
+    string final_value;
+    serialize(rec, final_value);
 
-  bool result = addMemcache(final_key.c_str(), final_value, expiration, 1); // the flag will be the version
+    bool result = addMemcache(final_key.c_str(), final_value, expiration, 1); // the flag will be the version
 
-  if (result && m_buildMap) {
-    log.debug("Got result, updating map");
+    if (result && m_buildMap) {
+        m_log.debug("Got result, updating map");
 
-    string map_name = context;
-    // we need to update the context map
-    if (! addLock(map_name)) {
-      log.error("Unable to get lock for context %s!", context);
-      deleteMemcache(final_key.c_str(), 0);
-      return false;
-    }
+        string map_name = context;
+        // we need to update the context map
+        if (!addLock(map_name)) {
+            m_log.error("Unable to get lock for context %s!", context);
+            deleteMemcache(final_key.c_str(), 0);
+            return false;
+        }
 
-    string ser_arr;
-    uint32_t flags;
-    bool result = getMemcache(map_name.c_str(), ser_arr, &flags);
+        string ser_arr;
+        uint32_t flags;
+        bool result = getMemcache(map_name.c_str(), ser_arr, &flags);
     
-    list<string> contents;
-    if (result) {
-      log.debug("Match found. Parsing...");
-
-      deserialize(ser_arr, contents);
-      
-      log.debug("Iterating retrieved session map...");
-      list<string>::iterator iter;
-      for(iter = contents.begin(); 
-          iter != contents.end();
-          iter++) {
-        log.debug("value = " + *iter);
-      }
-
-    } else {
-      log.debug("New context: %s", map_name.c_str());
-
+        list<string> contents;
+        if (result) {
+            m_log.debug("Match found. Parsing...");
+            deserialize(ser_arr, contents);
+            if (m_log.isDebugEnabled()) {
+                m_log.debug("Iterating retrieved session map...");
+                for(list<string>::const_iterator iter = contents.begin(); iter != contents.end(); ++iter)
+                    m_log.debug("value = %s", iter->c_str());
+            }
+        }
+        else {
+            m_log.debug("New context: %s", map_name.c_str());
+        }
+
+        contents.push_back(key);
+        serialize(contents, ser_arr);
+        setMemcache(map_name.c_str(), ser_arr, expiration, 0);
+        deleteLock(map_name);
     }
-
-    contents.push_back(key);
-    serialize(contents, ser_arr);    
-    setMemcache(map_name.c_str(), ser_arr, expiration, 0);    
-    
-    deleteLock(map_name);
-  }
-
-  return result;  
-
+    return result;
 }
 
-int MemcacheStorageService::readString(const char* context, const char* key, string* pvalue, time_t* pexpiration, int version) {
-
-  log.debug("readString ctx: %s - key: %s", context, key);
+int MemcacheStorageService::readString(const char* context, const char* key, string* pvalue, time_t* pexpiration, int version)
+{
+    m_log.debug("readString ctx: %s - key: %s", context, key);
 
-  string final_key = string(context) + ":" + string(key);
-  uint32_t rec_version;
-  string value;
-
-  if (m_buildMap) {
-    log.debug("Checking context");
-
-    string map_name = context;
-    string ser_arr;
-    uint32_t flags;
-    bool ctx_found = getMemcache(map_name.c_str(), ser_arr, &flags);
+    string final_key = string(context) + ":" + string(key);
+    uint32_t rec_version;
+    string value;
 
-    if (!ctx_found) {
-      return 0;
+    if (m_buildMap) {
+        m_log.debug("Checking context");
+        string map_name = context;
+        string ser_arr;
+        uint32_t flags;
+        bool ctx_found = getMemcache(map_name.c_str(), ser_arr, &flags);
+        if (!ctx_found)
+            return 0;
     }
-  }
 
-  bool found = getMemcache(final_key.c_str(), value, &rec_version);
-  if (!found) {
-    return 0;
-  }
+    bool found = getMemcache(final_key.c_str(), value, &rec_version);
+    if (!found)
+        return 0;
 
-  if (version && rec_version <= (uint32_t)version) {
-    return version;
-  }
+    if (version && rec_version <= (uint32_t)version)
+        return version;
 
-  if (pexpiration || pvalue) {
-    mc_record rec;
-    deserialize(value, rec);
+    if (pexpiration || pvalue) {
+        mc_record rec;
+        deserialize(value, rec);
     
-    if (pexpiration) {
-      *pexpiration = rec.expiration;
-    }
+        if (pexpiration)
+            *pexpiration = rec.expiration;
     
-    if (pvalue) {
-      *pvalue = rec.value;
+        if (pvalue)
+            *pvalue = rec.value;
     }
-  }
   
-  return rec_version;
-
+    return rec_version;
 }
 
-int MemcacheStorageService::updateString(const char* context, const char* key, const char* value, time_t expiration, int version) {
-
-  log.debug("updateString ctx: %s - key: %s", context, key);
+int MemcacheStorageService::updateString(const char* context, const char* key, const char* value, time_t expiration, int version)
+{
+    m_log.debug("updateString ctx: %s - key: %s", context, key);
 
-  time_t final_exp = expiration;
-  time_t *want_expiration = nullptr;
-  if (! final_exp) {
-    want_expiration = &final_exp;
-  }
+    time_t final_exp = expiration;
+    time_t* want_expiration = nullptr;
+    if (!final_exp)
+        want_expiration = &final_exp;
 
-  int read_res = readString(context, key, nullptr, want_expiration, version);
+    int read_res = readString(context, key, nullptr, want_expiration, version);
 
-  if (!read_res) {
-    // not found
-    return read_res;
-  }
-
-  if (version && version != read_res) {
-    // version incorrect
-    return -1;
-  }
+    if (!read_res) {
+        // not found
+        return read_res;
+    }
 
-  // Proceding with update
-  string final_key = string(context) + ":" + string(key);
-  mc_record rec(value, final_exp);
-  string final_value;
-  serialize(rec, final_value);
+    if (version && version != read_res) {
+        // version incorrect
+        return -1;
+    }
 
-  replaceMemcache(final_key.c_str(), final_value, final_exp, ++version);
-  return version;
+    // Proceding with update
+    string final_key = string(context) + ":" + string(key);
+    mc_record rec(value, final_exp);
+    string final_value;
+    serialize(rec, final_value);
 
+    replaceMemcache(final_key.c_str(), final_value, final_exp, ++version);
+    return version;
 }
 
-bool MemcacheStorageService::deleteString(const char* context, const char* key) {
-
-  log.debug("deleteString ctx: %s - key: %s", context, key);
+bool MemcacheStorageService::deleteString(const char* context, const char* key)
+{
+    m_log.debug("deleteString ctx: %s - key: %s", context, key);
   
-  string final_key = string(context) + ":" + string(key);
-
-  // Not updating context map, if there is one. There is no need.
-
-  return deleteMemcache(final_key.c_str(), 0);
+    string final_key = string(context) + ":" + string(key);
 
+    // Not updating context map, if there is one. There is no need.
+    return deleteMemcache(final_key.c_str(), 0);
 }
 
-void MemcacheStorageService::updateContext(const char* context, time_t expiration) {
+void MemcacheStorageService::updateContext(const char* context, time_t expiration)
+{
 
-  log.debug("updateContext ctx: %s", context);
+    m_log.debug("updateContext ctx: %s", context);
 
-  if (!m_buildMap) {
-    log.error("updateContext invoked on a Storage with no context map built!");
-    return;
-  }
+    if (!m_buildMap) {
+        m_log.error("updateContext invoked on a Storage with no context map built!");
+        return;
+    }
 
-  string map_name = context;
-  string ser_arr;
-  uint32_t flags;
-  bool result = getMemcache(map_name.c_str(), ser_arr, &flags);
+    string map_name = context;
+    string ser_arr;
+    uint32_t flags;
+    bool result = getMemcache(map_name.c_str(), ser_arr, &flags);
   
-  list<string> contents;
-  if (result) {
-    log.debug("Match found. Parsing...");
-    
-    deserialize(ser_arr, contents);
+    list<string> contents;
+    if (result) {
+        m_log.debug("Match found. Parsing...");
+        deserialize(ser_arr, contents);
     
-    log.debug("Iterating retrieved session map...");
-    list<string>::iterator iter;
-    for(iter = contents.begin(); 
-        iter != contents.end();
-        iter++) {
-
-      // Update expiration times
-      string value;      
-      int read_res = readString(context, iter->c_str(), &value, nullptr, 0);
-      
-      if (!read_res) {
-        // not found
-        continue;
-      }
-
-      updateString(context, iter->c_str(), value.c_str(), expiration, read_res);
+        m_log.debug("Iterating retrieved session map...");
+        for(list<string>::const_iterator iter = contents.begin(); iter != contents.end(); ++iter) {
+            // Update expiration times
+            string value;
+            int read_res = readString(context, iter->c_str(), &value, nullptr, 0);
+            if (!read_res) {
+                // not found
+                continue;
+            }
+
+            updateString(context, iter->c_str(), value.c_str(), expiration, read_res);
+        }
+        replaceMemcache(map_name.c_str(), ser_arr, expiration, flags);
     }
-    replaceMemcache(map_name.c_str(), ser_arr, expiration, flags);
-  }
-  
 }
 
-void MemcacheStorageService::deleteContext(const char* context) {
+void MemcacheStorageService::deleteContext(const char* context)
+{
 
-  log.debug("deleteContext ctx: %s", context);
+    m_log.debug("deleteContext ctx: %s", context);
 
-  if (!m_buildMap) {
-    log.error("deleteContext invoked on a Storage with no context map built!");
-    return;
-  }
+    if (!m_buildMap) {
+        m_log.error("deleteContext invoked on a Storage with no context map built!");
+        return;
+    }
 
-  string map_name = context;
-  string ser_arr;
-  uint32_t flags;
-  bool result = getMemcache(map_name.c_str(), ser_arr, &flags);
+    string map_name = context;
+    string ser_arr;
+    uint32_t flags;
+    bool result = getMemcache(map_name.c_str(), ser_arr, &flags);
   
-  list<string> contents;
-  if (result) {
-    log.debug("Match found. Parsing...");
+    list<string> contents;
+    if (result) {
+        m_log.debug("Match found. Parsing...");
+        deserialize(ser_arr, contents);
     
-    deserialize(ser_arr, contents);
+        m_log.debug("Iterating retrieved session map...");
+        for (list<string>::const_iterator iter = contents.begin(); iter != contents.end(); ++iter) {
+            string final_key = map_name + *iter;
+            deleteMemcache(final_key.c_str(), 0);
+        }
     
-    log.debug("Iterating retrieved session map...");
-    list<string>::iterator iter;
-    for(iter = contents.begin(); 
-        iter != contents.end();
-        iter++) {
-      string final_key = map_name + *iter;
-      deleteMemcache(final_key.c_str(), 0);
+        deleteMemcache(map_name.c_str(), 0);
     }
-    
-    deleteMemcache(map_name.c_str(), 0);
-  }
-  
 }
 
 extern "C" int MCEXT_EXPORTS xmltooling_extension_init(void*) {
index 4d96d3c..e34c481 100644 (file)
@@ -23,12 +23,13 @@ BuildRequires:      libsaml-devel >= 2.5
 %{!?_with_log4cpp:BuildRequires: liblog4shib-devel}
 %if 0%{?rhel} >= 6 || 0%{?centos_version} >= 600
 Requires:              libcurl-openssl >= 7.21.7
-BuildRequires: chrpath
+BuildRequires: chrpath, libmemcached-devel
 %endif
 BuildRequires:  gcc-c++, zlib-devel, boost-devel >= 1.32.0
 %{!?_without_doxygen:BuildRequires: doxygen}
 %{!?_without_odbc:BuildRequires:unixODBC-devel}
 %{?_with_fastcgi:BuildRequires: fcgi-devel}
+%{?_with_memcached:BuildRequires: libmemcached-devel}
 %if "%{_vendor}" == "redhat"
 %{!?_without_builtinapache:BuildRequires: httpd-devel}
 BuildRequires: redhat-rpm-config
@@ -82,7 +83,11 @@ This package includes files needed for development with Shibboleth.
 %setup -q
 
 %build
-%configure %{?_without_odbc:--disable-odbc} %{?_without_adfs:--disable-adfs} %{?_with_fastcgi} %{?_with_memcached} %{?shib_options}
+%if 0%{?rhel} >= 6 || 0%{?centos_version} >= 600
+       %configure %{?_without_odbc:--disable-odbc} %{?_without_adfs:--disable-adfs} %{?_with_fastcgi} %{!?_without_memcached:--with-memcached} %{?shib_options}
+%else
+       %configure %{?_without_odbc:--disable-odbc} %{?_without_adfs:--disable-adfs} %{?_with_fastcgi} %{?_with_memcached} %{?shib_options}
+%endif
 %{__make} pkgdocdir=%{pkgdocdir}
 
 %install
@@ -323,7 +328,7 @@ fi
 %doc %{pkgdocdir}/api
 
 %changelog
-* Wed Dec 14 2011  Scott Cantor  <cantor.2@osu.edu>  - 2.5-1
+* Fri Feb 10 2012  Scott Cantor  <cantor.2@osu.edu>  - 2.5-1
 - Move logo and stylesheet to version-independent tree
 - Make shib.conf noreplace
 - Post-fixup of Alias commands in older shib.conf
@@ -331,6 +336,7 @@ fi
 - Move init customizations to /etc/sysconfig/shibd
 - Copy shibd restart for Red Hat to postun
 - Add boost-devel dependency
+- Build memcache plugin on RH6
 
 * Sun Jun 26 2011  Scott Cantor  <cantor.2@osu.edu>  - 2.4.3-1
 - Log files shouldn't be world readable.