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