Skip to Content.
Sympa Menu

shibboleth-dev - RE: Memcache StorageService WAS:ODBC Storage Service

Subject: Shibboleth Developers

List archive

RE: Memcache StorageService WAS:ODBC Storage Service


Chronological Thread 
  • From: André Cruz <>
  • To:
  • Subject: RE: Memcache StorageService WAS:ODBC Storage Service
  • Date: Mon, 14 Apr 2008 13:40:47 +0100

Hello.

On Mon, 2008-04-07 at 12:26 -0400, Scott Cantor wrote:
> I'm not against it, but I'm not about to go learn how to setup memcache and
> support people using it, and I sort of find that when I depend on things, I
> end up having to teach people how to use them.

There are memcache binaries for most OSes/distributions. Memcache works
with the default options... You just have to start the daemon, no
configuration files... :)

> > As I see it the only other way to cluster SPs is using the
> > the odbc storage which doesn't even work well in unix+mysql.
>
> In the majority of cases, clustering is an application issue. If an
> application isn't relying on the SP session, and most don't, what good is
> it to cluster that session by itself? It's usually easier to just use a 5
> minute Shib session and rely on a few minutes of stickiness.

Personally I don't like any kind of stickyness... And some
load-balancers don't support it in any form, let alone sticky sessions
with timeout..

> I just haven't seen more than one or two cases here where somebody was
> clustering *and* relying on the SP's session.
>
> > Memcache is much easier to deploy and probably faster.
>
> It's also not persistent, though, which is one of the reasons I did the
> ODBC thing. It's a slightly different use case.

