+/*\r
+ * Copyright 2001-2008 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * memcache-store.cpp\r
+ *\r
+ * Storage Service using memcache (pre memcache tags)\r
+ */\r
+\r
+#if defined (_MSC_VER) || defined(__BORLANDC__)\r
+# include "config_win32.h"\r
+#else\r
+# include "config.h"\r
+#endif\r
+\r
+#include <xercesc/util/XMLUniDefs.hpp>\r
+\r
+#include <xmltooling/logging.h>\r
+\r
+#include <xmltooling/XMLToolingConfig.h>\r
+#include <xmltooling/util/NDC.h>\r
+#include <xmltooling/util/StorageService.h>\r
+#include <xmltooling/util/XMLHelper.h>\r
+\r
+#include <libmemcached/memcached.h>\r
+\r
+using namespace xmltooling::logging;\r
+using namespace xmltooling;\r
+using namespace xercesc;\r
+using namespace std;\r
+\r
+namespace xmltooling {\r
+ static const XMLCh Hosts[] = UNICODE_LITERAL_5(H,o,s,t,s);\r
+ static const XMLCh prefix[] = UNICODE_LITERAL_6(p,r,e,f,i,x);\r
+ static const XMLCh buildMap[] = UNICODE_LITERAL_8(b,u,i,l,d,M,a,p);\r
+ \r
+ class mc_record {\r
+ public:\r
+ string value;\r
+ time_t expiration;\r
+ mc_record(){};\r
+ mc_record(string _v, time_t _e) :\r
+ value(_v), expiration(_e)\r
+ {}\r
+ };\r
+\r
+ class MemcacheBase {\r
+ public:\r
+ MemcacheBase(const DOMElement* e);\r
+ ~MemcacheBase();\r
+ \r
+ bool addMemcache(const char *key,\r
+ string &value,\r
+ time_t timeout,\r
+ uint32_t flags,\r
+ bool use_prefix = true);\r
+ bool setMemcache(const char *key,\r
+ string &value,\r
+ time_t timeout,\r
+ uint32_t flags,\r
+ bool use_prefix = true);\r
+ bool replaceMemcache(const char *key,\r
+ string &value,\r
+ time_t timeout,\r
+ uint32_t flags,\r
+ bool use_prefix = true);\r
+ bool getMemcache(const char *key,\r
+ string &dest,\r
+ uint32_t *flags,\r
+ bool use_prefix = true);\r
+ bool deleteMemcache(const char *key,\r
+ time_t timeout,\r
+ bool use_prefix = true);\r
+\r
+ void serialize(mc_record &source, string &dest);\r
+ void serialize(list<string> &source, string &dest);\r
+ void deserialize(string &source, mc_record &dest);\r
+ void deserialize(string &source, list<string> &dest);\r
+\r
+ bool addSessionToUser(string &key, string &user);\r
+ bool addLock(string what, bool use_prefix = true);\r
+ void deleteLock(string what, bool use_prefix = true);\r
+\r
+ protected:\r
+ const DOMElement* m_root; // can only use this during initialization\r
+ Category& log;\r
+ memcached_st *memc;\r
+ string m_memcacheHosts;\r
+ string m_prefix;\r
+ \r
+ };\r
+ \r
+ class MemcacheStorageService : public StorageService, public MemcacheBase {\r
+\r
+ public:\r
+ MemcacheStorageService(const DOMElement* e);\r
+ ~MemcacheStorageService();\r
+ \r
+ bool createString(const char* context, const char* key, const char* value, time_t expiration);\r
+ int readString(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0);\r
+ int updateString(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0);\r
+ bool deleteString(const char* context, const char* key);\r
+ \r
+ bool createText(const char* context, const char* key, const char* value, time_t expiration) {\r
+ return createString(context, key, value, expiration);\r
+ }\r
+ int readText(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {\r
+ return readString(context, key, pvalue, pexpiration, version);\r
+ }\r
+ int updateText(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0) {\r
+ return updateString(context, key, value, expiration, version);\r
+ }\r
+ bool deleteText(const char* context, const char* key) {\r
+ return deleteString(context, key);\r
+ }\r
+ \r
+ void reap(const char* context) {}\r
+\r
+ void updateContext(const char* context, time_t expiration);\r
+ void deleteContext(const char* context);\r
+\r
+ private:\r
+\r
+ Category& m_log;\r
+ bool m_buildMap;\r
+\r
+\r
+ };\r
+\r
+ StorageService* MemcacheStorageServiceFactory(const DOMElement* const & e) {\r
+ return new MemcacheStorageService(e);\r
+ }\r
+\r
+};\r
+\r
+bool MemcacheBase::addLock(string what, bool use_prefix) {\r
+ string lock_name = what + ":LOCK";\r
+ string set_val = "1";\r
+ unsigned tries = 5;\r
+ while (!addMemcache(lock_name.c_str(), set_val, 5, 0, use_prefix)) {\r
+ if (tries-- < 0) {\r
+ log.debug("Unable to get lock %s... FAILED.", lock_name.c_str());\r
+ return false;\r
+ }\r
+ log.debug("Unable to get lock %s... Retrying.", lock_name.c_str());\r
+ \r
+ // sleep 100ms\r
+ struct timeval tv = { 0, 100000 };\r
+ select(0, 0, 0, 0, &tv); \r
+ }\r
+ return true;\r
+}\r
+\r
+void MemcacheBase::deleteLock(string what, bool use_prefix) {\r
+\r
+ string lock_name = what + ":LOCK";\r
+ deleteMemcache(lock_name.c_str(), 0, use_prefix);\r
+ return;\r
+\r
+} \r
+\r
+void MemcacheBase::deserialize(string &source, mc_record &dest) {\r
+ istringstream is(source, stringstream::in | stringstream::out);\r
+ is >> dest.expiration;\r
+ is.ignore(1); // ignore delimiter\r
+ dest.value = is.str().c_str() + is.tellg();\r
+}\r
+\r
+void MemcacheBase::deserialize(string &source, list<string> &dest) {\r
+ istringstream is(source, stringstream::in | stringstream::out);\r
+ while (!is.eof()) {\r
+ string s;\r
+ is >> s;\r
+ dest.push_back(s);\r
+ } \r
+}\r
+\r
+void MemcacheBase::serialize(mc_record &source, string &dest) {\r
+ ostringstream os(stringstream::in | stringstream::out);\r
+ os << source.expiration;\r
+ os << "-"; // delimiter\r
+ os << source.value;\r
+ dest = os.str();\r
+}\r
+\r
+void MemcacheBase::serialize(list<string> &source, string &dest) { \r
+ ostringstream os(stringstream::in | stringstream::out);\r
+ for(list<string>::iterator iter = source.begin(); iter != source.end(); iter++) {\r
+ if (iter != source.begin()) {\r
+ os << endl;\r
+ }\r
+ os << *iter;\r
+ }\r
+ dest = os.str();\r
+}\r
+\r
+bool MemcacheBase::addSessionToUser(string &key, string &user) {\r
+\r
+ if (! addLock(user, false)) {\r
+ return false;\r
+ }\r
+\r
+ // Aquired lock\r
+\r
+ string sessid = m_prefix + key; // add specific prefix to session\r
+ string delimiter = ";";\r
+ string user_key = "UDATA:";\r
+ user_key += user;\r
+ string user_val;\r
+ uint32_t flags;\r
+ bool result = getMemcache(user_key.c_str(), user_val, &flags, false);\r
+\r
+ if (result) {\r
+ bool already_there = false;\r
+ // skip delimiters at beginning.\r
+ string::size_type lastPos = user_val.find_first_not_of(delimiter, 0);\r
+ \r
+ // find first "non-delimiter".\r
+ string::size_type pos = user_val.find_first_of(delimiter, lastPos);\r
+ \r
+ while (string::npos != pos || string::npos != lastPos) {\r
+ // found a token, add it to the vector.\r
+ string session = user_val.substr(lastPos, pos - lastPos);\r
+ if (strcmp(session.c_str(), sessid.c_str()) == 0) {\r
+ already_there = true;\r
+ break;\r
+ }\r
+ \r
+ // skip delimiters. Note the "not_of"\r
+ lastPos = user_val.find_first_not_of(delimiter, pos);\r
+ \r
+ // find next "non-delimiter"\r
+ pos = user_val.find_first_of(delimiter, lastPos);\r
+ }\r
+ \r
+ if (!already_there) {\r
+ user_val += delimiter + sessid;\r
+ replaceMemcache(user_key.c_str(), user_val, 0, 0, false);\r
+ }\r
+ } else {\r
+ addMemcache(user_key.c_str(), sessid, 0, 0, false);\r
+ }\r
+\r
+ deleteLock(user, false);\r
+ return true;\r
+ \r
+}\r
+\r
+bool MemcacheBase::deleteMemcache(const char *key,\r
+ time_t timeout,\r
+ bool use_prefix) {\r
+ memcached_return rv;\r
+ string final_key;\r
+ memcached_st clone;\r
+ bool success;\r
+\r
+ if (use_prefix) {\r
+ final_key = m_prefix + key;\r
+ } else {\r
+ final_key = key;\r
+ }\r
+\r
+ if (memcached_clone(&clone, memc) == NULL) {\r
+ throw IOException("MemcacheBase::deleteMemcache(): memcached_clone() failed");\r
+ }\r
+\r
+ rv = memcached_delete(&clone, (char *)final_key.c_str(), final_key.length(), timeout);\r
+ if (rv == MEMCACHED_SUCCESS) {\r
+ success = true;\r
+ } else if (rv == MEMCACHED_NOTFOUND) {\r
+ // Key wasn't there... No biggie.\r
+ success = false;\r
+ } else {\r
+ log.error(string("Memcache::deleteMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
+ // shouldn't be here\r
+ success = false;\r
+ }\r
+\r
+ memcached_free(&clone);\r
+ return success;\r
+}\r
+\r
+bool MemcacheBase::getMemcache(const char *key,\r
+ string &dest,\r
+ uint32_t *flags,\r
+ bool use_prefix) {\r
+ memcached_return rv;\r
+ size_t len;\r
+ char *result;\r
+ string final_key;\r
+ memcached_st clone;\r
+ bool success;\r
+ \r
+ if (use_prefix) {\r
+ final_key = m_prefix + key;\r
+ } else {\r
+ final_key = key;\r
+ }\r
+\r
+ if (memcached_clone(&clone, memc) == NULL) {\r
+ throw IOException("MemcacheBase::getMemcache(): memcached_clone() failed");\r
+ }\r
+\r
+ result = memcached_get(&clone, (char *)final_key.c_str(), final_key.length(), &len, flags, &rv);\r
+ if (rv == MEMCACHED_SUCCESS) {\r
+ dest = result;\r
+ free(result);\r
+ success = true;\r
+ } else if (rv == MEMCACHED_NOTFOUND) {\r
+ log.debug("Key %s not found in memcache...", key);\r
+ success = false;\r
+ } else {\r
+ log.error(string("Memcache::getMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
+ success = false;\r
+ }\r
+\r
+ memcached_free(&clone);\r
+ return success;\r
+}\r
+\r
+bool MemcacheBase::addMemcache(const char *key,\r
+ string &value,\r
+ time_t timeout,\r
+ uint32_t flags,\r
+ bool use_prefix) {\r
+\r
+ memcached_return rv;\r
+ string final_key;\r
+ memcached_st clone;\r
+ bool success;\r
+\r
+ if (use_prefix) {\r
+ final_key = m_prefix + key;\r
+ } else {\r
+ final_key = key;\r
+ }\r
+\r
+ if (memcached_clone(&clone, memc) == NULL) {\r
+ throw IOException("MemcacheBase::addMemcache(): memcached_clone() failed");\r
+ }\r
+\r
+ rv = memcached_add(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);\r
+ if (rv == MEMCACHED_SUCCESS) {\r
+ success = true;\r
+ } else if (rv == MEMCACHED_NOTSTORED) {\r
+ // already there\r
+ success = false;\r
+ } else {\r
+ // shouldn't be here\r
+ log.error(string("Memcache::addMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
+ success = false;\r
+ }\r
+\r
+ memcached_free(&clone);\r
+ return success;\r
+}\r
+\r
+bool MemcacheBase::setMemcache(const char *key,\r
+ string &value,\r
+ time_t timeout,\r
+ uint32_t flags,\r
+ bool use_prefix) {\r
+\r
+ memcached_return rv;\r
+ string final_key;\r
+ memcached_st clone;\r
+ bool success;\r
+\r
+ if (use_prefix) {\r
+ final_key = m_prefix + key;\r
+ } else {\r
+ final_key = key;\r
+ }\r
+\r
+ if (memcached_clone(&clone, memc) == NULL) {\r
+ throw IOException("MemcacheBase::setMemcache(): memcached_clone() failed");\r
+ }\r
+\r
+ rv = memcached_set(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);\r
+ if (rv == MEMCACHED_SUCCESS) {\r
+ success = true;\r
+ } else {\r
+ // shouldn't be here\r
+ log.error(string("Memcache::setMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
+ success = false;\r
+ }\r
+\r
+ memcached_free(&clone);\r
+ return success;\r
+}\r
+\r
+bool MemcacheBase::replaceMemcache(const char *key,\r
+ string &value,\r
+ time_t timeout,\r
+ uint32_t flags,\r
+ bool use_prefix) {\r
+ \r
+ memcached_return rv;\r
+ string final_key;\r
+ memcached_st clone;\r
+ bool success;\r
+\r
+ if (use_prefix) {\r
+ final_key = m_prefix + key;\r
+ } else {\r
+ final_key = key;\r
+ }\r
+\r
+ if (memcached_clone(&clone, memc) == NULL) {\r
+ throw IOException("MemcacheBase::replaceMemcache(): memcached_clone() failed");\r
+ }\r
+\r
+ rv = memcached_replace(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);\r
+ if (rv == MEMCACHED_SUCCESS) {\r
+ success = true;\r
+ } else if (rv == MEMCACHED_NOTSTORED) {\r
+ // not there\r
+ success = false;\r
+ } else {\r
+ // shouldn't be here\r
+ log.error(string("Memcache::replaceMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
+ success = false;\r
+ }\r
+\r
+ memcached_free(&clone);\r
+ return success;\r
+}\r
+\r
+MemcacheBase::MemcacheBase(const DOMElement* e) : m_root(e), log(Category::getInstance("XMLTooling.MemcacheBase")), m_memcacheHosts(""), m_prefix("") {\r
+\r
+ auto_ptr_char p(e ? e->getAttributeNS(NULL,prefix) : NULL);\r
+ if (p.get() && *p.get()) {\r
+ log.debug("INIT: GOT key prefix: %s", p.get());\r
+ m_prefix = p.get();\r
+ }\r
+\r
+ // Grab hosts from the configuration.\r
+ e = e ? XMLHelper::getFirstChildElement(e,Hosts) : NULL;\r
+ if (!e || !e->hasChildNodes()) {\r
+ throw XMLToolingException("Memcache StorageService requires Hosts element in configuration.");\r
+ }\r
+ auto_ptr_char h(e->getFirstChild()->getNodeValue());\r
+ log.debug("INIT: GOT Hosts: %s", h.get());\r
+ m_memcacheHosts = h.get();\r
+\r
+ memc = memcached_create(NULL);\r
+ if (memc == NULL) {\r
+ throw XMLToolingException("MemcacheBase::Memcache(): memcached_create() failed");\r
+ }\r
+\r
+ log.debug("Memcache created");\r
+\r
+ unsigned int set = MEMCACHED_HASH_CRC;\r
+ memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, set);\r
+ log.debug("CRC hash set");\r
+\r
+ memcached_server_st *servers;\r
+ servers = memcached_servers_parse((char *)m_memcacheHosts.c_str());\r
+ log.debug("Got %u hosts.", memcached_server_list_count(servers));\r
+ if (memcached_server_push(memc, servers) != MEMCACHED_SUCCESS) {\r
+ throw IOException("MemcacheBase::Memcache(): memcached_server_push() failed"); \r
+ }\r
+ memcached_server_list_free(servers);\r
+\r
+ log.debug("Memcache object initialized");\r
+}\r
+\r
+MemcacheBase::~MemcacheBase() {\r
+ memcached_free(memc);\r
+ log.debug("Base object destroyed");\r
+}\r
+\r
+MemcacheStorageService::MemcacheStorageService(const DOMElement* e)\r
+ : MemcacheBase(e), m_log(Category::getInstance("XMLTooling.MemcacheStorageService")), m_buildMap(false) {\r
+\r
+ const XMLCh* tag=e ? e->getAttributeNS(NULL,buildMap) : NULL;\r
+ if (tag && *tag && XMLString::parseInt(tag) != 0) {\r
+ m_buildMap = true;\r
+ m_log.debug("Cache built with buildMap ON");\r
+ }\r
+\r
+}\r
+\r
+MemcacheStorageService::~MemcacheStorageService() {\r
+\r
+ \r
+}\r
+\r
+bool MemcacheStorageService::createString(const char* context, const char* key, const char* value, time_t expiration) {\r
+\r
+ log.debug("createString ctx: %s - key: %s", context, key);\r
+\r
+ string final_key = string(context) + ":" + string(key);\r
+\r
+ mc_record rec(value, expiration);\r
+ string final_value;\r
+ serialize(rec, final_value);\r
+\r
+ bool result = addMemcache(final_key.c_str(), final_value, expiration, 1); // the flag will be the version\r
+\r
+ if (result && m_buildMap) {\r
+ log.debug("Got result, updating map");\r
+\r
+ string map_name = context;\r
+ // we need to update the context map\r
+ if (! addLock(map_name)) {\r
+ log.error("Unable to get lock for context %s!", context);\r
+ deleteMemcache(final_key.c_str(), 0);\r
+ return false;\r
+ }\r
+\r
+ string ser_arr;\r
+ uint32_t flags;\r
+ bool result = getMemcache(map_name.c_str(), ser_arr, &flags);\r
+ \r
+ list<string> contents;\r
+ if (result) {\r
+ log.debug("Match found. Parsing...");\r
+\r
+ deserialize(ser_arr, contents);\r
+ \r
+ log.debug("Iterating retrieved session map...");\r
+ list<string>::iterator iter;\r
+ for(iter = contents.begin(); \r
+ iter != contents.end();\r
+ iter++) {\r
+ log.debug("value = " + *iter);\r
+ }\r
+\r
+ } else {\r
+ log.debug("New context: %s", map_name.c_str());\r
+\r
+ }\r
+\r
+ contents.push_back(key);\r
+ serialize(contents, ser_arr); \r
+ setMemcache(map_name.c_str(), ser_arr, expiration, 0); \r
+ \r
+ deleteLock(map_name);\r
+ }\r
+\r
+ return result; \r
+\r
+}\r
+\r
+int MemcacheStorageService::readString(const char* context, const char* key, string* pvalue, time_t* pexpiration, int version) {\r
+\r
+ log.debug("readString ctx: %s - key: %s", context, key);\r
+\r
+ string final_key = string(context) + ":" + string(key);\r
+ uint32_t rec_version;\r
+ string value;\r
+\r
+ bool found = getMemcache(final_key.c_str(), value, &rec_version);\r
+ if (!found) {\r
+ return 0;\r
+ }\r
+\r
+ if (version && rec_version <= (uint32_t)version) {\r
+ return version;\r
+ }\r
+\r
+ if (pexpiration || pvalue) {\r
+ mc_record rec;\r
+ deserialize(value, rec);\r
+ \r
+ if (pexpiration) {\r
+ *pexpiration = rec.expiration;\r
+ }\r
+ \r
+ if (pvalue) {\r
+ *pvalue = rec.value;\r
+ }\r
+ }\r
+ \r
+ return rec_version;\r
+\r
+}\r
+\r
+int MemcacheStorageService::updateString(const char* context, const char* key, const char* value, time_t expiration, int version) {\r
+\r
+ log.debug("updateString ctx: %s - key: %s", context, key);\r
+\r
+ time_t final_exp = expiration;\r
+ time_t *want_expiration = NULL;\r
+ if (! final_exp) {\r
+ want_expiration = &final_exp;\r
+ }\r
+\r
+ int read_res = readString(context, key, NULL, want_expiration, version);\r
+\r
+ if (!read_res) {\r
+ // not found\r
+ return read_res;\r
+ }\r
+\r
+ if (version && version != read_res) {\r
+ // version incorrect\r
+ return -1;\r
+ }\r
+\r
+ // Proceding with update\r
+ string final_key = string(context) + ":" + string(key);\r
+ mc_record rec(value, final_exp);\r
+ string final_value;\r
+ serialize(rec, final_value);\r
+\r
+ replaceMemcache(final_key.c_str(), final_value, final_exp, ++version);\r
+ return version;\r
+\r
+}\r
+\r
+bool MemcacheStorageService::deleteString(const char* context, const char* key) {\r
+\r
+ log.debug("deleteString ctx: %s - key: %s", context, key);\r
+ \r
+ string final_key = string(context) + ":" + string(key);\r
+\r
+ // Not updating context map, if there is one. There is no need.\r
+\r
+ return deleteMemcache(final_key.c_str(), 0);\r
+\r
+}\r
+\r
+void MemcacheStorageService::updateContext(const char* context, time_t expiration) {\r
+\r
+ log.debug("updateContext ctx: %s", context);\r
+\r
+ if (!m_buildMap) {\r
+ log.error("updateContext invoked on a Storage with no context map built!");\r
+ return;\r
+ }\r
+\r
+ string map_name = context;\r
+ \r
+ if (! addLock(map_name)) {\r
+ log.error("Unable to get lock for context %s!", context);\r
+ return;\r
+ }\r
+ \r
+ string ser_arr;\r
+ uint32_t flags;\r
+ bool result = getMemcache(map_name.c_str(), ser_arr, &flags);\r
+ \r
+ list<string> contents;\r
+ if (result) {\r
+ log.debug("Match found. Parsing...");\r
+ \r
+ deserialize(ser_arr, contents);\r
+ \r
+ log.debug("Iterating retrieved session map...");\r
+ list<string>::iterator iter;\r
+ for(iter = contents.begin(); \r
+ iter != contents.end();\r
+ iter++) {\r
+\r
+ // Update expiration times\r
+ string value; \r
+ int read_res = readString(context, iter->c_str(), &value, NULL, 0);\r
+ \r
+ if (!read_res) {\r
+ // not found\r
+ continue;\r
+ }\r
+\r
+ updateString(context, iter->c_str(), value.c_str(), expiration, read_res);\r
+ }\r
+ replaceMemcache(map_name.c_str(), ser_arr, expiration, flags);\r
+ }\r
+ \r
+ deleteLock(map_name);\r
+ \r
+}\r
+\r
+void MemcacheStorageService::deleteContext(const char* context) {\r
+\r
+ log.debug("deleteContext ctx: %s", context);\r
+\r
+ if (!m_buildMap) {\r
+ log.error("deleteContext invoked on a Storage with no context map built!");\r
+ return;\r
+ }\r
+\r
+ string map_name = context;\r
+ \r
+ if (! addLock(map_name)) {\r
+ log.error("Unable to get lock for context %s!", context);\r
+ return;\r
+ }\r
+ \r
+ string ser_arr;\r
+ uint32_t flags;\r
+ bool result = getMemcache(map_name.c_str(), ser_arr, &flags);\r
+ \r
+ list<string> contents;\r
+ if (result) {\r
+ log.debug("Match found. Parsing...");\r
+ \r
+ deserialize(ser_arr, contents);\r
+ \r
+ log.debug("Iterating retrieved session map...");\r
+ list<string>::iterator iter;\r
+ for(iter = contents.begin(); \r
+ iter != contents.end();\r
+ iter++) {\r
+ string final_key = map_name + *iter;\r
+ deleteMemcache(final_key.c_str(), 0);\r
+ }\r
+ \r
+ deleteMemcache(map_name.c_str(), 0);\r
+ }\r
+ \r
+ deleteLock(map_name);\r
+\r
+}\r
+\r
+extern "C" int xmltooling_extension_init(void*) {\r
+ // Register this SS type\r
+ XMLToolingConfig::getConfig().StorageServiceManager.registerFactory("MEMCACHE", MemcacheStorageServiceFactory);\r
+ return 0;\r
+}\r
+\r
+extern "C" void xmltooling_extension_term() {\r
+ XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("MEMCACHE");\r
+}\r