Revisions to improve handling of backup files.
authorcantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Wed, 19 May 2010 18:30:32 +0000 (18:30 +0000)
committercantor <cantor@de75baf8-a10c-0410-a50a-987c0e22f00f>
Wed, 19 May 2010 18:30:32 +0000 (18:30 +0000)
git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/branches/REL_1@747 de75baf8-a10c-0410-a50a-987c0e22f00f

xmltooling/util/ReloadableXMLFile.cpp
xmltooling/util/ReloadableXMLFile.h

index e51d0ee..5aacc9a 100644 (file)
@@ -92,6 +92,7 @@ static const XMLCh filename[] =         UNICODE_LITERAL_8(f,i,l,e,n,a,m,e);
 static const XMLCh validate[] =         UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);
 static const XMLCh reloadChanges[] =    UNICODE_LITERAL_13(r,e,l,o,a,d,C,h,a,n,g,e,s);
 static const XMLCh reloadInterval[] =   UNICODE_LITERAL_14(r,e,l,o,a,d,I,n,t,e,r,v,a,l);
+static const XMLCh maxRefreshDelay[] =  UNICODE_LITERAL_15(m,a,x,R,e,f,r,e,s,h,D,e,l,a,y);
 static const XMLCh backingFilePath[] =  UNICODE_LITERAL_15(b,a,c,k,i,n,g,F,i,l,e,P,a,t,h);
 static const XMLCh type[] =             UNICODE_LITERAL_4(t,y,p,e);
 static const XMLCh certificate[] =      UNICODE_LITERAL_11(c,e,r,t,i,f,i,c,a,t,e);
@@ -100,12 +101,13 @@ static const XMLCh _TrustEngine[] =     UNICODE_LITERAL_11(T,r,u,s,t,E,n,g,i,n,e
 static const XMLCh _CredentialResolver[] = UNICODE_LITERAL_18(C,r,e,d,e,n,t,i,a,l,R,e,s,o,l,v,e,r);
 
 
-ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e, Category& log)
-    : m_root(e), m_local(true), m_validate(false), m_backupIndicator(true), m_filestamp(0), m_reloadInterval(0), m_lock(nullptr), m_log(log),
+ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e, Category& log, bool startReloadThread)
+    : m_root(e), m_local(true), m_validate(false), m_filestamp(0), m_reloadInterval(0),
+      m_lock(nullptr), m_loaded(false), m_log(log),
 #ifndef XMLTOOLING_LITE
-        m_credResolver(nullptr), m_trust(nullptr),
+      m_credResolver(nullptr), m_trust(nullptr),
 #endif
-        m_shutdown(false), m_reload_wait(nullptr), m_reload_thread(nullptr)
+      m_shutdown(false), m_reload_wait(nullptr), m_reload_thread(nullptr)
 {
 #ifdef _DEBUG
     NDC ndc("ReloadableXMLFile");
@@ -208,6 +210,8 @@ ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e, Category& log)
                 log.debug("backup remote resource to (%s)", m_backing.c_str());
             }
             source = e->getAttributeNS(nullptr,reloadInterval);
+            if (!source || !*source)
+                source = e->getAttributeNS(nullptr,maxRefreshDelay);
             if (source && *source) {
                 m_reloadInterval = XMLString::parseInt(source);
                 if (m_reloadInterval > 0) {
@@ -218,10 +222,8 @@ ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e, Category& log)
             m_filestamp = time(nullptr);   // assume it gets loaded initially
         }
 
-        if (m_lock) {
-            m_reload_wait = CondWait::create();
-            m_reload_thread = Thread::create(&reload_fn, this);
-        }
+        if (startReloadThread)
+            startup();
     }
     else {
         log.debug("no resource uri/path/name supplied, will load inline configuration");
@@ -240,6 +242,14 @@ ReloadableXMLFile::~ReloadableXMLFile()
     delete m_lock;
 }
 
