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