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