https://issues.shibboleth.net/jira/browse/SSPCPP-605
[shibboleth/cpp-sp.git] / memcache-store / memcache-store.cpp
index 6724232..8948e53 100644 (file)
-/*\r
- *  Copyright 2001-2009 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
-#ifdef WIN32\r
-# define _CRT_NONSTDC_NO_DEPRECATE 1\r
-# define _CRT_SECURE_NO_DEPRECATE 1\r
-# define MCEXT_EXPORTS __declspec(dllexport)\r
-#else\r
-# define MCEXT_EXPORTS\r
-#endif\r
-\r
-#include <xmltooling/base.h>\r
-\r
-#include <list>\r
-#include <iostream> \r
-#include <libmemcached/memcached.h>\r
-#include <xercesc/util/XMLUniDefs.hpp>\r
-\r
-#include <xmltooling/logging.h>\r
-#include <xmltooling/unicode.h>\r
-#include <xmltooling/XMLToolingConfig.h>\r
-#include <xmltooling/util/NDC.h>\r
-#include <xmltooling/util/StorageService.h>\r
-#include <xmltooling/util/Threads.h>\r
-#include <xmltooling/util/XMLHelper.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
-  static const XMLCh sendTimeout[] = UNICODE_LITERAL_11(s,e,n,d,T,i,m,e,o,u,t);\r
-  static const XMLCh recvTimeout[] = UNICODE_LITERAL_11(r,e,c,v,T,i,m,e,o,u,t);\r
-  static const XMLCh pollTimeout[] = UNICODE_LITERAL_11(p,o,l,l,T,i,m,e,o,u,t);\r
-  static const XMLCh failLimit[] = UNICODE_LITERAL_9(f,a,i,l,L,i,m,i,t);\r
-  static const XMLCh retryTimeout[] = UNICODE_LITERAL_12(r,e,t,r,y,T,i,m,e,o,u,t);\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_prefix;\r
-    Mutex* m_lock;\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
-#ifdef WIN32\r
-    Sleep(100);\r
-#else\r
-    struct timeval tv = { 0, 100000 };\r
-    select(0, 0, 0, 0, &tv);\r
-#endif\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
-  bool success;\r
-\r
-  if (use_prefix) {\r
-    final_key = m_prefix + key;\r
-  } else {\r
-    final_key = key;\r
-  }\r
-\r
-  m_lock->lock();\r
-  rv = memcached_delete(memc, (char *)final_key.c_str(), final_key.length(), timeout);\r
-  m_lock->unlock();\r
-\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 if (rv == MEMCACHED_ERRNO) {\r
-    // System error\r
-    string error = string("Memcache::deleteMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));\r
-    log.error(error);\r
-    throw IOException(error);\r
-  } else {\r
-    string error = string("Memcache::deleteMemcache() Problems: ") + memcached_strerror(memc, rv);\r
-    log.error(error);\r
-    throw IOException(error);\r
-  }\r
-\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
-  bool success;\r
-  \r
-  if (use_prefix) {\r
-    final_key = m_prefix + key;\r
-  } else {\r
-    final_key = key;\r
-  }\r
-\r
-  m_lock->lock();\r
-  result = memcached_get(memc, (char *)final_key.c_str(), final_key.length(), &len, flags, &rv);\r
-  m_lock->unlock();\r
-\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 if (rv == MEMCACHED_ERRNO) {\r
-    // System error\r
-    string error = string("Memcache::getMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));\r
-    log.error(error);\r
-    throw IOException(error);\r
-  } else {\r
-    string error = string("Memcache::getMemcache() Problems: ") + memcached_strerror(memc, rv);\r
-    log.error(error);\r
-    throw IOException(error);\r
-  }\r
-\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
-  bool success;\r
-\r
-  if (use_prefix) {\r
-    final_key = m_prefix + key;\r
-  } else {\r
-    final_key = key;\r
-  }\r
-\r
-  m_lock->lock();\r
-  rv = memcached_add(memc, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);\r
-  m_lock->unlock();\r
-\r
-  if (rv == MEMCACHED_SUCCESS) {\r
-    success = true;\r
-  } else if (rv == MEMCACHED_NOTSTORED) {\r
-    // already there\r
-    success = false;\r
-  } else if (rv == MEMCACHED_ERRNO) {\r
-    // System error\r
-    string error = string("Memcache::addMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));\r
-    log.error(error);\r
-    throw IOException(error);\r
-  } else {\r
-    string error = string("Memcache::addMemcache() Problems: ") + memcached_strerror(memc, rv);\r
-    log.error(error);\r
-    throw IOException(error);\r
-  }\r
-\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
-  bool success;\r
-\r
-  if (use_prefix) {\r
-    final_key = m_prefix + key;\r
-  } else {\r
-    final_key = key;\r
-  }\r
-\r
-  m_lock->lock();\r
-  rv = memcached_set(memc, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);\r
-  m_lock->unlock();\r
-\r
-  if (rv == MEMCACHED_SUCCESS) {\r
-    success = true;\r
-  } else if (rv == MEMCACHED_ERRNO) {\r
-    // System error\r
-    string error = string("Memcache::setMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));\r
-    log.error(error);\r
-    throw IOException(error);\r
-  } else {\r
-    string error = string("Memcache::setMemcache() Problems: ") + memcached_strerror(memc, rv);\r
-    log.error(error);\r
-    throw IOException(error);\r
-  }\r
-\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
-  bool success;\r
-\r
-  if (use_prefix) {\r
-    final_key = m_prefix + key;\r
-  } else {\r
-    final_key = key;\r
-  }\r
-\r
-  m_lock->lock();\r
-  rv = memcached_replace(memc, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);\r
-  m_lock->unlock();\r
-\r
-  if (rv == MEMCACHED_SUCCESS) {\r
-    success = true;\r
-  } else if (rv == MEMCACHED_NOTSTORED) {\r
-    // not there\r
-    success = false;\r
-  } else if (rv == MEMCACHED_ERRNO) {\r
-    // System error\r
-    string error = string("Memcache::replaceMemcache() SYSTEM ERROR: ") + string(strerror(memc->cached_errno));\r
-    log.error(error);\r
-    throw IOException(error);\r
-  } else {\r
-    string error = string("Memcache::replaceMemcache() Problems: ") + memcached_strerror(memc, rv);\r
-    log.error(error);\r
-    throw IOException(error);\r
-  }\r
-\r
-  return success;\r
-}\r
-\r
-MemcacheBase::MemcacheBase(const DOMElement* e) : m_root(e), log(Category::getInstance("XMLTooling.MemcacheBase")), 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
-  m_lock = Mutex::create();\r
-  log.debug("Lock created");\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 hash = MEMCACHED_HASH_CRC;\r
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, hash);\r
-  log.debug("CRC hash set");\r
-\r
-  int32_t send_timeout = 1000000;\r
-  const XMLCh* tag = e ? e->getAttributeNS(NULL, sendTimeout) : NULL;\r
-  if (tag && *tag) {\r
-    send_timeout = XMLString::parseInt(tag);\r
-  }\r
-  log.debug("MEMCACHED_BEHAVIOR_SND_TIMEOUT will be set to %d", send_timeout);\r
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SND_TIMEOUT, send_timeout);\r
-\r
-  int32_t recv_timeout = 1000000;\r
-  tag = e ? e->getAttributeNS(NULL, sendTimeout) : NULL;\r
-  if (tag && *tag) {\r
-    recv_timeout = XMLString::parseInt(tag);\r
-  }\r
-  log.debug("MEMCACHED_BEHAVIOR_RCV_TIMEOUT will be set to %d", recv_timeout);\r
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RCV_TIMEOUT, recv_timeout);\r
-\r
-  int32_t poll_timeout = 1000;\r
-  tag = e ? e->getAttributeNS(NULL, pollTimeout) : NULL;\r
-  if (tag && *tag) {\r
-    poll_timeout = XMLString::parseInt(tag);\r
-  }\r
-  log.debug("MEMCACHED_BEHAVIOR_POLL_TIMEOUT will be set to %d", poll_timeout);\r
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_POLL_TIMEOUT, poll_timeout);\r
-\r
-  int32_t fail_limit = 5;\r
-  tag = e ? e->getAttributeNS(NULL, failLimit) : NULL;\r
-  if (tag && *tag) {\r
-    fail_limit = XMLString::parseInt(tag);\r
-  }\r
-  log.debug("MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT will be set to %d", fail_limit);\r
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, fail_limit);\r
-\r
-  int32_t retry_timeout = 30;\r
-  tag = e ? e->getAttributeNS(NULL, retryTimeout) : NULL;\r
-  if (tag && *tag) {\r
-    retry_timeout = XMLString::parseInt(tag);\r
-  }\r
-  log.debug("MEMCACHED_BEHAVIOR_RETRY_TIMEOUT will be set to %d", retry_timeout);\r
-  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, retry_timeout);\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
-  memcached_server_st *servers;\r
-  servers = memcached_servers_parse(const_cast<char*>(h.get()));\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
-  delete m_lock;\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
-  if (m_buildMap) {\r
-    log.debug("Checking context");\r
-\r
-    string map_name = context;\r
-    string ser_arr;\r
-    uint32_t flags;\r
-    bool ctx_found = getMemcache(map_name.c_str(), ser_arr, &flags);\r
-\r
-    if (!ctx_found) {\r
-      return 0;\r
-    }\r
-  }\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
-  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
-}\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
-  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
-}\r
-\r
-extern "C" int MCEXT_EXPORTS 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 MCEXT_EXPORTS xmltooling_extension_term() {\r
-    XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("MEMCACHE");\r
-}\r
+/**
+ * Licensed to the University Corporation for Advanced Internet
+ * Development, Inc. (UCAID) under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work for
+ * additional information regarding copyright ownership.
+ *
+ * UCAID licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file except
+ * in compliance with the License. You may obtain a copy of the
+ * License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
+ * either express or implied. See the License for the specific
+ * language governing permissions and limitations under the License.
+ */
+
+/**
+ * memcache-store.cpp
+ *
+ * Storage Service using memcache (pre memcache tags).
+ */
+
+#if defined (_MSC_VER) || defined(__BORLANDC__)
+# include "config_win32.h"
+#else
+# include "config.h"
+#endif
+
+#ifdef WIN32
+# define _CRT_NONSTDC_NO_DEPRECATE 1
+# define _CRT_SECURE_NO_DEPRECATE 1
+# define MCEXT_EXPORTS __declspec(dllexport)
+#else
+# define MCEXT_EXPORTS
+#endif
+
+#include <xmltooling/base.h>
+
+#include <list>
+#include <iostream> 
+#include <libmemcached/memcached.h>
+#include <xercesc/util/XMLUniDefs.hpp>
+
+#include <xmltooling/logging.h>
+#include <xmltooling/unicode.h>
+#include <xmltooling/XMLToolingConfig.h>
+#include <xmltooling/util/NDC.h>
+#include <xmltooling/util/StorageService.h>
+#include <xmltooling/util/Threads.h>
+#include <xmltooling/util/XMLHelper.h>
+
+using namespace xmltooling::logging;
+using namespace xmltooling;
+using namespace xercesc;
+using namespace boost;
+using namespace std;
+
+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();
+        
+        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 {
+
+    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);
+    
+        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 updateContext(const char* context, time_t expiration);
+        void deleteContext(const char* context);
+
+    private:
+        Capabilities m_caps;
+        bool m_buildMap;
+    };
+
+    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::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);
+    }
+#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
+#ifdef WIN32
+        Sleep(100);
+#else
+        struct timeval tv = { 0, 100000 };
+        select(0, 0, 0, 0, &tv);
+#endif
+    }
+    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::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)
+{
+    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)
+{
+    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)
+{
+    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)
+{
+  
+    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);
+    }
+}
+
+
+MemcacheStorageService::MemcacheStorageService(const DOMElement* e)
+    : 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");
+}
+
+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);
+
+    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 && m_buildMap) {
+        m_log.debug("Got result, updating map");
+
+        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);
+    
+        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);
+    }
+    return result;
+}
+
+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) {
+        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;
+
+    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)
+{
+    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;
+
+    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;
+    }
+
+    // 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)
+{
+    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);
+}
+
+void MemcacheStorageService::updateContext(const char* context, time_t expiration)
+{
+
+    m_log.debug("updateContext ctx: %s", context);
+
+    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);
+  
+    list<string> contents;
+    if (result) {
+        m_log.debug("Match found. Parsing...");
+        deserialize(ser_arr, contents);
+    
+        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);
+    }
+}
+
+void MemcacheStorageService::deleteContext(const char* context)
+{
+
+    m_log.debug("deleteContext ctx: %s", context);
+
+    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);
+  
+    list<string> contents;
+    if (result) {
+        m_log.debug("Match found. Parsing...");
+        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);
+        }
+    
+        deleteMemcache(map_name.c_str(), 0);
+    }
+}
+
+extern "C" int MCEXT_EXPORTS xmltooling_extension_init(void*) {
+    // Register this SS type
+    XMLToolingConfig::getConfig().StorageServiceManager.registerFactory("MEMCACHE", MemcacheStorageServiceFactory);
+    return 0;
+}
+
+extern "C" void MCEXT_EXPORTS xmltooling_extension_term() {
+    XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("MEMCACHE");
+}