https://issues.shibboleth.net/jira/browse/SSPCPP-94
authorScott Cantor <cantor.2@osu.edu>
Mon, 2 Jun 2008 22:35:02 +0000 (22:35 +0000)
committerScott Cantor <cantor.2@osu.edu>
Mon, 2 Jun 2008 22:35:02 +0000 (22:35 +0000)
Makefile.am
Shibboleth.sln
configure.ac
memcache-store/Makefile.am [new file with mode: 0644]
memcache-store/memcache-store.cpp [new file with mode: 0644]
memcache-store/memcache-store.rc [new file with mode: 0644]
memcache-store/memcache-store.vcproj [new file with mode: 0644]
memcache-store/resource.h [new file with mode: 0644]

index 0f3e8b5..28448cf 100644 (file)
@@ -10,7 +10,7 @@ WANT_SUBDIRS = @WANT_SUBDIRS@
 
 SUBDIRS = $(WANT_SUBDIRS)
 
-DIST_SUBDIRS = doc schemas configs shibsp shibd adfs util apache nsapi_shib fastcgi odbc-store selinux
+DIST_SUBDIRS = doc schemas configs shibsp shibd adfs util apache nsapi_shib fastcgi odbc-store memcache-store selinux
 
 if DX_COND_doc
 all-local: shibboleth.spec pkginfo doxygen-doc
index c4616c3..9ee3446 100644 (file)
@@ -150,6 +150,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "shibresponder", "fastcgi\sh
                {81F0F7A6-DC36-46EF-957F-F9E81D4403F7} = {81F0F7A6-DC36-46EF-957F-F9E81D4403F7}
        EndProjectSection
 EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "memcache-store", "memcache-store\memcache-store.vcproj", "{666A63A7-983F-4C19-8411-207F24305198}"
+       ProjectSection(WebsiteProperties) = preProject
+               Debug.AspNetCompiler.Debug = "True"
+               Release.AspNetCompiler.Debug = "False"
+       EndProjectSection
+EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
                Debug|Win32 = Debug|Win32
@@ -216,6 +222,10 @@ Global
                {B2423DCE-048D-4BAA-9AB9-F5D1FCDD3D25}.Debug|Win32.Build.0 = Debug|Win32
                {B2423DCE-048D-4BAA-9AB9-F5D1FCDD3D25}.Release|Win32.ActiveCfg = Release|Win32
                {B2423DCE-048D-4BAA-9AB9-F5D1FCDD3D25}.Release|Win32.Build.0 = Release|Win32
+               {666A63A7-983F-4C19-8411-207F24305198}.Debug|Win32.ActiveCfg = Debug|Win32
+               {666A63A7-983F-4C19-8411-207F24305198}.Debug|Win32.Build.0 = Debug|Win32
+               {666A63A7-983F-4C19-8411-207F24305198}.Release|Win32.ActiveCfg = Release|Win32
+               {666A63A7-983F-4C19-8411-207F24305198}.Release|Win32.Build.0 = Release|Win32
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
@@ -230,6 +240,7 @@ Global
                {666A63A7-983F-4C19-8411-207F24305197} = {96AE4FC9-45EF-4C18-9F3B-EDA439E26E4C}
                {26D4FABF-ACDE-4947-9C4A-7AE1B50CD83A} = {96AE4FC9-45EF-4C18-9F3B-EDA439E26E4C}
                {26D4FABF-ACDE-4947-9C4A-7AE1B50CD83B} = {96AE4FC9-45EF-4C18-9F3B-EDA439E26E4C}
+               {666A63A7-983F-4C19-8411-207F24305198} = {96AE4FC9-45EF-4C18-9F3B-EDA439E26E4C}
                {F13141B6-6C87-40BB-8D4E-5CC56EBB4C5A} = {FED80230-119E-4B2F-9F53-D2660A5F022B}
                {F13141B6-6C87-40BB-8D4E-5CC56EBB4C59} = {FED80230-119E-4B2F-9F53-D2660A5F022B}
                {8CF7DDFA-EAA0-416E-853E-3DCB210C4AE0} = {8E1AF2CF-24E1-4983-8681-394D89DF9AD2}
index ef10e95..75de698 100644 (file)
@@ -436,6 +436,39 @@ if test ! "$WANT_FASTCGI" = "no" ; then
     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 
