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