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