Yes, it's not persistent. What will happen if a memcache server fails
(and I haven't seen this happen) is that sessions stored in that server
will disappear and people will have to login again, no biggie.

> Anyway, I don't care that much, I'm willing to check it in if people want
> it. (It would get done quicker if you have a tested autoconf patch to go
> with it.)

Attached is a patch which includes the plugin and modifications to
configure.ac and a new Makefile.am.

Best regards,
André
Index: memcache-store/Makefile.am
===================================================================
--- memcache-store/Makefile.am	(revision 0)
+++ memcache-store/Makefile.am	(revision 0)
@@ -0,0 +1,19 @@
+AUTOMAKE_OPTIONS = foreign
+
+plugindir = $(libdir)/@PACKAGE@
+plugin_LTLIBRARIES = memcache-store.la
+
+AM_CFLAGS = $(MEMCACHED_CFLAGS)
+AM_CXXFLAGS = $(MEMCACHED_CFLAGS)
+
+memcache_store_la_LIBADD = \
+	$(MEMCACHED_LIBS)
+
+memcache_store_la_SOURCES = \
+	memcache-store.cpp
+
+memcache_store_la_LDFLAGS = -module -avoid-version $(XMLSEC_LIBS)
+
+install-exec-hook:
+	for la in $(plugin_LTLIBRARIES) ; do rm -f $(DESTDIR)$(plugindir)/$$la ; done
+
Index: memcache-store/memcache-store.cpp
===================================================================
--- memcache-store/memcache-store.cpp	(revision 0)
+++ memcache-store/memcache-store.cpp	(revision 0)
@@ -0,0 +1,725 @@
+/**
+ * memcache-store.cpp
+ *
+ * Storage Service using memcache (pre memcache tags)
+ */
+
+// /bin/sh ../libtool --silent --tag=CXX --mode=compile g++ -DHAVE_CONFIG_H -I. -I. -I..   -I/usr/local/shib-sp2/include -pthread -Wall -O2 -DNDEBUG     -pthread -Wall  -g -D_DEBUG -MT memcache-store.lo -MD -MP -I/usr/local/shib-sp2/include/libmemcached -c -o memcache-store.lo memcache-store.cpp
+
+// /bin/sh ../libtool --silent --tag=CXX --mode=link g++  -pthread -Wall  -g -D_DEBUG  -L/usr/local/shib-sp2/lib -llog4shib -lnsl  -o memcache-store.la -rpath /usr/local/shib-sp2/lib/shibboleth -module -avoid-version -lsaml -lxml-security-c -lxmltooling -lmemcached memcache-store.lo -lxerces-c
+
+#if defined (_MSC_VER) || defined(__BORLANDC__)
+# include "config_win32.h"
+#else
+# include "config.h"
+#endif
+
+#include <xercesc/util/XMLUniDefs.hpp>
+
+#include <xmltooling/logging.h>
+
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/StorageService.h>
+#include <xmltooling/util/XMLHelper.h>
+
+#include <libmemcached/memcached.h>
+
+using namespace xmltooling::logging;
+using namespace xmltooling;
+using namespace xercesc;
+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);
+  
+  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();
+    
+    
+  protected:
+    const DOMElement* m_root; // can only use this during initialization
+    Category& log;
+    memcached_st *memc;
+    string m_memcacheHosts;
+    string m_prefix;
+    
+    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 addSessionToUser(string &key, string &user);
+    bool addLock(string what, bool use_prefix = true);
+    void deleteLock(string what, bool use_prefix = true);
+  };
+  
+  class MemcacheStorageService : public StorageService, public MemcacheBase {
+
+  public:
+    MemcacheStorageService(const DOMElement* e);
+    ~MemcacheStorageService();
+    
+    bool createString(const char* context, const char* key, const char* value, time_t expiration);
+    int readString(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0);
+    int updateString(const char* context, const char* key, const char* value=NULL, 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=NULL, time_t* pexpiration=NULL, int version=0) {
+      return readString(context, key, pvalue, pexpiration, version);
+    }
+    int updateText(const char* context, const char* key, const char* value=NULL, 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 updateContext(const char* context, time_t expiration);
+    void deleteContext(const char* context);
+
+    private:
+
+    Category& m_log;
+    bool m_buildMap;
+
+
+  };
+
+  StorageService* MemcacheStorageServiceFactory(const DOMElement* const & e) {
+    return new MemcacheStorageService(e);
+  }
+
+};
+
+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;
+    }
+    log.debug("Unable to get lock %s... Retrying.", lock_name.c_str());
+    
+    // sleep 100ms
+    struct timeval tv = { 0, 100000 };
+    select(0, 0, 0, 0, &tv);    
+  }
+  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::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::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;
+    }
+    os << *iter;
+  }
+  dest = os.str();
+}
+
+bool MemcacheBase::addSessionToUser(string &key, string &user) {
+
+  if (! addLock(user, false)) {
+    return false;
+  }
+
+  // Aquired lock
+
+  string sessid = m_prefix + key; // add specific prefix to session
+  string delimiter = ";";
+  string user_key = "UDATA:";
+  user_key += user;
+  string user_val;
+  uint32_t flags;
+  bool result = getMemcache(user_key.c_str(), user_val, &flags, false);
+
+  if (result) {
+    bool already_there = false;
+    // skip delimiters at beginning.
+    string::size_type lastPos = user_val.find_first_not_of(delimiter, 0);
+    
+    // find first "non-delimiter".
+    string::size_type pos = user_val.find_first_of(delimiter, lastPos);
+    
+    while (string::npos != pos || string::npos != lastPos) {
+      // found a token, add it to the vector.
+      string session = user_val.substr(lastPos, pos - lastPos);
+      if (strcmp(session.c_str(), sessid.c_str()) == 0) {
+        already_there = true;
+        break;
+      }
+      
+      // skip delimiters.  Note the "not_of"
+      lastPos = user_val.find_first_not_of(delimiter, pos);
+      
+      // find next "non-delimiter"
+      pos = user_val.find_first_of(delimiter, lastPos);
+    }
+    
+    if (!already_there) {
+      user_val += delimiter + sessid;
+      replaceMemcache(user_key.c_str(), user_val, 0, 0, false);
+    }
+  } else {
+    addMemcache(user_key.c_str(), sessid, 0, 0, false);
+  }
+
+  deleteLock(user, false);
+  return true;
+  
+}
+
+bool MemcacheBase::deleteMemcache(const char *key,
+                                  time_t timeout,
+                                  bool use_prefix) {
+  memcached_return rv;
+  string final_key;
+  memcached_st clone;
+  bool success;
+
+  if (use_prefix) {
+    final_key = m_prefix + key;
+  } else {
+    final_key = key;
+  }
+
+  if (memcached_clone(&clone, memc) == NULL) {
+    throw IOException("MemcacheBase::deleteMemcache(): memcached_clone() failed");
+  }
+
+  rv = memcached_delete(&clone, (char *)final_key.c_str(), final_key.length(), timeout);
+  if (rv == MEMCACHED_SUCCESS) {
+    success = true;
+  } else if (rv == MEMCACHED_NOTFOUND) {
+    // Key wasn't there... No biggie.
+    success = false;
+  } else {
+    log.error(string("Memcache::deleteMemcache() Problems: ") + memcached_strerror(&clone, rv));
+    // shouldn't be here
+    success = false;
+  }
+
+  memcached_free(&clone);
+  return success;
+}
+
+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;
+  memcached_st clone;
+  bool success;
+  
+  if (use_prefix) {
+    final_key = m_prefix + key;
+  } else {
+    final_key = key;
+  }
+
+  if (memcached_clone(&clone, memc) == NULL) {
+    throw IOException("MemcacheBase::getMemcache(): memcached_clone() failed");
+  }
+
+  result = memcached_get(&clone, (char *)final_key.c_str(), final_key.length(), &len, flags, &rv);
+  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 {
+    log.error(string("Memcache::getMemcache() Problems: ") + memcached_strerror(&clone, rv));
+    success = false;
+  }
+
+  memcached_free(&clone);
+  return success;
+}
+
+bool MemcacheBase::addMemcache(const char *key,
+                               string &value,
+                               time_t timeout,
+                               uint32_t flags,
+                               bool use_prefix) {
+
+  memcached_return rv;
+  string final_key;
+  memcached_st clone;
+  bool success;
+
+  if (use_prefix) {
+    final_key = m_prefix + key;
+  } else {
+    final_key = key;
+  }
+
+  if (memcached_clone(&clone, memc) == NULL) {
+    throw IOException("MemcacheBase::addMemcache(): memcached_clone() failed");
+  }
+
+  rv = memcached_add(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);
+  if (rv == MEMCACHED_SUCCESS) {
+    success = true;
+  } else if (rv == MEMCACHED_NOTSTORED) {
+    // already there
+    success = false;
+  } else {
+    // shouldn't be here
+    log.error(string("Memcache::addMemcache() Problems: ") + memcached_strerror(&clone, rv));
+    success = false;
+  }
+
+  memcached_free(&clone);
+  return success;
+}
+
+bool MemcacheBase::setMemcache(const char *key,
+                               string &value,
+                               time_t timeout,
+                               uint32_t flags,
+                               bool use_prefix) {
+
+  memcached_return rv;
+  string final_key;
+  memcached_st clone;
+  bool success;
+
+  if (use_prefix) {
+    final_key = m_prefix + key;
+  } else {
+    final_key = key;
+  }
+
+  if (memcached_clone(&clone, memc) == NULL) {
+    throw IOException("MemcacheBase::setMemcache(): memcached_clone() failed");
+  }
+
+  rv = memcached_set(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);
+  if (rv == MEMCACHED_SUCCESS) {
+    success = true;
+  } else {
+    // shouldn't be here
+    log.error(string("Memcache::setMemcache() Problems: ") + memcached_strerror(&clone, rv));
+    success = false;
+  }
+
+  memcached_free(&clone);
+  return success;
+}
+
+bool MemcacheBase::replaceMemcache(const char *key,
+                                   string &value,
+                                   time_t timeout,
+                                   uint32_t flags,
+                                   bool use_prefix) {
+  
+  memcached_return rv;
+  string final_key;
+  memcached_st clone;
+  bool success;
+
+  if (use_prefix) {
+    final_key = m_prefix + key;
+  } else {
+    final_key = key;
+  }
+
+  if (memcached_clone(&clone, memc) == NULL) {
+    throw IOException("MemcacheBase::replaceMemcache(): memcached_clone() failed");
+  }
+
+  rv = memcached_replace(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);
+  if (rv == MEMCACHED_SUCCESS) {
+    success = true;
+  } else if (rv == MEMCACHED_NOTSTORED) {
+    // not there
+    success = false;
+  } else {
+    // shouldn't be here
+    log.error(string("Memcache::replaceMemcache() Problems: ") + memcached_strerror(&clone, rv));
+    success = false;
+  }
+
+  memcached_free(&clone);
+  return success;
+}
+
+MemcacheBase::MemcacheBase(const DOMElement* e) : m_root(e), log(Category::getInstance("XMLTooling.MemcacheBase")), m_memcacheHosts(""), m_prefix("") {
+
+  auto_ptr_char p(e ? e->getAttributeNS(NULL,prefix) : NULL);
+  if (p.get() && *p.get()) {
+    log.debug("INIT: GOT key prefix: %s", p.get());
+    m_prefix = p.get();
+  }
+
+  // Grab hosts from the configuration.
+  e = e ? XMLHelper::getFirstChildElement(e,Hosts) : NULL;
+  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());
+  m_memcacheHosts = h.get();
+
+  memc = memcached_create(NULL);
+  if (memc == NULL) {
+    throw XMLToolingException("MemcacheBase::Memcache(): memcached_create() failed");
+  }
+
+  log.debug("Memcache created");
+
+  unsigned int set = MEMCACHED_HASH_CRC;
+  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, set);
+  log.debug("CRC hash set");
+
+  memcached_server_st *servers;
+  servers = memcached_servers_parse((char *)m_memcacheHosts.c_str());
+  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");
+}
+
+MemcacheBase::~MemcacheBase() {
+  memcached_free(memc);
+  log.debug("Base object destroyed");
+}
+
+MemcacheStorageService::MemcacheStorageService(const DOMElement* e)
+  : MemcacheBase(e), m_log(Category::getInstance("XMLTooling.MemcacheStorageService")), m_buildMap(false) {
+
+    const XMLCh* tag=e ? e->getAttributeNS(NULL,buildMap) : NULL;
+    if (tag && *tag && XMLString::parseInt(tag) != 0) {
+        m_buildMap = true;
+    }
+
+}
+
+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);
+
+  string final_key = string(context) + ":" + string(key);
+
+  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
+
+  if (result && buildMap) {
+    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 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());
+
+    }
+
+    contents.push_back(key);
+    serialize(contents, ser_arr);    
+    setMemcache(map_name.c_str(), ser_arr, expiration, 0);    
+    
+    deleteLock(map_name);
+  }
+
+  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);
+
+  string final_key = string(context) + ":" + string(key);
+  uint32_t rec_version;
+  string value;
+
+  bool found = getMemcache(final_key.c_str(), value, &rec_version);
+  if (!found) {
+    return 0;
+  }
+
+  if (version && rec_version <= (uint32_t)version) {
+    return version;
+  }
+
+  if (pexpiration || pvalue) {
+    mc_record rec;
+    deserialize(value, rec);
+    
+    if (pexpiration) {
+      *pexpiration = rec.expiration;
+    }
+    
+    if (pvalue) {
+      *pvalue = rec.value;
+    }
+  }
+  
+  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);
+
+  time_t final_exp = expiration;
+  time_t *want_expiration = NULL;
+  if (! final_exp) {
+    want_expiration = &final_exp;
+  }
+
+  int read_res = readString(context, key, NULL, want_expiration, version);
+
+  if (!read_res) {
+    // not found
+    return read_res;
+  }
+
+  if (version && version != read_res) {
+    // version incorrect
+    return -1;
+  }
+
+  // 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);
+  
+  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) {
+
+  log.debug("updateContext ctx: %s", context);
+
+  if (!buildMap) {
+    log.error("updateContext invoked on a Storage with no context map built!");
+    return;
+  }
+
+  string map_name = context;
+  
+  if (! addLock(map_name)) {
+    log.error("Unable to get lock for context %s!", context);
+    return;
+  }
+  
+  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++) {
+
+      // Update expiration times
+      string value;      
+      int read_res = readString(context, iter->c_str(), &value, NULL, 0);
+      
+      if (!read_res) {
+        // not found
+        continue;
+      }
+
+      updateString(context, iter->c_str(), value.c_str(), expiration, read_res);
+    }
+    
+  }
+  
+  deleteLock(map_name);
+  
+}
+
+void MemcacheStorageService::deleteContext(const char* context) {
+
+  log.debug("deleteContext ctx: %s", context);
+
+  if (!buildMap) {
+    log.error("deleteContext invoked on a Storage with no context map built!");
+    return;
+  }
+
+  string map_name = context;
+  
+  if (! addLock(map_name)) {
+    log.error("Unable to get lock for context %s!", context);
+    return;
+  }
+  
+  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++) {
+      string final_key = map_name + *iter;
+      deleteMemcache(final_key.c_str(), 0);
+    }
+    
+    deleteMemcache(map_name.c_str(), 0);
+  }
+  
+  deleteLock(map_name);
+
+}
+
+extern "C" int xmltooling_extension_init(void*) {
+    // Register this SS type
+    XMLToolingConfig::getConfig().StorageServiceManager.registerFactory("MEMCACHE", MemcacheStorageServiceFactory);
+    return 0;
+}
+
+extern "C" void xmltooling_extension_term() {
+    XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("MEMCACHE");
+}
Index: configure.ac
===================================================================
--- configure.ac	(revision 2800)
+++ configure.ac	(working copy)
@@ -436,7 +436,40 @@
     WANT_SUBDIRS="$WANT_SUBDIRS fastcgi"
 fi
 