diff --git a/memcache-store/Makefile.am b/memcache-store/Makefile.am
new file mode 100644 (file)
index 0000000..9d46119
--- /dev/null
@@ -0,0 +1,20 @@
+AUTOMAKE_OPTIONS = foreign\r
+\r
+plugindir = $(libdir)/@PACKAGE@\r
+plugin_LTLIBRARIES = memcache-store.la\r
+\r
+AM_CFLAGS = $(MEMCACHED_CFLAGS)\r
+AM_CXXFLAGS = $(MEMCACHED_CFLAGS)\r
+\r
+memcache_store_la_LIBADD = \\r
+       $(MEMCACHED_LIBS)\r
+\r
+memcache_store_la_SOURCES = \\r
+       memcache-store.cpp\r
+\r
+memcache_store_la_LDFLAGS = -module -avoid-version $(XMLSEC_LIBS)\r
+\r
+install-exec-hook:\r
+       for la in $(plugin_LTLIBRARIES) ; do rm -f $(DESTDIR)$(plugindir)/$$la ; done\r
+\r
+EXTRA_DIST = memcache-store.vcproj memcache-store.rc resource.h\r
diff --git a/memcache-store/memcache-store.cpp b/memcache-store/memcache-store.cpp
new file mode 100644 (file)
index 0000000..d1fb373
--- /dev/null
@@ -0,0 +1,738 @@
+/*\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
diff --git a/memcache-store/memcache-store.rc b/memcache-store/memcache-store.rc
new file mode 100644 (file)
index 0000000..b0c27d9
--- /dev/null
@@ -0,0 +1,101 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 2,0,0,0
+ PRODUCTVERSION 2,0,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Internet2"
+            VALUE "FileDescription", "Shibboleth Memcache Storage Service Plugin"
+            VALUE "FileVersion", "2, 0, 0, 0"
+            VALUE "InternalName", "memcache-store"
+            VALUE "LegalCopyright", "Copyright (C) 2008 Internet2"
+            VALUE "OriginalFilename", "memcache-store.so"
+            VALUE "ProductName", "Shibboleth 2.0"
+            VALUE "ProductVersion", "2, 0, 0, 0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
\ No newline at end of file
diff --git a/memcache-store/memcache-store.vcproj b/memcache-store/memcache-store.vcproj
new file mode 100644 (file)
index 0000000..5a93585
--- /dev/null
@@ -0,0 +1,195 @@
+<?xml version="1.0" encoding="Windows-1252"?>\r
+<VisualStudioProject\r
+       ProjectType="Visual C++"\r
+       Version="8.00"\r
+       Name="memcache-store"\r
+       ProjectGUID="{666A63A7-983F-4C19-8411-207F24305198}"\r
+       RootNamespace="memcachestore"\r
+       Keyword="Win32Proj"\r
+       >\r
+       <Platforms>\r
+               <Platform\r
+                       Name="Win32"\r
+               />\r
+       </Platforms>\r
+       <ToolFiles>\r
+       </ToolFiles>\r
+       <Configurations>\r
+               <Configuration\r
+                       Name="Debug|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               Optimization="0"\r
+                               AdditionalIncludeDirectories="&quot;..\..\cpp-xmltooling&quot;"\r
+                               PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS"\r
+                               MinimalRebuild="true"\r
+                               BasicRuntimeChecks="3"\r
+                               RuntimeLibrary="3"\r
+                               BrowseInformation="1"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="4"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                               PreprocessorDefinitions="_DEBUG"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="log4shib1D.lib xerces-c_2D.lib xmltooling1D.lib"\r
+                               OutputFile="$(OutDir)\$(ProjectName).so"\r
+                               LinkIncremental="2"\r
+                               AdditionalLibraryDirectories="..\..\cpp-xmltooling\$(ConfigurationName)"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+               <Configuration\r
+                       Name="Release|Win32"\r
+                       OutputDirectory="$(SolutionDir)$(ConfigurationName)"\r
+                       IntermediateDirectory="$(ConfigurationName)"\r
+                       ConfigurationType="2"\r
+                       CharacterSet="2"\r
+                       WholeProgramOptimization="1"\r
+                       >\r
+                       <Tool\r
+                               Name="VCPreBuildEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCustomBuildTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXMLDataGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebServiceProxyGeneratorTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCMIDLTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCCLCompilerTool"\r
+                               AdditionalIncludeDirectories="&quot;..\..\cpp-xmltooling&quot;"\r
+                               PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"\r
+                               RuntimeLibrary="2"\r
+                               WarningLevel="3"\r
+                               Detect64BitPortabilityProblems="true"\r
+                               DebugInformationFormat="3"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManagedResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCResourceCompilerTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPreLinkEventTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCLinkerTool"\r
+                               AdditionalDependencies="log4shib1.lib xerces-c_2.lib xmltooling1.lib"\r
+                               OutputFile="$(OutDir)\$(ProjectName).so"\r
+                               LinkIncremental="1"\r
+                               AdditionalLibraryDirectories="..\..\cpp-xmltooling\$(ConfigurationName)"\r
+                               GenerateDebugInformation="true"\r
+                               SubSystem="2"\r
+                               OptimizeReferences="2"\r
+                               EnableCOMDATFolding="2"\r
+                               TargetMachine="1"\r
+                       />\r
+                       <Tool\r
+                               Name="VCALinkTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCManifestTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCXDCMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCBscMakeTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCFxCopTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCAppVerifierTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCWebDeploymentTool"\r
+                       />\r
+                       <Tool\r
+                               Name="VCPostBuildEventTool"\r
+                       />\r
+               </Configuration>\r
+       </Configurations>\r
+       <References>\r
+       </References>\r
+       <Files>\r
+               <File\r
+                       RelativePath=".\memcache-store.cpp"\r
+                       >\r
+               </File>\r
+               <File\r
+                       RelativePath=".\memcache-store.rc"\r
+                       >\r
+               </File>\r
+               <File\r
+                       RelativePath=".\resource.h"\r
+                       >\r
+               </File>\r
+       </Files>\r
+       <Globals>\r
+       </Globals>\r
+</VisualStudioProject>\r
diff --git a/memcache-store/resource.h b/memcache-store/resource.h
new file mode 100644 (file)
index 0000000..42c4d44
--- /dev/null
@@ -0,0 +1,14 @@
+//{{NO_DEPENDENCIES}}\r
+// Microsoft Visual C++ generated include file.\r
+// Used by memcache-store.rc\r
+\r
+// Next default values for new objects\r
+// \r
+#ifdef APSTUDIO_INVOKED\r
+#ifndef APSTUDIO_READONLY_SYMBOLS\r
+#define _APS_NEXT_RESOURCE_VALUE        101\r
+#define _APS_NEXT_COMMAND_VALUE         40001\r
+#define _APS_NEXT_CONTROL_VALUE         1001\r
+#define _APS_NEXT_SYMED_VALUE           101\r
+#endif\r
+#endif\r