+void ReloadableXMLFile::startup()
+{
+    if (m_lock && !m_reload_thread) {
+        m_reload_wait = CondWait::create();
+        m_reload_thread = Thread::create(&reload_fn, this);
+    }
+}
+
 void ReloadableXMLFile::shutdown()
 {
     if (m_reload_thread) {
@@ -441,27 +451,6 @@ pair<bool,DOMElement*> ReloadableXMLFile::load(bool backup)
 
             }
 #endif
-
-            if (!backup && !m_backing.empty()) {
-                // If the indicator is true, we're responsible for the backup.
-                if (m_backupIndicator) {
-                    m_log.debug("backing up remote resource to (%s)", m_backing.c_str());
-                    try {
-                        Locker locker(getBackupLock());
-                        ofstream backer(m_backing.c_str());
-                        backer << *doc;
-                    }
-                    catch (exception& ex) {
-                        m_log.crit("exception while backing up resource: %s", ex.what());
-                    }
-                }
-                else {
-                    // If the indicator was false, set true to signal that a backup is needed.
-                    // The caller will presumably flip it back to false once that's done.
-                    m_backupIndicator = true;
-                }
-            }
-
             return make_pair(true, doc->getDocumentElement());
         }
     }
@@ -469,19 +458,67 @@ pair<bool,DOMElement*> ReloadableXMLFile::load(bool backup)
         auto_ptr_char msg(e.getMessage());
         m_log.errorStream() << "Xerces error while loading resource (" << (backup ? m_backing : m_source) << "): "
             << msg.get() << logging::eol;
-        if (!backup && !m_backing.empty())
-            return load(true);
         throw XMLParserException(msg.get());
     }
     catch (exception& e) {
         m_log.errorStream() << "error while loading resource ("
             << (m_source.empty() ? "inline" : (backup ? m_backing : m_source)) << "): " << e.what() << logging::eol;
-        if (!backup && !m_backing.empty())
+        throw;
+    }
+}
+
+pair<bool,DOMElement*> ReloadableXMLFile::load()
+{
+    // If this method is used, we're responsible for managing failover to a
+    // backup of a remote resource (if available), and for backing up remote
+    // resources.
+    try {
+        pair<bool,DOMElement*> ret = load(false);
+        if (!m_backing.empty()) {
+            m_log.debug("backing up remote resource to (%s)", m_backing.c_str());
+            try {
+                Locker locker(getBackupLock());
+                ofstream backer(m_backing.c_str());
+                backer << *(ret.second->getOwnerDocument());
+            }
+            catch (exception& ex) {
+                m_log.crit("exception while backing up resource: %s", ex.what());
+            }
+        }
+        return ret;
+    }
+    catch (long&) {
+        // If there's an HTTP error or the document hasn't changed,
+        // use the backup iff we have no "valid" resource in place.
+        // That prevents reload of the backup copy any time the document
+        // hasn't changed.
+        if (!m_loaded && !m_backing.empty())
+            return load(true);
+        throw;
+    }
+    catch (exception&) {
+        // Same as above, but for general load/parse errors.
+        if (!m_loaded && !m_backing.empty())
             return load(true);
         throw;
     }
 }
 
+pair<bool,DOMElement*> ReloadableXMLFile::background_load()
+{
+    // If this method isn't overridden, we acquire a write lock
+    // and just call the old override.
+    if (m_lock)
+        m_lock->wrlock();
+    SharedLock locker(m_lock, false);
+    return load();
+}
+
+Lockable* ReloadableXMLFile::getBackupLock()
+{
+    return &XMLToolingConfig::getConfig();
+}
+
 #ifndef XMLTOOLING_LITE
 
 void ReloadableXMLFile::validateSignature(Signature& sigObj) const
@@ -556,23 +593,3 @@ void ReloadableXMLFile::validateSignature(Signature& sigObj) const
 }
 
 #endif
