Added a simple storage abstraction, and an in-memory sample.
authorScott Cantor <cantor.2@osu.edu>
Thu, 21 Sep 2006 19:29:33 +0000 (19:29 +0000)
committerScott Cantor <cantor.2@osu.edu>
Thu, 21 Sep 2006 19:29:33 +0000 (19:29 +0000)
14 files changed:
.project
xmltooling/Makefile.am
xmltooling/XMLToolingConfig.cpp
xmltooling/XMLToolingConfig.h
xmltooling/exceptions.h
xmltooling/impl/MemoryStorageService.cpp [new file with mode: 0644]
xmltooling/util/PThreads.cpp
xmltooling/util/StorageService.cpp [new file with mode: 0644]
xmltooling/util/StorageService.h [new file with mode: 0644]
xmltooling/util/Threads.h
xmltooling/util/Win32Threads.cpp
xmltooling/xmltooling.vcproj
xmltoolingtest/MemoryStorageServiceTest.h [new file with mode: 0644]
xmltoolingtest/xmltoolingtest.vcproj

index 573a04f..40ff0eb 100644 (file)
--- a/.project
+++ b/.project
                                        <value>org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser;</value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.enableAutoBuild</key>\r
-                                       <value>false</value>\r
-                               </dictionary>\r
-                               <dictionary>\r
                                        <key>org.eclipse.cdt.make.core.environment</key>\r
                                        <value></value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.enableFullBuild</key>\r
-                                       <value>true</value>\r
+                                       <key>org.eclipse.cdt.make.core.enableAutoBuild</key>\r
+                                       <value>false</value>\r
                                </dictionary>\r
                                <dictionary>\r
                                        <key>org.eclipse.cdt.make.core.build.target.inc</key>\r
                                        <value>all</value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.enabledIncrementalBuild</key>\r
+                                       <key>org.eclipse.cdt.make.core.enableFullBuild</key>\r
                                        <value>true</value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.build.target.clean</key>\r
-                                       <value>clean</value>\r
+                                       <key>org.eclipse.cdt.make.core.enabledIncrementalBuild</key>\r
+                                       <value>true</value>\r
                                </dictionary>\r
                                <dictionary>\r
                                        <key>org.eclipse.cdt.make.core.build.command</key>\r
                                        <value>make</value>\r
                                </dictionary>\r
                                <dictionary>\r
+                                       <key>org.eclipse.cdt.make.core.build.target.clean</key>\r
+                                       <value>clean</value>\r
+                               </dictionary>\r
+                               <dictionary>\r
                                        <key>org.eclipse.cdt.make.core.enableCleanBuild</key>\r
                                        <value>true</value>\r
                                </dictionary>\r
index 9a2bcdd..38cd95e 100644 (file)
@@ -81,6 +81,7 @@ utilinclude_HEADERS = \
        util/DateTime.h \
        util/NDC.h \
        util/ParserPool.h \
+       util/StorageService.h \
        util/Threads.h \
        util/XMLConstants.h \
        util/XMLHelper.h \
@@ -132,6 +133,7 @@ libxmltooling_la_SOURCES = \
        encryption/impl/EncryptionImpl.cpp \
        encryption/impl/EncryptionSchemaValidators.cpp \
        impl/AnyElement.cpp \
+       impl/MemoryStorageService.cpp \
        impl/UnknownElement.cpp \
        io/AbstractXMLObjectMarshaller.cpp \
        io/AbstractXMLObjectUnmarshaller.cpp \
@@ -142,6 +144,7 @@ libxmltooling_la_SOURCES = \
        util/DateTime.cpp \
        util/NDC.cpp \
        util/ParserPool.cpp \
+       util/StorageService.cpp \
        util/XMLConstants.cpp \
        util/XMLHelper.cpp \
        validation/ValidatorSuite.cpp \
index a7cbc42..f8e7f3b 100644 (file)
@@ -30,6 +30,7 @@
 #include "signature/CredentialResolver.h"
 #include "soap/SOAP.h"
 #include "util/NDC.h"
+#include "util/StorageService.h"
 #include "util/XMLConstants.h"
 #include "validation/Validator.h"
 
@@ -62,6 +63,8 @@ DECL_EXCEPTION_FACTORY(UnknownElementException,xmltooling);
 DECL_EXCEPTION_FACTORY(UnknownAttributeException,xmltooling);
 DECL_EXCEPTION_FACTORY(UnknownExtensionException,xmltooling);
 DECL_EXCEPTION_FACTORY(ValidationException,xmltooling);
