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