https://bugs.internet2.edu/jira/browse/SSPCPP-166
[shibboleth/sp.git] / memcache-store / memcache-store.cpp
1 /*\r
2  *  Copyright 2001-2008 Internet2\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  *\r
8  *     http://www.apache.org/licenses/LICENSE-2.0\r
9  *\r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  */\r
16 \r
17 /**\r
18  * memcache-store.cpp\r
19  *\r
20  * Storage Service using memcache (pre memcache tags)\r
21  */\r
22 \r
23 #if defined (_MSC_VER) || defined(__BORLANDC__)\r
24 # include "config_win32.h"\r
25 #else\r
26 # include "config.h"\r
27 #endif\r
28 \r
29 #ifdef WIN32\r
30 # define _CRT_NONSTDC_NO_DEPRECATE 1\r
31 # define _CRT_SECURE_NO_DEPRECATE 1\r
32 # define MCEXT_EXPORTS __declspec(dllexport)\r
33 #else\r
34 # define MCEXT_EXPORTS\r
35 #endif\r
36 \r
37 #include <xmltooling/base.h>\r
38 #include <libmemcached/memcached.h>\r
39 #include <xercesc/util/XMLUniDefs.hpp>\r
40 \r
41 #include <xmltooling/logging.h>\r
42 #include <xmltooling/XMLToolingConfig.h>\r
43 #include <xmltooling/util/NDC.h>\r
44 #include <xmltooling/util/StorageService.h>\r
45 #include <xmltooling/util/XMLHelper.h>\r
46 \r
47 using namespace xmltooling::logging;\r
48 using namespace xmltooling;\r
49 using namespace xercesc;\r
50 using namespace std;\r
51 \r
52 namespace xmltooling {\r
53   static const XMLCh Hosts[] = UNICODE_LITERAL_5(H,o,s,t,s);\r
54   static const XMLCh prefix[] = UNICODE_LITERAL_6(p,r,e,f,i,x);\r
55   static const XMLCh buildMap[] = UNICODE_LITERAL_8(b,u,i,l,d,M,a,p);\r
56   \r
57   class mc_record {\r
58   public:\r
59     string value;\r
60     time_t expiration;\r
61     mc_record(){};\r
62     mc_record(string _v, time_t _e) :\r
63       value(_v), expiration(_e)\r
64     {}\r
65   };\r
66 \r
67   class MemcacheBase {\r
68   public:\r
69     MemcacheBase(const DOMElement* e);\r
70     ~MemcacheBase();\r
71         \r
72     bool addMemcache(const char *key,\r
73                      string &value,\r
74                      time_t timeout,\r
75                      uint32_t flags,\r
76                      bool use_prefix = true);\r
77     bool setMemcache(const char *key,\r
78                      string &value,\r
79                      time_t timeout,\r
80                      uint32_t flags,\r
81                      bool use_prefix = true);\r
82     bool replaceMemcache(const char *key,\r
83                          string &value,\r
84                          time_t timeout,\r
85                          uint32_t flags,\r
86                          bool use_prefix = true);\r
87     bool getMemcache(const char *key,\r
88                      string &dest,\r
89                      uint32_t *flags,\r
90                      bool use_prefix = true);\r
91     bool deleteMemcache(const char *key,\r
92                         time_t timeout,\r
93                         bool use_prefix = true);\r
94 \r
95     void serialize(mc_record &source, string &dest);\r
96     void serialize(list<string> &source, string &dest);\r
97     void deserialize(string &source, mc_record &dest);\r
98     void deserialize(string &source, list<string> &dest);\r
99 \r
100     bool addSessionToUser(string &key, string &user);\r
101     bool addLock(string what, bool use_prefix = true);\r
102     void deleteLock(string what, bool use_prefix = true);\r
103 \r
104   protected:\r
105     const DOMElement* m_root; // can only use this during initialization\r
106     Category& log;\r
107     memcached_st *memc;\r
108     string m_memcacheHosts;\r
109     string m_prefix;\r
110     \r
111   };\r
112   \r
113   class MemcacheStorageService : public StorageService, public MemcacheBase {\r
114 \r
115   public:\r
116     MemcacheStorageService(const DOMElement* e);\r
117     ~MemcacheStorageService();\r
118     \r
119     bool createString(const char* context, const char* key, const char* value, time_t expiration);\r
120     int readString(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0);\r
121     int updateString(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0);\r
122     bool deleteString(const char* context, const char* key);\r
123     \r
124     bool createText(const char* context, const char* key, const char* value, time_t expiration) {\r
125       return createString(context, key, value, expiration);\r
126     }\r
127     int readText(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {\r
128       return readString(context, key, pvalue, pexpiration, version);\r
129     }\r
130     int updateText(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0) {\r
131       return updateString(context, key, value, expiration, version);\r
132     }\r
133     bool deleteText(const char* context, const char* key) {\r
134       return deleteString(context, key);\r
135     }\r
136     \r
137     void reap(const char* context) {}\r
138 \r
139     void updateContext(const char* context, time_t expiration);\r
140     void deleteContext(const char* context);\r
141 \r
142     private:\r
143 \r
144     Category& m_log;\r
145     bool m_buildMap;\r
146 \r
147 \r
148   };\r
149 \r
150   StorageService* MemcacheStorageServiceFactory(const DOMElement* const & e) {\r
151     return new MemcacheStorageService(e);\r
152   }\r
153 \r
154 };\r
155 \r
156 bool MemcacheBase::addLock(string what, bool use_prefix) {\r
157   string lock_name = what + ":LOCK";\r
158   string set_val = "1";\r
159   unsigned tries = 5;\r
160   while (!addMemcache(lock_name.c_str(), set_val, 5, 0, use_prefix)) {\r
161     if (tries-- < 0) {\r
162       log.debug("Unable to get lock %s... FAILED.", lock_name.c_str());\r
163       return false;\r
164     }\r
165     log.debug("Unable to get lock %s... Retrying.", lock_name.c_str());\r
166     \r
167     // sleep 100ms\r
168 #ifdef WIN32\r
169     Sleep(100);\r
170 #else\r
171     struct timeval tv = { 0, 100000 };\r
172     select(0, 0, 0, 0, &tv);\r
173 #endif\r
174   }\r
175   return true;\r
176 }\r
177 \r
178 void MemcacheBase::deleteLock(string what, bool use_prefix) {\r
179 \r
180   string lock_name = what + ":LOCK";\r
181   deleteMemcache(lock_name.c_str(), 0, use_prefix);\r
182   return;\r
183 \r
184 }  \r
185 \r
186 void MemcacheBase::deserialize(string &source, mc_record &dest) {\r
187   istringstream is(source, stringstream::in | stringstream::out);\r
188   is >> dest.expiration;\r
189   is.ignore(1); // ignore delimiter\r
190   dest.value = is.str().c_str() + is.tellg();\r
191 }\r
192 \r
193 void MemcacheBase::deserialize(string &source, list<string> &dest) {\r
194   istringstream is(source, stringstream::in | stringstream::out);\r
195   while (!is.eof()) {\r
196     string s;\r
197     is >> s;\r
198     dest.push_back(s);\r
199   }  \r
200 }\r
201 \r
202 void MemcacheBase::serialize(mc_record &source, string &dest) {\r
203   ostringstream os(stringstream::in | stringstream::out);\r
204   os << source.expiration;\r
205   os << "-"; // delimiter\r
206   os << source.value;\r
207   dest = os.str();\r
208 }\r
209 \r
210 void MemcacheBase::serialize(list<string> &source, string &dest) {  \r
211   ostringstream os(stringstream::in | stringstream::out);\r
212   for(list<string>::iterator iter = source.begin(); iter != source.end(); iter++) {\r
213     if (iter != source.begin()) {\r
214       os << endl;\r
215     }\r
216     os << *iter;\r
217   }\r
218   dest = os.str();\r
219 }\r
220 \r
221 bool MemcacheBase::addSessionToUser(string &key, string &user) {\r
222 \r
223   if (! addLock(user, false)) {\r
224     return false;\r
225   }\r
226 \r
227   // Aquired lock\r
228 \r
229   string sessid = m_prefix + key; // add specific prefix to session\r
230   string delimiter = ";";\r
231   string user_key = "UDATA:";\r
232   user_key += user;\r
233   string user_val;\r
234   uint32_t flags;\r
235   bool result = getMemcache(user_key.c_str(), user_val, &flags, false);\r
236 \r
237   if (result) {\r
238     bool already_there = false;\r
239     // skip delimiters at beginning.\r
240     string::size_type lastPos = user_val.find_first_not_of(delimiter, 0);\r
241     \r
242     // find first "non-delimiter".\r
243     string::size_type pos = user_val.find_first_of(delimiter, lastPos);\r
244     \r
245     while (string::npos != pos || string::npos != lastPos) {\r
246       // found a token, add it to the vector.\r
247       string session = user_val.substr(lastPos, pos - lastPos);\r
248       if (strcmp(session.c_str(), sessid.c_str()) == 0) {\r
249         already_there = true;\r
250         break;\r
251       }\r
252       \r
253       // skip delimiters.  Note the "not_of"\r
254       lastPos = user_val.find_first_not_of(delimiter, pos);\r
255       \r
256       // find next "non-delimiter"\r
257       pos = user_val.find_first_of(delimiter, lastPos);\r
258     }\r
259     \r
260     if (!already_there) {\r
261       user_val += delimiter + sessid;\r
262       replaceMemcache(user_key.c_str(), user_val, 0, 0, false);\r
263     }\r
264   } else {\r
265     addMemcache(user_key.c_str(), sessid, 0, 0, false);\r
266   }\r
267 \r
268   deleteLock(user, false);\r
269   return true;\r
270   \r
271 }\r
272 \r
273 bool MemcacheBase::deleteMemcache(const char *key,\r
274                                   time_t timeout,\r
275                                   bool use_prefix) {\r
276   memcached_return rv;\r
277   string final_key;\r
278   memcached_st clone;\r
279   bool success;\r
280 \r
281   if (use_prefix) {\r
282     final_key = m_prefix + key;\r
283   } else {\r
284     final_key = key;\r
285   }\r
286 \r
287   memset(&clone, 0 , sizeof(memcached_st));\r
288   if (memcached_clone(&clone, memc) == NULL) {\r
289     throw IOException("MemcacheBase::deleteMemcache(): memcached_clone() failed");\r
290   }\r
291 \r
292   rv = memcached_delete(&clone, (char *)final_key.c_str(), final_key.length(), timeout);\r
293   if (rv == MEMCACHED_SUCCESS) {\r
294     success = true;\r
295   } else if (rv == MEMCACHED_NOTFOUND) {\r
296     // Key wasn't there... No biggie.\r
297     success = false;\r
298   } else {\r
299     log.error(string("Memcache::deleteMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
300     // shouldn't be here\r
301     success = false;\r
302   }\r
303 \r
304   memcached_free(&clone);\r
305   return success;\r
306 }\r
307 \r
308 bool MemcacheBase::getMemcache(const char *key,\r
309                                string &dest,\r
310                                uint32_t *flags,\r
311                                bool use_prefix) {\r
312   memcached_return rv;\r
313   size_t len;\r
314   char *result;\r
315   string final_key;\r
316   memcached_st clone;\r
317   bool success;\r
318   \r
319   if (use_prefix) {\r
320     final_key = m_prefix + key;\r
321   } else {\r
322     final_key = key;\r
323   }\r
324 \r
325   memset(&clone, 0 , sizeof(memcached_st));\r
326   if (memcached_clone(&clone, memc) == NULL) {\r
327     throw IOException("MemcacheBase::getMemcache(): memcached_clone() failed");\r
328   }\r
329 \r
330   result = memcached_get(&clone, (char *)final_key.c_str(), final_key.length(), &len, flags, &rv);\r
331   if (rv == MEMCACHED_SUCCESS) {\r
332     dest = result;\r
333     free(result);\r
334     success = true;\r
335   } else if (rv == MEMCACHED_NOTFOUND) {\r
336     log.debug("Key %s not found in memcache...", key);\r
337     success = false;\r
338   } else {\r
339     log.error(string("Memcache::getMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
340     success = false;\r
341   }\r
342 \r
343   memcached_free(&clone);\r
344   return success;\r
345 }\r
346 \r
347 bool MemcacheBase::addMemcache(const char *key,\r
348                                string &value,\r
349                                time_t timeout,\r
350                                uint32_t flags,\r
351                                bool use_prefix) {\r
352 \r
353   memcached_return rv;\r
354   string final_key;\r
355   memcached_st clone;\r
356   bool success;\r
357 \r
358   if (use_prefix) {\r
359     final_key = m_prefix + key;\r
360   } else {\r
361     final_key = key;\r
362   }\r
363 \r
364   memset(&clone, 0 , sizeof(memcached_st));\r
365   if (memcached_clone(&clone, memc) == NULL) {\r
366     throw IOException("MemcacheBase::addMemcache(): memcached_clone() failed");\r
367   }\r
368 \r
369   rv = memcached_add(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);\r
370   if (rv == MEMCACHED_SUCCESS) {\r
371     success = true;\r
372   } else if (rv == MEMCACHED_NOTSTORED) {\r
373     // already there\r
374     success = false;\r
375   } else {\r
376     // shouldn't be here\r
377     log.error(string("Memcache::addMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
378     success = false;\r
379   }\r
380 \r
381   memcached_free(&clone);\r
382   return success;\r
383 }\r
384 \r
385 bool MemcacheBase::setMemcache(const char *key,\r
386                                string &value,\r
387                                time_t timeout,\r
388                                uint32_t flags,\r
389                                bool use_prefix) {\r
390 \r
391   memcached_return rv;\r
392   string final_key;\r
393   memcached_st clone;\r
394   bool success;\r
395 \r
396   if (use_prefix) {\r
397     final_key = m_prefix + key;\r
398   } else {\r
399     final_key = key;\r
400   }\r
401 \r
402   memset(&clone, 0 , sizeof(memcached_st));\r
403   if (memcached_clone(&clone, memc) == NULL) {\r
404     throw IOException("MemcacheBase::setMemcache(): memcached_clone() failed");\r
405   }\r
406 \r
407   rv = memcached_set(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);\r
408   if (rv == MEMCACHED_SUCCESS) {\r
409     success = true;\r
410   } else {\r
411     // shouldn't be here\r
412     log.error(string("Memcache::setMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
413     success = false;\r
414   }\r
415 \r
416   memcached_free(&clone);\r
417   return success;\r
418 }\r
419 \r
420 bool MemcacheBase::replaceMemcache(const char *key,\r
421                                    string &value,\r
422                                    time_t timeout,\r
423                                    uint32_t flags,\r
424                                    bool use_prefix) {\r
425   \r
426   memcached_return rv;\r
427   string final_key;\r
428   memcached_st clone;\r
429   bool success;\r
430 \r
431   if (use_prefix) {\r
432     final_key = m_prefix + key;\r
433   } else {\r
434     final_key = key;\r
435   }\r
436 \r
437   memset(&clone, 0 , sizeof(memcached_st));\r
438   if (memcached_clone(&clone, memc) == NULL) {\r
439     throw IOException("MemcacheBase::replaceMemcache(): memcached_clone() failed");\r
440   }\r
441 \r
442   rv = memcached_replace(&clone, (char *)final_key.c_str(), final_key.length(), (char *)value.c_str(), value.length(), timeout, flags);\r
443   if (rv == MEMCACHED_SUCCESS) {\r
444     success = true;\r
445   } else if (rv == MEMCACHED_NOTSTORED) {\r
446     // not there\r
447     success = false;\r
448   } else {\r
449     // shouldn't be here\r
450     log.error(string("Memcache::replaceMemcache() Problems: ") + memcached_strerror(&clone, rv));\r
451     success = false;\r
452   }\r
453 \r
454   memcached_free(&clone);\r
455   return success;\r
456 }\r
457 \r
458 MemcacheBase::MemcacheBase(const DOMElement* e) : m_root(e), log(Category::getInstance("XMLTooling.MemcacheBase")), m_memcacheHosts(""), m_prefix("") {\r
459 \r
460   auto_ptr_char p(e ? e->getAttributeNS(NULL,prefix) : NULL);\r
461   if (p.get() && *p.get()) {\r
462     log.debug("INIT: GOT key prefix: %s", p.get());\r
463     m_prefix = p.get();\r
464   }\r
465 \r
466   // Grab hosts from the configuration.\r
467   e = e ? XMLHelper::getFirstChildElement(e,Hosts) : NULL;\r
468   if (!e || !e->hasChildNodes()) {\r
469     throw XMLToolingException("Memcache StorageService requires Hosts element in configuration.");\r
470   }\r
471   auto_ptr_char h(e->getFirstChild()->getNodeValue());\r
472   log.debug("INIT: GOT Hosts: %s", h.get());\r
473   m_memcacheHosts = h.get();\r
474 \r
475   memc = memcached_create(NULL);\r
476   if (memc == NULL) {\r
477     throw XMLToolingException("MemcacheBase::Memcache(): memcached_create() failed");\r
478   }\r
479 \r
480   log.debug("Memcache created");\r
481 \r
482   unsigned int set = MEMCACHED_HASH_CRC;\r
483   memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_HASH, set);\r
484   log.debug("CRC hash set");\r
485 \r
486   memcached_server_st *servers;\r
487   servers = memcached_servers_parse((char *)m_memcacheHosts.c_str());\r
488   log.debug("Got %u hosts.",  memcached_server_list_count(servers));\r
489   if (memcached_server_push(memc, servers) != MEMCACHED_SUCCESS) {\r
490     throw IOException("MemcacheBase::Memcache(): memcached_server_push() failed");    \r
491   }\r
492   memcached_server_list_free(servers);\r
493 \r
494   log.debug("Memcache object initialized");\r
495 }\r
496 \r
497 MemcacheBase::~MemcacheBase() {\r
498   memcached_free(memc);\r
499   log.debug("Base object destroyed");\r
500 }\r
501 \r
502 MemcacheStorageService::MemcacheStorageService(const DOMElement* e)\r
503   : MemcacheBase(e), m_log(Category::getInstance("XMLTooling.MemcacheStorageService")), m_buildMap(false) {\r
504 \r
505     const XMLCh* tag=e ? e->getAttributeNS(NULL,buildMap) : NULL;\r
506     if (tag && *tag && XMLString::parseInt(tag) != 0) {\r
507         m_buildMap = true;\r
508         m_log.debug("Cache built with buildMap ON");\r
509     }\r
510 \r
511 }\r
512 \r
513 MemcacheStorageService::~MemcacheStorageService() {\r
514 \r
515   \r
516 }\r
517 \r
518 bool MemcacheStorageService::createString(const char* context, const char* key, const char* value, time_t expiration) {\r
519 \r
520   log.debug("createString ctx: %s - key: %s", context, key);\r
521 \r
522   string final_key = string(context) + ":" + string(key);\r
523 \r
524   mc_record rec(value, expiration);\r
525   string final_value;\r
526   serialize(rec, final_value);\r
527 \r
528   bool result = addMemcache(final_key.c_str(), final_value, expiration, 1); // the flag will be the version\r
529 \r
530   if (result && m_buildMap) {\r
531     log.debug("Got result, updating map");\r
532 \r
533     string map_name = context;\r
534     // we need to update the context map\r
535     if (! addLock(map_name)) {\r
536       log.error("Unable to get lock for context %s!", context);\r
537       deleteMemcache(final_key.c_str(), 0);\r
538       return false;\r
539     }\r
540 \r
541     string ser_arr;\r
542     uint32_t flags;\r
543     bool result = getMemcache(map_name.c_str(), ser_arr, &flags);\r
544     \r
545     list<string> contents;\r
546     if (result) {\r
547       log.debug("Match found. Parsing...");\r
548 \r
549       deserialize(ser_arr, contents);\r
550       \r
551       log.debug("Iterating retrieved session map...");\r
552       list<string>::iterator iter;\r
553       for(iter = contents.begin(); \r
554           iter != contents.end();\r
555           iter++) {\r
556         log.debug("value = " + *iter);\r
557       }\r
558 \r
559     } else {\r
560       log.debug("New context: %s", map_name.c_str());\r
561 \r
562     }\r
563 \r
564     contents.push_back(key);\r
565     serialize(contents, ser_arr);    \r
566     setMemcache(map_name.c_str(), ser_arr, expiration, 0);    \r
567     \r
568     deleteLock(map_name);\r
569   }\r
570 \r
571   return result;  \r
572 \r
573 }\r
574 \r
575 int MemcacheStorageService::readString(const char* context, const char* key, string* pvalue, time_t* pexpiration, int version) {\r
576 \r
577   log.debug("readString ctx: %s - key: %s", context, key);\r
578 \r
579   string final_key = string(context) + ":" + string(key);\r
580   uint32_t rec_version;\r
581   string value;\r
582 \r
583   bool found = getMemcache(final_key.c_str(), value, &rec_version);\r
584   if (!found) {\r
585     return 0;\r
586   }\r
587 \r
588   if (version && rec_version <= (uint32_t)version) {\r
589     return version;\r
590   }\r
591 \r
592   if (pexpiration || pvalue) {\r
593     mc_record rec;\r
594     deserialize(value, rec);\r
595     \r
596     if (pexpiration) {\r
597       *pexpiration = rec.expiration;\r
598     }\r
599     \r
600     if (pvalue) {\r
601       *pvalue = rec.value;\r
602     }\r
603   }\r
604   \r
605   return rec_version;\r
606 \r
607 }\r
608 \r
609 int MemcacheStorageService::updateString(const char* context, const char* key, const char* value, time_t expiration, int version) {\r
610 \r
611   log.debug("updateString ctx: %s - key: %s", context, key);\r
612 \r
613   time_t final_exp = expiration;\r
614   time_t *want_expiration = NULL;\r
615   if (! final_exp) {\r
616     want_expiration = &final_exp;\r
617   }\r
618 \r
619   int read_res = readString(context, key, NULL, want_expiration, version);\r
620 \r
621   if (!read_res) {\r
622     // not found\r
623     return read_res;\r
624   }\r
625 \r
626   if (version && version != read_res) {\r
627     // version incorrect\r
628     return -1;\r
629   }\r
630 \r
631   // Proceding with update\r
632   string final_key = string(context) + ":" + string(key);\r
633   mc_record rec(value, final_exp);\r
634   string final_value;\r
635   serialize(rec, final_value);\r
636 \r
637   replaceMemcache(final_key.c_str(), final_value, final_exp, ++version);\r
638   return version;\r
639 \r
640 }\r
641 \r
642 bool MemcacheStorageService::deleteString(const char* context, const char* key) {\r
643 \r
644   log.debug("deleteString ctx: %s - key: %s", context, key);\r
645   \r
646   string final_key = string(context) + ":" + string(key);\r
647 \r
648   // Not updating context map, if there is one. There is no need.\r
649 \r
650   return deleteMemcache(final_key.c_str(), 0);\r
651 \r
652 }\r
653 \r
654 void MemcacheStorageService::updateContext(const char* context, time_t expiration) {\r
655 \r
656   log.debug("updateContext ctx: %s", context);\r
657 \r
658   if (!m_buildMap) {\r
659     log.error("updateContext invoked on a Storage with no context map built!");\r
660     return;\r
661   }\r
662 \r
663   string map_name = context;\r
664   \r
665   if (! addLock(map_name)) {\r
666     log.error("Unable to get lock for context %s!", context);\r
667     return;\r
668   }\r
669   \r
670   string ser_arr;\r
671   uint32_t flags;\r
672   bool result = getMemcache(map_name.c_str(), ser_arr, &flags);\r
673   \r
674   list<string> contents;\r
675   if (result) {\r
676     log.debug("Match found. Parsing...");\r
677     \r
678     deserialize(ser_arr, contents);\r
679     \r
680     log.debug("Iterating retrieved session map...");\r
681     list<string>::iterator iter;\r
682     for(iter = contents.begin(); \r
683         iter != contents.end();\r
684         iter++) {\r
685 \r
686       // Update expiration times\r
687       string value;      \r
688       int read_res = readString(context, iter->c_str(), &value, NULL, 0);\r
689       \r
690       if (!read_res) {\r
691         // not found\r
692         continue;\r
693       }\r
694 \r
695       updateString(context, iter->c_str(), value.c_str(), expiration, read_res);\r
696     }\r
697     replaceMemcache(map_name.c_str(), ser_arr, expiration, flags);\r
698   }\r
699   \r
700   deleteLock(map_name);\r
701   \r
702 }\r
703 \r
704 void MemcacheStorageService::deleteContext(const char* context) {\r
705 \r
706   log.debug("deleteContext ctx: %s", context);\r
707 \r
708   if (!m_buildMap) {\r
709     log.error("deleteContext invoked on a Storage with no context map built!");\r
710     return;\r
711   }\r
712 \r
713   string map_name = context;\r
714   \r
715   if (! addLock(map_name)) {\r
716     log.error("Unable to get lock for context %s!", context);\r
717     return;\r
718   }\r
719   \r
720   string ser_arr;\r
721   uint32_t flags;\r
722   bool result = getMemcache(map_name.c_str(), ser_arr, &flags);\r
723   \r
724   list<string> contents;\r
725   if (result) {\r
726     log.debug("Match found. Parsing...");\r
727     \r
728     deserialize(ser_arr, contents);\r
729     \r
730     log.debug("Iterating retrieved session map...");\r
731     list<string>::iterator iter;\r
732     for(iter = contents.begin(); \r
733         iter != contents.end();\r
734         iter++) {\r
735       string final_key = map_name + *iter;\r
736       deleteMemcache(final_key.c_str(), 0);\r
737     }\r
738     \r
739     deleteMemcache(map_name.c_str(), 0);\r
740   }\r
741   \r
742   deleteLock(map_name);\r
743 \r
744 }\r
745 \r
746 extern "C" int MCEXT_EXPORTS xmltooling_extension_init(void*) {\r
747     // Register this SS type\r
748     XMLToolingConfig::getConfig().StorageServiceManager.registerFactory("MEMCACHE", MemcacheStorageServiceFactory);\r
749     return 0;\r
750 }\r
751 \r
752 extern "C" void MCEXT_EXPORTS xmltooling_extension_term() {\r
753     XMLToolingConfig::getConfig().StorageServiceManager.deregisterFactory("MEMCACHE");\r
754 }\r