+DECL_EXCEPTION_FACTORY(XMLSecurityException,xmltooling);
+DECL_EXCEPTION_FACTORY(IOException,xmltooling);
 
 #ifndef XMLTOOLING_NO_XMLSEC
     DECL_EXCEPTION_FACTORY(SignatureException,xmlsignature);
@@ -192,6 +195,8 @@ bool XMLToolingInternalConfig::init()
         REGISTER_EXCEPTION_FACTORY(UnknownElementException,xmltooling);
         REGISTER_EXCEPTION_FACTORY(UnknownAttributeException,xmltooling);
         REGISTER_EXCEPTION_FACTORY(ValidationException,xmltooling);
+        REGISTER_EXCEPTION_FACTORY(XMLSecurityException,xmltooling);
+        REGISTER_EXCEPTION_FACTORY(IOException,xmltooling);
         
 #ifndef XMLTOOLING_NO_XMLSEC
         XMLObjectBuilder::registerBuilder(QName(XMLConstants::XMLSIG_NS,Signature::LOCAL_NAME),new SignatureBuilder());
@@ -200,6 +205,7 @@ bool XMLToolingInternalConfig::init()
         registerCredentialResolvers();
         registerTrustEngines();
 #endif
+        registerStorageServices();
 
         // Register xml:id as an ID attribute.        
         static const XMLCh xmlid[] = UNICODE_LITERAL_2(i,d);
