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