From: cantor Date: Tue, 30 Mar 2010 03:11:59 +0000 (+0000) Subject: https://bugs.internet2.edu/jira/browse/CPPXT-54 X-Git-Tag: 1.4.1~113 X-Git-Url: http://www.project-moonshot.org/gitweb/?p=shibboleth%2Fxmltooling.git;a=commitdiff_plain;h=27bb9fc95b4170c09ae05d90be591cc4b82fb9d9 https://bugs.internet2.edu/jira/browse/CPPXT-54 git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/branches/REL_1@725 de75baf8-a10c-0410-a50a-987c0e22f00f --- diff --git a/xmltooling/util/ReloadableXMLFile.cpp b/xmltooling/util/ReloadableXMLFile.cpp index db20c57..ca8ad07 100644 --- a/xmltooling/util/ReloadableXMLFile.cpp +++ b/xmltooling/util/ReloadableXMLFile.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2001-2009 Internet2 + * Copyright 2001-2010 Internet2 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,12 @@ #include "util/XMLConstants.h" #include "util/XMLHelper.h" +#if defined(XMLTOOLING_LOG4SHIB) +# include +#elif defined(XMLTOOLING_LOG4CPP) +# include +#endif + #include #include #include @@ -42,6 +48,7 @@ using namespace xmltooling; using namespace xercesc; using namespace std; +static const XMLCh id[] = UNICODE_LITERAL_2(i,d); static const XMLCh uri[] = UNICODE_LITERAL_3(u,r,i); static const XMLCh url[] = UNICODE_LITERAL_3(u,r,l); static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h); @@ -54,13 +61,9 @@ static const XMLCh reloadInterval[] = UNICODE_LITERAL_14(r,e,l,o,a,d,I,n,t,e,r static const XMLCh backingFilePath[] = UNICODE_LITERAL_15(b,a,c,k,i,n,g,F,i,l,e,P,a,t,h); -ReloadableXMLFile::~ReloadableXMLFile() -{ - delete m_lock; -} - ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e, Category& log) - : m_root(e), m_local(true), m_validate(false), m_filestamp(0), m_reloadInterval(0), m_lock(NULL), m_log(log) + : m_root(e), m_local(true), m_validate(false), m_filestamp(0), m_reloadInterval(0), m_lock(NULL), m_log(log), + m_shutdown(false), m_reload_wait(NULL), m_reload_thread(NULL) { #ifdef _DEBUG NDC ndc("ReloadableXMLFile"); @@ -138,23 +141,162 @@ ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e, Category& log) } m_filestamp = time(NULL); // assume it gets loaded initially } + + if (m_lock) { + m_reload_wait = CondWait::create(); + m_reload_thread = Thread::create(&reload_fn, this); + } } else { log.debug("no resource uri/path/name supplied, will load inline configuration"); } + + source = e->getAttributeNS(NULL, id); + if (source && *source) { + auto_ptr_char tempid(source); + m_id = tempid.get(); + } +} + +ReloadableXMLFile::~ReloadableXMLFile() +{ + if (m_reload_thread) { + // Shut down the reload thread and let it know. + m_shutdown = true; + m_reload_wait->signal(); + m_reload_thread->join(NULL); + delete m_reload_thread; + delete m_reload_wait; + } + + delete m_lock; +} + +void* ReloadableXMLFile::reload_fn(void* pv) +{ + ReloadableXMLFile* r = reinterpret_cast(pv); + +#ifndef WIN32 + // First, let's block all signals + Thread::mask_all_signals(); +#endif + + if (!r->m_id.empty()) { + string threadid("["); + threadid += r->m_id + ']'; + logging::NDC::push(threadid); + } + +#ifdef _DEBUG + NDC ndc("reload"); +#endif + + auto_ptr mutex(Mutex::create()); + mutex->lock(); + + if (r->m_local) + r->m_log.info("reload thread started...running when signaled"); + else + r->m_log.info("reload thread started...running every %d seconds", r->m_reloadInterval); + + while (!r->m_shutdown) { + if (r->m_local) + r->m_reload_wait->wait(mutex.get()); + else + r->m_reload_wait->timedwait(mutex.get(), r->m_reloadInterval); + if (r->m_shutdown) + break; + + try { + r->m_log.info("reloading %s resource...", r->m_local ? "local" : "remote"); + pair ret = r->background_load(); + if (ret.first) + ret.second->getOwnerDocument()->release(); + } + catch (long& ex) { + if (ex == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED) { + r->m_log.info("remote resource (%s) unchanged from cached version", r->m_source.c_str()); + } + else { + // Shouldn't happen, we should only get codes intended to be gracefully handled. + r->m_log.crit("maintaining existing configuration, remote resource fetch returned atypical status code (%d)", ex); + } + } + catch (exception& ex) { + r->m_log.crit("maintaining existing configuration, error reloading resource (%s): %s", r->m_source.c_str(), ex.what()); + } + } + + r->m_log.info("reload thread finished"); + + mutex->unlock(); + + if (!r->m_id.empty()) { + logging::NDC::pop(); + } + + return NULL; +} + +Lockable* ReloadableXMLFile::lock() +{ + if (!m_lock) + return this; + + m_lock->rdlock(); + + if (m_local) { + // Check if we need to refresh. +#ifdef WIN32 + struct _stat stat_buf; + if (_stat(m_source.c_str(), &stat_buf) != 0) + return this; +#else + struct stat stat_buf; + if (stat(m_source.c_str(), &stat_buf) != 0) + return this; +#endif + if (m_filestamp >= stat_buf.st_mtime) + return this; + + // Elevate lock and recheck. + m_log.debug("timestamp of local resource changed, elevating to a write lock"); + m_lock->unlock(); + m_lock->wrlock(); + if (m_filestamp >= stat_buf.st_mtime) { + // Somebody else handled it, just downgrade. + m_log.debug("update of local resource handled by another thread, downgrading lock"); + m_lock->unlock(); + m_lock->rdlock(); + return this; + } + + // Update the timestamp regardless. + m_filestamp = stat_buf.st_mtime; + m_log.info("change detected, signaling reload thread..."); + m_reload_wait->signal(); + } + + return this; +} + +void ReloadableXMLFile::unlock() +{ + if (m_lock) + m_lock->unlock(); } pair ReloadableXMLFile::load(bool backup) { #ifdef _DEBUG - NDC ndc("init"); + NDC ndc("load"); #endif try { if (m_source.empty()) { // Data comes from the DOM we were handed. m_log.debug("loading inline configuration..."); - return make_pair(false,XMLHelper::getFirstChildElement(m_root)); + return make_pair(false, XMLHelper::getFirstChildElement(m_root)); } else { // Data comes from a file we have to parse. @@ -167,7 +309,7 @@ pair ReloadableXMLFile::load(bool backup) if (m_local || backup) { auto_ptr_XMLCh widenit(backup ? m_backing.c_str() : m_source.c_str()); LocalFileInputSource src(widenit.get()); - Wrapper4InputSource dsrc(&src,false); + Wrapper4InputSource dsrc(&src, false); if (m_validate) doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc); else @@ -175,7 +317,7 @@ pair ReloadableXMLFile::load(bool backup) } else { URLInputSource src(m_root, NULL, &m_cacheTag); - Wrapper4InputSource dsrc(&src,false); + Wrapper4InputSource dsrc(&src, false); if (m_validate) doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc); else @@ -208,7 +350,7 @@ pair ReloadableXMLFile::load(bool backup) } } - return make_pair(true,doc->getDocumentElement()); + return make_pair(true, doc->getDocumentElement()); } } catch (XMLException& e) { @@ -228,101 +370,16 @@ pair ReloadableXMLFile::load(bool backup) } } -Lockable* ReloadableXMLFile::lock() -{ - if (!m_lock) - return this; - - m_lock->rdlock(); - - // Check if we need to refresh. - if (m_local) { -#ifdef WIN32 - struct _stat stat_buf; - if (_stat(m_source.c_str(), &stat_buf) != 0) - return this; -#else - struct stat stat_buf; - if (stat(m_source.c_str(), &stat_buf) != 0) - return this; -#endif - if (m_filestamp>=stat_buf.st_mtime) - return this; - - // Elevate lock and recheck. - m_log.debug("timestamp of local resource changed, elevating to a write lock"); - m_lock->unlock(); - m_lock->wrlock(); - if (m_filestamp>=stat_buf.st_mtime) { - // Somebody else handled it, just downgrade. - m_log.debug("update of local resource handled by another thread, downgrading lock"); - m_lock->unlock(); - m_lock->rdlock(); - return this; - } - - // Update the timestamp regardless. No point in repeatedly trying. - m_filestamp=stat_buf.st_mtime; - m_log.info("change detected, reloading local resource..."); - } - else { - time_t now = time(NULL); - - // Time to reload? If we have no data, filestamp is zero - // and there's no way current time is less than the interval. - if (now - m_filestamp < m_reloadInterval) - return this; - - // Elevate lock and recheck. - m_log.debug("reload interval for remote resource elapsed, elevating to a write lock"); - m_lock->unlock(); - m_lock->wrlock(); - if (now - m_filestamp < m_reloadInterval) { - // Somebody else handled it, just downgrade. - m_log.debug("update of remote resource handled by another thread, downgrading lock"); - m_lock->unlock(); - m_lock->rdlock(); - return this; - } - - m_filestamp = now; - m_log.info("reloading remote resource..."); - } - - // Do this once... - try { - // At this point we're holding the write lock, so make sure we pop it. - SharedLock lockwrap(m_lock,false); - pair ret=load(); - if (ret.first) - ret.second->getOwnerDocument()->release(); - } - catch (long& ex) { - if (ex == HTTPResponse::XMLTOOLING_HTTP_STATUS_NOTMODIFIED) { - m_log.info("remote resource (%s) unchanged from cached version", m_source.c_str()); - } - else { - // Shouldn't happen, we should only get codes intended to be gracefully handled. - m_log.crit("maintaining existing configuration, remote resource fetch returned atypical status code (%d)", ex); - } - } - catch (exception& ex) { - m_log.crit("maintaining existing configuration, error reloading resource (%s): %s", m_source.c_str(), ex.what()); - } - - // If we made it here, the swap may or may not have worked, but we need to relock. - m_log.debug("attempt to update resource complete, relocking"); - m_lock->rdlock(); - return this; -} - -void ReloadableXMLFile::unlock() +pair ReloadableXMLFile::load() { - if (m_lock) - m_lock->unlock(); + return load(false); } -pair ReloadableXMLFile::load() +pair ReloadableXMLFile::background_load() { - return load(false); + // If this method isn't overridden, we acquire a write lock + // and just call the old override. + m_lock->wrlock(); + SharedLock locker(m_lock, false); + return load(); } diff --git a/xmltooling/util/ReloadableXMLFile.h b/xmltooling/util/ReloadableXMLFile.h index 8ae62cf..70aca5a 100644 --- a/xmltooling/util/ReloadableXMLFile.h +++ b/xmltooling/util/ReloadableXMLFile.h @@ -1,5 +1,5 @@ /* - * Copyright 2001-2009 Internet2 + * Copyright 2001-2010 Internet2 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -32,7 +32,9 @@ namespace xmltooling { + class XMLTOOL_API CondWait; class XMLTOOL_API RWLock; + class XMLTOOL_API Thread; /** * Base class for file-based XML configuration. @@ -57,6 +59,8 @@ namespace xmltooling { *
enables periodic refresh of remote file
*
backingFilePath
*
location for backup of remote resource
+ *
id
+ *
identifies the plugin instance for logging purposes
* * * @param e DOM to supply configuration @@ -72,6 +76,25 @@ namespace xmltooling { *

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

This method is not called with the object locked, so actual + * modification of implementation state requires explicit locking within + * the method override. + * + * @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 background_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. + * + *

Subclasses that continue to override this method will function, but + * a write lock will be acquired and held for the entire operation. * * @return a pair consisting of a flag indicating whether to take ownership of * the document, and the root element of the tree to load @@ -93,7 +116,7 @@ namespace xmltooling { /** Path to backup copy for remote resource. */ std::string m_backing; - /** Last modification of local resource or reload of remote resource. */ + /** Last modification of local resource. */ time_t m_filestamp; /** Time in seconds to wait before trying for new copy of remote resource. */ @@ -108,12 +131,21 @@ namespace xmltooling { /** Logging object. */ logging::Category& m_log; + /** Plugin identifier. */ + std::string m_id; + public: Lockable* lock(); void unlock(); private: std::pair load(bool backup); + + // Used to manage background reload/refresh. + bool m_shutdown; + CondWait* m_reload_wait; + Thread* m_reload_thread; + static void* reload_fn(void*); }; };