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