From 5ce9990cca9b4aa1bad75daa4b39cb09195b2b46 Mon Sep 17 00:00:00 2001 From: cantor Date: Wed, 19 May 2010 18:30:32 +0000 Subject: [PATCH] Revisions to improve handling of backup files. git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/branches/REL_1@747 de75baf8-a10c-0410-a50a-987c0e22f00f --- xmltooling/util/ReloadableXMLFile.cpp | 121 +++++++++++++++++++--------------- xmltooling/util/ReloadableXMLFile.h | 43 ++++++++---- 2 files changed, 101 insertions(+), 63 deletions(-) diff --git a/xmltooling/util/ReloadableXMLFile.cpp b/xmltooling/util/ReloadableXMLFile.cpp index e51d0ee..5aacc9a 100644 --- a/xmltooling/util/ReloadableXMLFile.cpp +++ b/xmltooling/util/ReloadableXMLFile.cpp @@ -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 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 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 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 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 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 ReloadableXMLFile::load() -{ - return load(false); -} - -pair 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(); -} diff --git a/xmltooling/util/ReloadableXMLFile.h b/xmltooling/util/ReloadableXMLFile.h index 795bd69..4301896 100644 --- a/xmltooling/util/ReloadableXMLFile.h +++ b/xmltooling/util/ReloadableXMLFile.h @@ -66,7 +66,7 @@ namespace xmltooling { *
use a validating parser
*
reloadChanges
*
enables monitoring of local file for changes
- *
reloadInterval
+ *
reloadInterval or maxRefreshDelay
*
enables periodic refresh of remote file
*
backingFilePath
*
location for backup of remote resource
@@ -83,10 +83,11 @@ namespace xmltooling { *
requires XML be signed with an enveloped signature verifiable with specified TrustEngine
* * - * @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 background_load(); /** + * @deprecated * Basic load/parse of configuration material. * *

The base version performs basic parsing duties and returns the result. @@ -122,6 +124,23 @@ namespace xmltooling { virtual std::pair load(); /** + * Basic load/parse of configuration material. + * + *

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. + * + *

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 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 load(bool backup); #ifndef XMLTOOLING_LITE void validateSignature(xmlsignature::Signature& sigObj) const; #endif -- 2.1.4