Base class for reloadable local and remote configurations.
authorcantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Wed, 3 Jan 2007 21:52:04 +0000 (21:52 +0000)
committercantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Wed, 3 Jan 2007 21:52:04 +0000 (21:52 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/trunk@228 de75baf8-a10c-0410-a50a-987c0e22f00f

.project
xmltooling/Makefile.am
xmltooling/util/ReloadableXMLFile.cpp [new file with mode: 0644]
xmltooling/util/ReloadableXMLFile.h [new file with mode: 0644]
xmltooling/xmltooling.vcproj

index 40ff0eb..b3666fd 100644 (file)
--- a/.project
+++ b/.project
                        <triggers>clean,full,incremental,</triggers>\r
                        <arguments>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.build.arguments</key>\r
-                                       <value></value>\r
-                               </dictionary>\r
-                               <dictionary>\r
-                                       <key>org.eclipse.cdt.core.errorOutputParser</key>\r
-                                       <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
+                                       <key>org.eclipse.cdt.make.core.enableCleanBuild</key>\r
+                                       <value>true</value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.environment</key>\r
-                                       <value></value>\r
+                                       <key>org.eclipse.cdt.make.core.append_environment</key>\r
+                                       <value>true</value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.enableAutoBuild</key>\r
+                                       <key>org.eclipse.cdt.make.core.stopOnError</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.enableFullBuild</key>\r
-                                       <value>true</value>\r
-                               </dictionary>\r
-                               <dictionary>\r
                                        <key>org.eclipse.cdt.make.core.enabledIncrementalBuild</key>\r
                                        <value>true</value>\r
                                </dictionary>\r
                                        <value>make</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.build.target.inc</key>\r
+                                       <value>all</value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.enableCleanBuild</key>\r
-                                       <value>true</value>\r
+                                       <key>org.eclipse.cdt.make.core.build.target.full</key>\r
+                                       <value>clean all</value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.append_environment</key>\r
+                                       <key>org.eclipse.cdt.make.core.build.arguments</key>\r
+                                       <value></value>\r
+                               </dictionary>\r
+                               <dictionary>\r
+                                       <key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>\r
                                        <value>true</value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.build.target.full</key>\r
-                                       <value>clean all</value>\r
+                                       <key>org.eclipse.cdt.make.core.environment</key>\r
+                                       <value></value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.useDefaultBuildCmd</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.enableAutoBuild</key>\r
+                                       <value>false</value>\r
+                               </dictionary>\r
+                               <dictionary>\r
                                        <key>org.eclipse.cdt.make.core.build.target.auto</key>\r
                                        <value>all</value>\r
                                </dictionary>\r
                                <dictionary>\r
-                                       <key>org.eclipse.cdt.make.core.stopOnError</key>\r
-                                       <value>false</value>\r
+                                       <key>org.eclipse.cdt.make.core.build.target.clean</key>\r
+                                       <value>clean</value>\r
+                               </dictionary>\r
+                               <dictionary>\r
+                                       <key>org.eclipse.cdt.core.errorOutputParser</key>\r
+                                       <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
                        </arguments>\r
                </buildCommand>\r
index ee9038c..b4a5c80 100644 (file)
@@ -87,6 +87,7 @@ utilinclude_HEADERS = \
        util/DateTime.h \
        util/NDC.h \
        util/ParserPool.h \
+       util/ReloadableXMLFile.h \
        util/ReplayCache.h \
        util/StorageService.h \
        util/TemplateEngine.h \
@@ -156,6 +157,7 @@ libxmltooling_la_SOURCES = \
        util/DateTime.cpp \
        util/NDC.cpp \
        util/ParserPool.cpp \
+       util/ReloadableXMLFile.cpp \
        util/ReplayCache.cpp \
        util/StorageService.cpp \
        util/TemplateEngine.cpp \
diff --git a/xmltooling/util/ReloadableXMLFile.cpp b/xmltooling/util/ReloadableXMLFile.cpp
new file mode 100644 (file)
index 0000000..3aab81a
--- /dev/null
@@ -0,0 +1,233 @@
+/*\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
+ * @file ReloadableXMLFile.cpp\r
+ * \r
+ * Base class for file-based XML configuration.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "util/NDC.h"\r
+#include "util/ReloadableXMLFile.h"\r
+#include "util/XMLConstants.h"\r
+#include "util/XMLHelper.h"\r
+\r
+#include <sys/types.h>\r
+#include <sys/stat.h>\r
+\r
+#include <log4cpp/Category.hh>\r
+#include <xercesc/framework/LocalFileInputSource.hpp>\r
+#include <xercesc/framework/Wrapper4InputSource.hpp>\r
+#include <xercesc/framework/URLInputSource.hpp>\r
+#include <xercesc/util/XMLUniDefs.hpp>\r
+\r
+using namespace xmltooling;\r
+using namespace log4cpp;\r
+using namespace std;\r
+\r
+static const XMLCh uri[] =          UNICODE_LITERAL_3(u,r,i);\r
+static const XMLCh url[] =          UNICODE_LITERAL_3(u,r,l);\r
+static const XMLCh path[] =         UNICODE_LITERAL_4(p,a,t,h);\r
+static const XMLCh pathname[] =     UNICODE_LITERAL_8(p,a,t,h,n,a,m,e);\r
+static const XMLCh file[] =         UNICODE_LITERAL_4(f,i,l,e);\r
+static const XMLCh filename[] =     UNICODE_LITERAL_8(f,i,l,e,n,a,m,e);\r
+static const XMLCh validate[] =     UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);\r
+static const XMLCh reloadChanges[] =UNICODE_LITERAL_13(r,e,l,o,a,d,C,h,a,n,g,e,s);\r
+\r
+ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e)\r
+    : m_root(e), m_local(true), m_validate(false), m_filestamp(0), m_lock(NULL)\r
+{\r
+#ifdef _DEBUG\r
+    NDC ndc("ReloadableXMLFile");\r
+#endif\r
+    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".ReloadableXMLFile");\r
+\r
+    // Establish source of data...\r
+    const XMLCh* source=e->getAttributeNS(NULL,uri);\r
+    if (!source || !*source) {\r
+        source=e->getAttributeNS(NULL,url);\r
+        if (!source || !*source) {\r
+            source=e->getAttributeNS(NULL,path);\r
+            if (!source || !*source) {\r
+                source=e->getAttributeNS(NULL,pathname);\r
+                if (!source || !*source) {\r
+                    source=e->getAttributeNS(NULL,file);\r
+                    if (!source || !*source) {\r
+                        source=e->getAttributeNS(NULL,filename);\r
+                    }\r
+                }\r
+            }\r
+        }\r
+        else\r
+            m_local=false;\r
+    }\r
+    else\r
+        m_local=false;\r
+    \r
+    if (source && *source) {\r
+        const XMLCh* flag=e->getAttributeNS(NULL,validate);\r
+        m_validate=(XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE));\r
+\r
+        auto_ptr_char temp(source);\r
+        m_source=temp.get();\r
+        \r
+        if (!m_local && !strstr(m_source.c_str(),"://")) {\r
+            log.warn("deprecated usage of uri/url attribute for a local resource, use path instead");\r
+            m_local=true;\r
+        }\r
+\r
+        flag=e->getAttributeNS(NULL,reloadChanges);\r
+        if (!flag || XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE)) {\r
+            if (m_local) {\r
+#ifdef WIN32\r
+                struct _stat stat_buf;\r
+                if (_stat(m_source.c_str(), &stat_buf) == 0)\r
+                    m_filestamp=stat_buf.st_mtime;\r
+                else\r
+                    m_local=false;\r
+#else\r
+                struct stat stat_buf;\r
+                if (stat(m_source.c_str(), &stat_buf) == 0)\r
+                    m_filestamp=stat_buf.st_mtime;\r
+                else\r
+                    m_local=false;\r
+#endif\r
+            }\r
+            m_lock=RWLock::create();\r
+        }\r
+\r
+        log.debug("using external resource (%s), will %smonitor for changes", m_source.c_str(), m_lock ? "" : "not ");\r
+    }\r
+    else\r
+        log.debug("no resource uri/path/name supplied, will load inline configuration");\r
+}\r
+\r
+pair<bool,DOMElement*> ReloadableXMLFile::load()\r
+{\r
+#ifdef _DEBUG\r
+    NDC ndc("init");\r
+#endif\r
+    Category& log=Category::getInstance(XMLTOOLING_LOGCAT".ReloadableXMLFile");\r
+\r
+    try {\r
+        if (m_source.empty()) {\r
+            // Data comes from the DOM we were handed.\r
+            log.debug("loading inline configuration...");\r
+            return make_pair(false,XMLHelper::getFirstChildElement(m_root));\r
+        }\r
+        else {\r
+            // Data comes from a file we have to parse.\r
+            log.debug("loading configuration from external resource...");\r
+\r
+            DOMDocument* doc=NULL;\r
+            auto_ptr_XMLCh widenit(m_source.c_str());\r
+            if (m_local) {\r
+                LocalFileInputSource src(widenit.get());\r
+                Wrapper4InputSource dsrc(&src,false);\r
+                if (m_validate)\r
+                    doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);\r
+                else\r
+                    doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);\r
+            }\r
+            else {\r
+                URLInputSource src(widenit.get());\r
+                Wrapper4InputSource dsrc(&src,false);\r
+                if (m_validate)\r
+                    doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);\r
+                else\r
+                    doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);\r
+            }\r
+\r
+            log.infoStream() << "loaded XML resource (" << m_source << ")" << CategoryStream::ENDLINE;\r
+            return make_pair(true,doc->getDocumentElement());\r
+        }\r
+    }\r
+    catch (XMLException& e) {\r
+        auto_ptr_char msg(e.getMessage());\r
+        log.errorStream() << "Xerces error while loading resource (" << m_source << "): "\r
+            << msg.get() << CategoryStream::ENDLINE;\r
+        throw XMLParserException(msg.get());\r
+    }\r
+    catch (XMLToolingException& e) {\r
+        log.errorStream() << "error while loading configuration from ("\r
+            << (m_source.empty() ? "inline" : m_source) << "): " << e.what() << CategoryStream::ENDLINE;\r
+        throw;\r
+    }\r
+}\r
+\r
+Lockable* ReloadableXMLFile::lock()\r
+{\r
+    if (!m_lock)\r
+        return this;\r
+        \r
+    m_lock->rdlock();\r
+\r
+    // Check if we need to refresh.\r
+    if (m_local) {\r
+#ifdef WIN32\r
+        struct _stat stat_buf;\r
+        if (_stat(m_source.c_str(), &stat_buf) != 0)\r
+            return this;\r
+#else\r
+        struct stat stat_buf;\r
+        if (stat(m_source.c_str(), &stat_buf) != 0)\r
+            return this;\r
+#endif\r
+        if (m_filestamp>=stat_buf.st_mtime)\r
+            return this;\r
+        \r
+        // Elevate lock and recheck.\r
+        m_lock->unlock();\r
+        m_lock->wrlock();\r
+        if (m_filestamp>=stat_buf.st_mtime) {\r
+            // Somebody else handled it, just downgrade.\r
+            m_lock->unlock();\r
+            m_lock->rdlock();\r
+            return this;\r
+        }\r
+\r
+        // Update the timestamp regardless. No point in repeatedly trying.\r
+        m_filestamp=stat_buf.st_mtime;\r
+    }\r
+    else {\r
+        if (isValid())\r
+            return this;\r
+\r
+        // Elevate lock and recheck.\r
+        m_lock->unlock();\r
+        m_lock->wrlock();\r
+        if (isValid()) {\r
+            // Somebody else handled it, just downgrade.\r
+            m_lock->unlock();\r
+            m_lock->rdlock();\r
+            return this;\r
+        }\r
+    }\r
+    \r
+    // Do this once...\r
+    do {\r
+        // At this point we're holding the write lock, so make sure we pop it.\r
+        SharedLock lockwrap(m_lock,false);\r
+        pair<bool,DOMElement*> ret=load();\r
+        if (ret.first)\r
+            ret.second->getOwnerDocument()->release();\r
+    } while(0);\r
+    \r
+    // If we made it here, the swap may or may not have worked, but we need to relock.\r
+    m_lock->rdlock();\r
+    return this;\r
+}\r
diff --git a/xmltooling/util/ReloadableXMLFile.h b/xmltooling/util/ReloadableXMLFile.h
new file mode 100644 (file)
index 0000000..019f9b2
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ *  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.
+ */
+
+/**
+ * @file xmltooling/util/ReloadableXMLFile.h
+ * 
+ * Base class for file-based XML configuration.
+ */
+
+#ifndef __xmltooling_reloadable_h__
+#define __xmltooling_reloadable_h__
+
+#include <xmltooling/Lockable.h>
+#include <xmltooling/util/Threads.h>
+
+#include <ctime>
+#include <string>
+
+namespace xmltooling {
+
+    /**
+     * Base class for file-based XML configuration.
+     */
+    class XMLTOOL_API ReloadableXMLFile : protected virtual Lockable
+    {
+        MAKE_NONCOPYABLE(ReloadableXMLFile);
+        
+    protected:
+        /**
+         * Constructor taking a DOM element supporting the following content:
+         * 
+         * <dl>
+         *  <dt>file | filename | path | pathname</dt>
+         *  <dd>identifies a local file</dd>
+         *  <dt>uri | url</dt>
+         *  <dd>identifies a remote resource</dd>
+         *  <dt>validate</dt>
+         *  <dd>use a validating parser</dd>
+         *  <dt>reloadChanges</dt>
+         *  <dd>enables monitoring of resources for changes</dd>
+         * </dl>
+         * 
+         * @param e DOM to supply configuration
+         */
+        ReloadableXMLFile(const DOMElement* e);
+    
+        virtual ~ReloadableXMLFile() {
+            delete m_lock;
+        }
+
+    public:
+        Lockable* lock();
+
+        void unlock() {
+            if (m_lock)
+                m_lock->unlock();
+        }
+
+        /**
+         * Loads configuration material.
+         * 
+         * <p>This method is called to load configuration material
+         * initially and any time a change is detected. The base version
+         * performs basic parsing duties and returns the result.
+         * 
+         * @return a pair consisting of a flag indicating whether to take ownership of
+         *      the document, and the root element of the tree to load
+         */
+        virtual std::pair<bool,DOMElement*> load();
+        
+        /**
+         * Overrideable method to determine whether a remote resource remains valid.
+         * 
+         * @return  true iff the resource remains valid and should not be reloaded
+         */
+        virtual bool isValid() const {
+            return true;
+        }
+
+    private:
+        const DOMElement* m_root;
+        bool m_local, m_validate;
+        std::string m_source;
+        time_t m_filestamp;
+        RWLock* m_lock;
+    };
+
+};
+
+#endif /* __xmltooling_reloadable_h__ */
index 3efe188..f92fdb0 100644 (file)
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\util\ReloadableXMLFile.cpp"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\util\ReplayCache.cpp"\r
                                        >\r
                                </File>\r
                                        >\r
                                </File>\r
                                <File\r
+                                       RelativePath=".\util\ReloadableXMLFile.h"\r
+                                       >\r
+                               </File>\r
+                               <File\r
                                        RelativePath=".\util\ReplayCache.h"\r
                                        >\r
                                </File>\r