-
-pair<bool,DOMElement*> ReloadableXMLFile::load()
-{
-    return load(false);
-}
-
-pair<bool,DOMElement*> ReloadableXMLFile::background_load()
-{
-    // If this method isn't overridden, we acquire a write lock
-    // and just call the old override.
-    if (m_lock)
-        m_lock->wrlock();
-    SharedLock locker(m_lock, false);
-    return load();
-}
-
-Lockable* ReloadableXMLFile::getBackupLock()
-{
-    return &XMLToolingConfig::getConfig();
-}
index 795bd69..4301896 100644 (file)
@@ -66,7 +66,7 @@ namespace xmltooling {
          *  <dd>use a validating parser</dd>
          *  <dt>reloadChanges</dt>
          *  <dd>enables monitoring of local file for changes</dd>
-         *  <dt>reloadInterval</dt>
+         *  <dt>reloadInterval or maxRefreshDelay</dt>
          *  <dd>enables periodic refresh of remote file</dd>
          *  <dt>backingFilePath</dt>
          *  <dd>location for backup of remote resource</dd>
@@ -83,10 +83,11 @@ namespace xmltooling {
          *  <dd>requires XML be signed with an enveloped signature verifiable with specified TrustEngine</dd>
          * </dl>
          * 
-         * @param e     DOM to supply configuration
-         * @param log   logging object to use
+         * @param e                 DOM to supply configuration
+         * @param log               logging object to use
+         * @param startReloadThread true iff refresh thread for resources should be started by constructor
          */
-        ReloadableXMLFile(const xercesc::DOMElement* e, logging::Category& log);
+        ReloadableXMLFile(const xercesc::DOMElement* e, logging::Category& log, bool startReloadThread=true);
     
         virtual ~ReloadableXMLFile();
 
@@ -107,6 +108,7 @@ namespace xmltooling {
         virtual std::pair<bool,xercesc::DOMElement*> background_load();
 
         /**
+         * @deprecated
          * Basic load/parse of configuration material.
          * 
          * <p>The base version performs basic parsing duties and returns the result.
@@ -122,6 +124,23 @@ namespace xmltooling {
         virtual std::pair<bool,xercesc::DOMElement*> load();
 
         /**
+         * Basic load/parse of configuration material.
+         *
+         * <p>The base version performs basic parsing duties and returns the result.
+         * Subclasses should override the new background_load() method and perform
+         * their own locking in conjunction with use of this method.
+         *
+         * <p>This version allows subclasses to explicitly control the use of a
+         * backup for remote resources, which allows additional validation to be
+         * performed besides just successful XML parsing.
+         *
+         * @param backup    true iff the backup source should be loaded
+         * @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,xercesc::DOMElement*> load(bool backup);
+
+        /**
          * Accesses a lock interface protecting use of backup file associated with the
          * object.
          *
@@ -132,6 +151,12 @@ namespace xmltooling {
         virtual Lockable* getBackupLock();
 
         /**
+         * Starts up reload thread, can be automatically called by constructor, or
+         * manually invoked by subclass.
+         */
+        void startup();
+
+        /**
          * Shuts down reload thread, should be called from subclass destructor.
          */
         void shutdown();
@@ -151,12 +176,6 @@ namespace xmltooling {
         /** Path to backup copy for remote resource. */
         std::string m_backing;
 
-        /**
-         * Before load, indicates whether the backup is handled by the base class,
-         * after load, will be true iff it started false and a backup needs to be done.
-         */
-        bool m_backupIndicator;
-
         /** Last modification of local resource. */
         time_t m_filestamp;
 
@@ -175,6 +194,9 @@ namespace xmltooling {
         /** Plugin identifier. */
         std::string m_id;
 
+        /** Indicates whether a usable version of the resource is in place. */
+        bool m_loaded;
+
 #ifndef XMLTOOLING_LITE
         /** CredentialResolver for signature verification. */
         CredentialResolver* m_credResolver;
@@ -191,7 +213,6 @@ namespace xmltooling {
         void unlock();
 
     private:
-        std::pair<bool,xercesc::DOMElement*> load(bool backup);
 #ifndef XMLTOOLING_LITE
         void validateSignature(xmlsignature::Signature& sigObj) const;
 #endif