+#
+# Build Memcached support?
+#
+AC_MSG_CHECKING(for Memcached support)
+AC_ARG_WITH(memcached,
+    AC_HELP_STRING([--with-memcached=DIR], [Build Memcached support]),
+    [WANT_MEMCACHED=$withval],[WANT_MEMCACHED=no])
+AC_MSG_RESULT($WANT_MEMCACHED)
 
+if test "$WANT_MEMCACHED" != "no"; then
+    if test "$WANT_MEMCACHED" != "yes"; then
+        if test x_$WANT_MEMCACHED != x_/usr; then
+            MEMCACHED_INCLUDE="-I$WANT_MEMCACHED/include"
+            MEMCACHED_LDFLAGS="-L$WANT_MEMCACHED/lib"
+        fi
+    fi
+    AC_CHECK_HEADER([libmemcached/memcached.h],,
+        AC_MSG_ERROR([unable to find Memcached header files]))
+    MEMCACHED_LIBS="-lmemcached"
+fi
+
+AC_SUBST(MEMCACHED_INCLUDE)
+AC_SUBST(MEMCACHED_LDFLAGS)
+AC_SUBST(MEMCACHED_LIBS)
+
+# always output the Makefile, even if you don't use it
+AC_CONFIG_FILES([memcache-store/Makefile])
+AM_CONDITIONAL(BUILD_MEMCACHED,test ! "$WANT_MEMCACHED" = "no")
+
+if test ! "$WANT_MEMCACHED" = "no" ; then
+    WANT_SUBDIRS="$WANT_SUBDIRS memcache-store"
+fi
+
+
 #
 # If no --enable-apache-xx specified 
 # find a default and fake the specific parameters



Archive powered by MHonArc 2.6.16.

Top of Page