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