index f60a06a..e1ce7f0 100644 (file)
@@ -41,6 +41,7 @@ namespace xmlsignature {
 \r
 namespace xmltooling {\r
     \r
+    class XMLTOOL_API StorageService;\r
     class XMLTOOL_API TrustEngine;\r
     class XMLTOOL_API XSECCryptoX509CRL;\r
 \r
@@ -53,7 +54,9 @@ namespace xmltooling {
      */\r
     class XMLTOOL_API XMLToolingConfig : public Lockable\r
     {\r
-    MAKE_NONCOPYABLE(XMLToolingConfig);\r
+        MAKE_NONCOPYABLE(XMLToolingConfig);\r
+    protected:\r
+        XMLToolingConfig() : clock_skew_secs(180) {}\r
     public:\r
         virtual ~XMLToolingConfig() {}\r
 \r
@@ -127,11 +130,17 @@ namespace xmltooling {
         virtual ParserPool& getValidatingParser() const=0;\r
         \r
         /**\r
-         * Set to catalog files to load into validating parser pool at initialization time.\r
+         * List of catalog files to load into validating parser pool at initialization time.\r
          * Like other path settings, the separator depends on the platform\r
          * (semicolon on Windows, colon otherwise). \r
          */\r
         std::string catalog_path;\r
+        \r
+        /**\r
+         * Adjusts any clock comparisons to be more liberal/permissive by the\r
+         * indicated number of seconds.\r
+         */\r
+        unsigned int clock_skew_secs;\r
 \r
 #ifndef XMLTOOLING_NO_XMLSEC\r
         /**\r
@@ -155,8 +164,10 @@ namespace xmltooling {
         PluginManager<TrustEngine,const DOMElement*> TrustEngineManager;\r
 #endif\r
 \r
-    protected:\r
-        XMLToolingConfig() {}\r
+        /**\r
+         * Manages factories for StorageService plugins.\r
+         */\r
+        PluginManager<StorageService,const DOMElement*> StorageServiceManager;\r
     };\r
 \r
 };\r
index 1c07a34..55f8208 100644 (file)
@@ -351,6 +351,7 @@ namespace xmltooling {
     DECL_XMLTOOLING_EXCEPTION(UnknownExtensionException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmltooling,XMLToolingException,Exceptions from use of an unrecognized extension/plugin);
     DECL_XMLTOOLING_EXCEPTION(ValidationException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmltooling,XMLToolingException,Exceptions during object validation);
     DECL_XMLTOOLING_EXCEPTION(XMLSecurityException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmltooling,XMLToolingException,Exceptions related to the XML security layer);
+    DECL_XMLTOOLING_EXCEPTION(IOException,XMLTOOL_EXCEPTIONAPI(XMLTOOL_API),xmltooling,XMLToolingException,Exceptions related to physical input/output errors);
 
 };
 
diff --git a/xmltooling/impl/MemoryStorageService.cpp b/xmltooling/impl/MemoryStorageService.cpp
new file mode 100644 (file)
index 0000000..3a041ea
--- /dev/null
@@ -0,0 +1,263 @@
+/*\r
+ *  Copyright 2001-2005 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * MemoryStorageService.cpp\r
+ * \r
+ * In-memory "persistent" storage, suitable for simple applications.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "util/NDC.h"\r
+#include "util/StorageService.h"\r
+#include "util/Threads.h"\r
+#include "util/XMLHelper.h"\r
+\r
+#include <log4cpp/Category.hh>\r
+#include <xercesc/util/XMLUniDefs.hpp>\r
+\r
+using namespace xmltooling;\r
+using namespace log4cpp;\r
+using namespace std;\r
+\r
+namespace xmltooling {\r
+    class XMLTOOL_DLLLOCAL MemoryStorageService : public StorageService\r
+    {\r
+    public:\r
+        MemoryStorageService(const DOMElement* e);\r
+        virtual ~MemoryStorageService();\r
+        \r
+        void createString(const char* key, const char* value, time_t expiration);\r
+        bool readString(const char* key, string& value, time_t modifiedSince=0);\r
+        bool updateString(const char* key, const char* value=NULL, time_t expiration=0);\r
+        bool deleteString(const char* key);\r
+        \r
+        void createText(const char* key, const char* value, time_t expiration) {\r
+            return createString(key, value, expiration);\r
+        }\r
+        bool readText(const char* key, string& value, time_t modifiedSince=0) {\r
+            return readString(key, value, modifiedSince);\r
+        }\r
+        bool updateText(const char* key, const char* value=NULL, time_t expiration=0) {\r
+            return updateString(key, value, expiration);\r
+        }\r
+        bool deleteText(const char* key) {\r
+            return deleteString(key);\r
+        }\r
+        \r
+        void reap() {\r
+            shutdown_wait->signal();\r
+        }\r
+\r
+    private:\r
+        void cleanup();\r
+    \r
+        struct XMLTOOL_DLLLOCAL Record {\r
+            Record() : modified(0), expiration(0) {}\r
+            Record(string s, time_t t1, time_t t2) : data(s), modified(t1), expiration(t2) {}\r
+            string data;\r
+            time_t modified, expiration;\r
+        };\r
+        \r
+        map<string,Record> m_dataMap;\r
+        multimap<time_t,string> m_expMap;\r
+        RWLock* m_lock;\r
+        CondWait* shutdown_wait;\r
+        Thread* cleanup_thread;\r
+        static void* cleanup_fn(void*);\r
+        bool shutdown;\r
+        int m_cleanupInterval;\r
+        Category& m_log;\r
+    };\r
+\r
+    StorageService* XMLTOOL_DLLLOCAL MemoryStorageServiceFactory(const DOMElement* const & e)\r
+    {\r
+        return new MemoryStorageService(e);\r
+    }\r
+\r
+};\r
+\r
+static const XMLCh cleanupInterval[] = UNICODE_LITERAL_15(c,l,e,a,n,u,p,I,n,t,e,r,v,a,l);\r
+\r
+MemoryStorageService::MemoryStorageService(const DOMElement* e)\r
+    : m_lock(NULL), shutdown_wait(NULL), cleanup_thread(NULL), shutdown(false), m_cleanupInterval(0),\r
+        m_log(Category::getInstance(XMLTOOLING_LOGCAT".StorageService"))\r
+{\r
+    m_lock = RWLock::create();\r
+    shutdown_wait = CondWait::create();\r
+    cleanup_thread = Thread::create(&cleanup_fn, (void*)this);\r
+\r
+    const XMLCh* tag=e ? e->getAttributeNS(NULL,cleanupInterval) : NULL;\r
+    if (tag && *tag) {\r
+        m_cleanupInterval = XMLString::parseInt(tag);\r
+    }\r
+    if (!m_cleanupInterval)\r
+        m_cleanupInterval=300;\r
+}\r
+\r
+MemoryStorageService::~MemoryStorageService()\r
+{\r
+    // Shut down the cleanup thread and let it know...\r
+    shutdown = true;\r
+    shutdown_wait->signal();\r
+    cleanup_thread->join(NULL);\r
+\r
+    delete m_lock;\r
+    delete shutdown_wait;\r
+}\r
+\r
+void* MemoryStorageService::cleanup_fn(void* cache_p)\r
+{\r
+    MemoryStorageService* cache = reinterpret_cast<MemoryStorageService*>(cache_p);\r
+\r
+#ifndef WIN32\r
+    // First, let's block all signals \r
+    Thread::mask_all_signals();\r
+#endif\r
+\r
+    // Now run the cleanup process.\r
+    cache->cleanup();\r
+    return NULL;\r
+}\r
+\r
+void MemoryStorageService::cleanup()\r
+{\r
+#ifdef _DEBUG\r
+    NDC ndc("cleanup");\r
+#endif\r
+    \r
+\r
+    Mutex* mutex = Mutex::create();\r
+    mutex->lock();\r
+\r
+    m_log.info("cleanup thread started...running every %d seconds", m_cleanupInterval);\r
+\r
+    while (!shutdown) {\r
+        shutdown_wait->timedwait(mutex, m_cleanupInterval);\r
+        if (shutdown)\r
+            break;\r
+\r
+        // Lock the "database".\r
+        m_lock->wrlock();\r
+        \r
+        // Garbage collect any expired entries.\r
+        unsigned int count=0;\r
+        time_t now=time(NULL)-XMLToolingConfig::getConfig().clock_skew_secs;\r
+        multimap<time_t,string>::iterator stop=m_expMap.upper_bound(now);\r
+        for (multimap<time_t,string>::iterator i=m_expMap.begin(); i!=stop; m_expMap.erase(i++)) {\r
+            m_dataMap.erase(i->second);\r
+            ++count;\r
+        }\r
+        \r
+        m_lock->unlock();\r
+        \r
+        if (count)\r
+            m_log.info("purged %d record(s) from storage", count);\r
+    }\r
+\r
+    m_log.info("cleanup thread finished");\r
+\r
+    mutex->unlock();\r
+    delete mutex;\r
+    Thread::exit(NULL);\r
+}\r
+\r
+void MemoryStorageService::createString(const char* key, const char* value, time_t expiration)\r
+{\r
+    // Lock the maps.\r
+    m_lock->wrlock();\r
+    SharedLock wrapper(m_lock, false);\r
+    \r
+    // Check for a duplicate.\r
+    map<string,Record>::iterator i=m_dataMap.find(key);\r
+    if (i!=m_dataMap.end())\r
+        throw IOException("attempted to insert a record with duplicate key ($1)", params(1,key));\r
+    \r
+    m_dataMap[key]=Record(value,time(NULL),expiration);\r
+    m_expMap.insert(multimap<time_t,string>::value_type(expiration,key));\r
+    \r
+    m_log.debug("inserted record (%s)", key);\r
+}\r
+\r
+bool MemoryStorageService::readString(const char* key, string& value, time_t modifiedSince)\r
+{\r
+    SharedLock wrapper(m_lock);\r
+    map<string,Record>::iterator i=m_dataMap.find(key);\r
+    if (i==m_dataMap.end())\r
+        return false;\r
+    else if (modifiedSince >= i->second.modified)\r
+        return false;\r
+    value = i->second.data;\r
+    return true;\r
+}\r
+\r
+bool MemoryStorageService::updateString(const char* key, const char* value, time_t expiration)\r
+{\r
+    // Lock the maps.\r
+    m_lock->wrlock();\r
+    SharedLock wrapper(m_lock, false);\r
+\r
+    map<string,Record>::iterator i=m_dataMap.find(key);\r
+    if (i==m_dataMap.end())\r
+        return false;\r
+        \r
+    if (value)\r
+        i->second.data = value;\r
+        \r
+    if (expiration && expiration != i->second.expiration) {\r
+        // Update secondary map.\r
+        pair<multimap<time_t,string>::iterator,multimap<time_t,string>::iterator> range=m_expMap.equal_range(i->second.expiration);\r
+        for (; range.first != range.second; ++range.first) {\r
+            if (range.first->second == i->first) {\r
+                m_expMap.erase(range.first);\r
+                break;\r
+            }\r
+        }\r
+        i->second.expiration = expiration;\r
+        m_expMap.insert(multimap<time_t,string>::value_type(expiration,key));\r
+    }\r
+\r
+    i->second.modified = time(NULL);\r
+    m_log.debug("updated record (%s)", key);\r
+    return true;\r
+}\r
+\r
+bool MemoryStorageService::deleteString(const char* key)\r
+{\r
+    // Lock the maps.\r
+    m_lock->wrlock();\r
+    SharedLock wrapper(m_lock, false);\r
+    \r
+    // Find the record.\r
+    map<string,Record>::iterator i=m_dataMap.find(key);\r
+    if (i!=m_dataMap.end()) {\r
+        // Now find the reversed index of expiration to key, so we can clear it.\r
+        pair<multimap<time_t,string>::iterator,multimap<time_t,string>::iterator> range=m_expMap.equal_range(i->second.expiration);\r
+        for (; range.first != range.second; ++range.first) {\r
+            if (range.first->second == i->first) {\r
+                m_expMap.erase(range.first);\r
+                break;\r
+            }\r
+        }\r
+        // And finally delete the record itself.\r
+        m_dataMap.erase(i);\r
+        m_log.debug("deleted record (%s)", key);\r
+        return true;\r
+    }\r
+\r
+    m_log.debug("deleting record (%s)....not found", key);\r
+    return false;\r
+}\r
index 2f9a1d0..6474ba3 100644 (file)
@@ -269,7 +269,12 @@ void Thread::exit(void* return_val)
 {
     pthread_exit(return_val);
 }
-    
+
+void Thread::sleep(int seconds)
+{
+    sleep(seconds);
+}
+
 void Thread::mask_all_signals(void)
 {
     sigset_t sigmask;
diff --git a/xmltooling/util/StorageService.cpp b/xmltooling/util/StorageService.cpp
new file mode 100644 (file)
index 0000000..cefb07a
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ *  Copyright 2001-2006 Internet2
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * StorageService.cpp
+ * 
+ * Registration of factories for built-in storage services
+ */
+
+#include "internal.h"
+#include "util/StorageService.h"
+
+using namespace xmltooling;
+using namespace std;
+
+namespace xmltooling {
+    XMLTOOL_DLLLOCAL PluginManager<StorageService,const DOMElement*>::Factory MemoryStorageServiceFactory; 
+};
+
+void XMLTOOL_API xmltooling::registerStorageServices()
+{
+    XMLToolingConfig& conf=XMLToolingConfig::getConfig();
+    conf.StorageServiceManager.registerFactory(MEMORY_STORAGE_SERVICE, MemoryStorageServiceFactory);
+}
diff --git a/xmltooling/util/StorageService.h b/xmltooling/util/StorageService.h
new file mode 100644 (file)
index 0000000..905884b
--- /dev/null
@@ -0,0 +1,153 @@
+/*\r
+ *  Copyright 2001-2006 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * @file xmltooling/util/StorageService.h\r
+ * \r
+ * Generic data storage facility for use by services that require persistence.\r
+ */\r
+\r
+#ifndef __xmltooling_storage_h__\r
+#define __xmltooling_storage_h__\r
+\r
+#include <xmltooling/XMLObject.h>\r
+\r
+#include <ctime>\r
+\r
+namespace xmltooling {\r
+\r
+    /**\r
+     * Generic data storage facility for use by services that require persistence.\r
+     */\r
+    class XMLTOOL_API StorageService\r
+    {\r
+        MAKE_NONCOPYABLE(StorageService);\r
+    protected:\r
+        StorageService() {}\r
+        \r
+    public:\r
+        virtual ~StorageService() {}\r
+        \r
+        /**\r
+         * Creates a new "short" record in the storage service.\r
+         * \r
+         * @param key           null-terminated unique key of up to 255 bytes\r
+         * @param value         null-terminated value of up to 255 bytes to store\r
+         * @param expiration    an expiration timestamp, after which the record can be purged\r
+         * \r
+         * @throws IOException  raised if errors occur in the insertion process \r
+         */\r
+        virtual void createString(const char* key, const char* value, time_t expiration)=0;\r
+        \r
+        /**\r
+         * Returns an existing "short" record from the storage service.\r
+         * \r
+         * @param key           null-terminated unique key of up to 255 bytes\r
+         * @param value         location in which to return the record value\r
+         * @param modifiedSince the record should not be returned if unmodified since this time,\r
+         *  or 0 to always return\r
+         * @return  true iff the record exists and was returned (based on the modifiedSince value)   \r
+         * \r
+         * @throws IOException  raised if errors occur in the read process \r
+         */\r
+        virtual bool readString(const char* key, std::string& value, time_t modifiedSince=0)=0;\r
+\r
+        /**\r
+         * Updates an existing "short" record in the storage service.\r
+         * \r
+         * @param key           null-terminated unique key of up to 255 bytes\r
+         * @param value         null-terminated value of up to 255 bytes to store, or NULL to leave alone\r
+         * @param expiration    a new expiration timestamp, or 0 to leave alone\r
+         * @return true iff the record exists and was updated\r
+         *    \r
+         * @throws IOException  raised if errors occur in the update process \r
+         */\r
+        virtual bool updateString(const char* key, const char* value=NULL, time_t expiration=0)=0;\r
+        \r
+        /**\r
+         * Deletes an existing "short" record from the storage service.\r
+         * \r
+         * @param key           null-terminated unique key of up to 255 bytes\r
+         * @return true iff the record existed and was deleted\r
+         *    \r
+         * @throws IOException  raised if errors occur in the deletion process \r
+         */\r
+        virtual bool deleteString(const char* key)=0;\r
+        \r
+        /**\r
+         * Creates a new "long" record in the storage service.\r
+         * \r
+         * @param key           null-terminated unique key of up to 255 bytes\r
+         * @param value         null-terminated value of arbitrary length\r
+         * @param expiration    an expiration timestamp, after which the record can be purged\r
+         * \r
+         * @throws IOException  raised if errors occur in the insertion process \r
+         */\r
+        virtual void createText(const char* key, const char* value, time_t expiration)=0;\r
+        \r
+        /**\r
+         * Returns an existing "long" record from the storage service.\r
+         * \r
+         * @param key           null-terminated unique key of up to 255 bytes\r
+         * @param value         location in which to return the record value\r
+         * @param modifiedSince the record should not be returned if unmodified since this time,\r
+         *  or 0 to always return\r
+         * @return  true iff the record exists and was returned (based on the modifiedSince value)\r
+         *    \r
+         * @throws IOException  raised if errors occur in the read process \r
+         */\r
+        virtual bool readText(const char* key, std::string& value, time_t modifiedSince=0)=0;\r
+\r
+        /**\r
+         * Updates an existing "long" record in the storage service.\r
+         * \r
+         * @param key           null-terminated unique key of up to 255 bytes\r
+         * @param value         null-terminated value of arbitrary length to store, or NULL to leave alone\r
+         * @param expiration    a new expiration timestamp, or 0 to leave alone\r
+         * @return true iff the record exists and was updated\r
+         *    \r
+         * @throws IOException  raised if errors occur in the update process \r
+         */\r
+        virtual bool updateText(const char* key, const char* value=NULL, time_t expiration=0)=0;\r
+        \r
+        /**\r
+         * Deletes an existing "long" record from the storage service.\r
+         * \r
+         * @param key           null-terminated unique key of up to 255 bytes\r
+         * @return true iff the record existed and was deleted\r
+         *    \r
+         * @throws IOException  raised if errors occur in the deletion process \r
+         */\r
+        virtual bool deleteText(const char* key)=0;\r
+        \r
+        /**\r
+         * Manually trigger a cleanup of expired records.\r
+         * The method <strong>MAY</strong> return without guaranteeing that\r
+         * cleanup has already occurred.\r
+         */\r
+        virtual void reap()=0;\r
+    };\r
+\r
+    /**\r
+     * Registers StorageService classes into the runtime.\r
+     */\r
+    void XMLTOOL_API registerStorageServices();\r
+\r
+    /** StorageService based on in-memory caching. */\r
+    #define MEMORY_STORAGE_SERVICE  "org.opensaml.xmlooling.MemoryStorageService"\r
+};\r
+\r
+#endif /* __xmltooling_storage_h__ */\r
index 25147c6..50d031f 100644 (file)
@@ -78,7 +78,13 @@ namespace xmltooling
          * @param return_val    the return value for the thread
          */
         static void exit(void* return_val);
-        
+
+        /**
+         * Sleeps the current thread for the specified amount of time.
+         * 
+         * @param seconds   time to sleep
+         */
+        static void sleep(int secounds);        
 #ifndef WIN32
         /**
          * Masks all signals from a thread. 
index e239a17..8e5cdc0 100644 (file)
@@ -383,6 +383,11 @@ void Thread::exit(void* return_val)
     ExitThread((DWORD)return_val);\r
 }\r
 \r
+void Thread::sleep(int seconds)\r
+{\r
+    Sleep(seconds * 1000);\r
+}\r
+\r
 Mutex * Mutex::create()\r
 {\r
     return new MutexImpl();\r
index 8fdc20e..51cf2be 100644 (file)
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\util\StorageService.cpp"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\util\Win32Threads.cpp"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\impl\MemoryStorageService.cpp"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\impl\UnknownElement.cpp"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\util\StorageService.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\util\Threads.h"\r
                                        >\r
                                </File>\r
diff --git a/xmltoolingtest/MemoryStorageServiceTest.h b/xmltoolingtest/MemoryStorageServiceTest.h
new file mode 100644 (file)
index 0000000..1d91950
--- /dev/null
@@ -0,0 +1,47 @@
+/*\r
+ *  Copyright 2001-2006 Internet2\r
+ * \r
+ * Licensed under the Apache License, Version 2.0 (the "License");\r
+ * you may not use this file except in compliance with the License.\r
+ * You may obtain a copy of the License at\r
+ *\r
+ *     http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+#include "XMLObjectBaseTestCase.h"\r
+\r
+#include <xmltooling/util/StorageService.h>\r
+\r
+class MemoryStorageServiceTest : public CxxTest::TestSuite {\r
+public:\r
+    void setUp() {\r
+    }\r
+    \r
+    void tearDown() {\r
+    }\r
+\r
+    void testMemoryService() {\r
+        auto_ptr<StorageService> storage(\r
+            XMLToolingConfig::getConfig().StorageServiceManager.newPlugin(MEMORY_STORAGE_SERVICE,NULL)\r
+            );\r
+\r
+        string data;\r
+        TSM_ASSERT("Record found in storage.", !storage->readString("foo1", data));\r
+        storage->createString("foo1", "bar1", time(NULL) - 300);\r
+        storage->createString("foo2", "bar2", time(NULL));\r
+        TSM_ASSERT("Record not found in storage.", storage->readString("foo1", data));\r
+        TSM_ASSERT_EQUALS("Record value doesn't match.", data, "bar1");\r
+        TSM_ASSERT("Update failed.", storage->updateString("foo2", "bar1"));\r
+        TSM_ASSERT("Record not found in storage.", storage->readString("foo2", data));\r
+        TSM_ASSERT_EQUALS("Record value doesn't match.", data, "bar1");\r
+        TSM_ASSERT("Delete failed.", storage->deleteString("foo2"));\r
+        storage->reap();\r
+        Thread::sleep(1);\r
+    }\r
+};\r
index 8d365db..31feac6 100644 (file)
                                >\r
                        </File>\r
                        <File\r
+                               RelativePath=".\MemoryStorageServiceTest.cpp"\r
+                               >\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\SignatureTest.cpp"\r
                                >\r
                        </File>\r
                                        >\r
                                        <Tool\r
                                                Name="VCCustomBuildTool"\r
-                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
                                                Outputs="&quot;$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\r
                                        >\r
                                        <Tool\r
                                                Name="VCCustomBuildTool"\r
-                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;&#x0D;&#x0A;"\r
                                                Outputs="&quot;$(InputName)&quot;.cpp"\r
                                        />\r
                                </FileConfiguration>\r
                                </FileConfiguration>\r
                        </File>\r
                        <File\r
+                               RelativePath=".\MemoryStorageServiceTest.h"\r
+                               >\r
+                               <FileConfiguration\r
+                                       Name="Debug|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCustomBuildTool"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               Outputs="&quot;$(InputName)&quot;.cpp"\r
+                                       />\r
+                               </FileConfiguration>\r
+                               <FileConfiguration\r
+                                       Name="Release|Win32"\r
+                                       >\r
+                                       <Tool\r
+                                               Name="VCCustomBuildTool"\r
+                                               CommandLine="\perl\bin\perl.exe -w \cxxtest\cxxtestgen.pl --part --have-eh --have-std --abort-on-fail -o &quot;$(InputName)&quot;.cpp &quot;$(InputPath)&quot;"\r
+                                               Outputs="&quot;$(InputName)&quot;.cpp"\r
+                                       />\r
+                               </FileConfiguration>\r
+                       </File>\r
+                       <File\r
                                RelativePath=".\SignatureTest.h"\r
                                >\r
                                <FileConfiguration\r