2 * Copyright 2001-2007 Internet2
\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
8 * http://www.apache.org/licenses/LICENSE-2.0
\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
18 * MemoryStorageService.cpp
\r
20 * In-memory "persistent" storage, suitable for simple applications.
\r
23 #include "internal.h"
\r
24 #include "logging.h"
\r
25 #include "util/NDC.h"
\r
26 #include "util/StorageService.h"
\r
27 #include "util/Threads.h"
\r
28 #include "util/XMLHelper.h"
\r
31 #include <xercesc/util/XMLUniDefs.hpp>
\r
33 using namespace xmltooling::logging;
\r
34 using namespace xmltooling;
\r
35 using namespace std;
\r
37 namespace xmltooling {
\r
38 class XMLTOOL_DLLLOCAL MemoryStorageService : public StorageService
\r
41 MemoryStorageService(const DOMElement* e);
\r
42 virtual ~MemoryStorageService();
\r
44 bool createString(const char* context, const char* key, const char* value, time_t expiration);
\r
45 int readString(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0);
\r
46 int updateString(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0);
\r
47 bool deleteString(const char* context, const char* key);
\r
49 bool createText(const char* context, const char* key, const char* value, time_t expiration) {
\r
50 return createString(context, key, value, expiration);
\r
52 int readText(const char* context, const char* key, string* pvalue=NULL, time_t* pexpiration=NULL, int version=0) {
\r
53 return readString(context, key, pvalue, pexpiration, version);
\r
55 int updateText(const char* context, const char* key, const char* value=NULL, time_t expiration=0, int version=0) {
\r
56 return updateString(context, key, value, expiration, version);
\r
58 bool deleteText(const char* context, const char* key) {
\r
59 return deleteString(context, key);
\r
62 void reap(const char* context);
\r
63 void updateContext(const char* context, time_t expiration);
\r
64 void deleteContext(const char* context) {
\r
66 m_contextMap.erase(context);
\r
73 struct XMLTOOL_DLLLOCAL Record {
\r
74 Record() : expiration(0), version(1) {}
\r
75 Record(const string& s, time_t t) : data(s), expiration(t), version(1) {}
\r
81 struct XMLTOOL_DLLLOCAL Context {
\r
83 Context(const Context& src) {
\r
84 m_dataMap = src.m_dataMap;
\r
86 map<string,Record> m_dataMap;
\r
87 unsigned long reap(time_t exp);
\r
90 Context& readContext(const char* context) {
\r
92 map<string,Context>::iterator i = m_contextMap.find(context);
\r
93 if (i != m_contextMap.end())
\r
97 return m_contextMap[context];
\r
100 Context& writeContext(const char* context) {
\r
102 return m_contextMap[context];
\r
105 map<string,Context> m_contextMap;
\r
107 CondWait* shutdown_wait;
\r
108 Thread* cleanup_thread;
\r
109 static void* cleanup_fn(void*);
\r
111 int m_cleanupInterval;
\r
115 StorageService* XMLTOOL_DLLLOCAL MemoryStorageServiceFactory(const DOMElement* const & e)
\r
117 return new MemoryStorageService(e);
\r
121 static const XMLCh cleanupInterval[] = UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);
\r
123 MemoryStorageService::MemoryStorageService(const DOMElement* e)
\r
124 : m_lock(NULL), shutdown_wait(NULL), cleanup_thread(NULL), shutdown(false), m_cleanupInterval(0),
\r
125 m_log(Category::getInstance(XMLTOOLING_LOGCAT".StorageService"))
\r
127 const XMLCh* tag=e ? e->getAttributeNS(NULL,cleanupInterval) : NULL;
\r
129 m_cleanupInterval = XMLString::parseInt(tag);
\r
131 if (!m_cleanupInterval)
\r
132 m_cleanupInterval=900;
\r
134 m_lock = RWLock::create();
\r
135 shutdown_wait = CondWait::create();
\r
136 cleanup_thread = Thread::create(&cleanup_fn, (void*)this);
\r
139 MemoryStorageService::~MemoryStorageService()
\r
141 // Shut down the cleanup thread and let it know...
\r
143 shutdown_wait->signal();
\r
144 cleanup_thread->join(NULL);
\r
146 delete cleanup_thread;
\r
147 delete shutdown_wait;
\r
151 void* MemoryStorageService::cleanup_fn(void* cache_p)
\r
153 MemoryStorageService* cache = reinterpret_cast<MemoryStorageService*>(cache_p);
\r
156 // First, let's block all signals
\r
157 Thread::mask_all_signals();
\r
160 // Now run the cleanup process.
\r
165 void MemoryStorageService::cleanup()
\r
168 NDC ndc("cleanup");
\r
171 auto_ptr<Mutex> mutex(Mutex::create());
\r
174 m_log.info("cleanup thread started...running every %d seconds", m_cleanupInterval);
\r
176 while (!shutdown) {
\r
177 shutdown_wait->timedwait(mutex.get(), m_cleanupInterval);
\r
181 unsigned long count=0;
\r
182 time_t now = time(NULL);
\r
184 SharedLock locker(m_lock, false);
\r
185 for (map<string,Context>::iterator i=m_contextMap.begin(); i!=m_contextMap.end(); ++i)
\r
186 count += i->second.reap(now);
\r
189 m_log.info("purged %d expired record(s) from storage", count);
\r
192 m_log.info("cleanup thread finished");
\r
195 Thread::exit(NULL);
\r
198 void MemoryStorageService::reap(const char* context)
\r
200 Context& ctx = writeContext(context);
\r
201 SharedLock locker(m_lock, false);
\r
202 ctx.reap(time(NULL));
\r
205 unsigned long MemoryStorageService::Context::reap(time_t exp)
\r
207 // Garbage collect any expired entries.
\r
208 unsigned long count=0;
\r
209 map<string,Record>::iterator cur = m_dataMap.begin();
\r
210 map<string,Record>::iterator stop = m_dataMap.end();
\r
211 while (cur != stop) {
\r
212 if (cur->second.expiration <= exp) {
\r
213 map<string,Record>::iterator tmp = cur++;
\r
214 m_dataMap.erase(tmp);
\r
224 bool MemoryStorageService::createString(const char* context, const char* key, const char* value, time_t expiration)
\r
226 Context& ctx = writeContext(context);
\r
227 SharedLock locker(m_lock, false);
\r
229 // Check for a duplicate.
\r
230 map<string,Record>::iterator i=ctx.m_dataMap.find(key);
\r
231 if (i!=ctx.m_dataMap.end()) {
\r
232 // Not yet expired?
\r
233 if (time(NULL) < i->second.expiration)
\r
235 // It's dead, so we can just remove it now and create the new record.
\r
236 ctx.m_dataMap.erase(i);
\r
239 ctx.m_dataMap[key]=Record(value,expiration);
\r
241 m_log.debug("inserted record (%s) in context (%s)", key, context);
\r
245 int MemoryStorageService::readString(const char* context, const char* key, string* pvalue, time_t* pexpiration, int version)
\r
247 Context& ctx = readContext(context);
\r
248 SharedLock locker(m_lock, false);
\r
250 map<string,Record>::iterator i=ctx.m_dataMap.find(key);
\r
251 if (i==ctx.m_dataMap.end())
\r
253 else if (time(NULL) >= i->second.expiration)
\r
256 *pexpiration = i->second.expiration;
\r
257 if (i->second.version == version)
\r
258 return version; // nothing's changed, so just echo back the version
\r
260 *pvalue = i->second.data;
\r
261 return i->second.version;
\r
264 int MemoryStorageService::updateString(const char* context, const char* key, const char* value, time_t expiration, int version)
\r
266 Context& ctx = writeContext(context);
\r
267 SharedLock locker(m_lock, false);
\r
269 map<string,Record>::iterator i=ctx.m_dataMap.find(key);
\r
270 if (i==ctx.m_dataMap.end())
\r
272 else if (time(NULL) >= i->second.expiration)
\r
275 if (version > 0 && version != i->second.version)
\r
276 return -1; // caller's out of sync
\r
279 i->second.data = value;
\r
280 ++(i->second.version);
\r
283 if (expiration && expiration != i->second.expiration)
\r
284 i->second.expiration = expiration;
\r
286 m_log.debug("updated record (%s) in context (%s)", key, context);
\r
287 return i->second.version;
\r
290 bool MemoryStorageService::deleteString(const char* context, const char* key)
\r
292 Context& ctx = writeContext(context);
\r
293 SharedLock locker(m_lock, false);
\r
295 // Find the record.
\r
296 map<string,Record>::iterator i=ctx.m_dataMap.find(key);
\r
297 if (i!=ctx.m_dataMap.end()) {
\r
298 ctx.m_dataMap.erase(i);
\r
299 m_log.debug("deleted record (%s) in context (%s)", key, context);
\r
303 m_log.debug("deleting record (%s) in context (%s)....not found", key, context);
\r
307 void MemoryStorageService::updateContext(const char* context, time_t expiration)
\r
309 Context& ctx = writeContext(context);
\r
310 SharedLock locker(m_lock, false);
\r
312 time_t now = time(NULL);
\r
313 map<string,Record>::iterator stop=ctx.m_dataMap.end();
\r
314 for (map<string,Record>::iterator i = ctx.m_dataMap.begin(); i!=stop; ++i) {
\r
315 if (now < i->second.expiration)
\r
316 i->second.expiration = expiration;
\r
319 m_log.debug("updated expiration of valid records in context (%s)", context);
\r