/*\r
- * Copyright 2001-2005 Internet2\r
+ * Copyright 2001-2007 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
#include "util/XMLConstants.h"\r
#include "util/XMLHelper.h"\r
\r
+#include <fstream>\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::logging;\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
+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
+static const XMLCh reloadInterval[] = UNICODE_LITERAL_14(r,e,l,o,a,d,I,n,t,e,r,v,a,l);\r
+static const XMLCh backingFilePath[] = UNICODE_LITERAL_15(b,a,c,k,i,n,g,F,i,l,e,P,a,t,h);\r
+\r
+ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e, Category& log)\r
+ : m_root(e), m_local(true), m_validate(false), m_filestamp(0), m_reloadInterval(0), m_lock(NULL), m_log(log)\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
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
+ if (m_local) {\r
+ flag=e->getAttributeNS(NULL,reloadChanges);\r
+ if (!XMLString::equals(flag,xmlconstants::XML_FALSE) && !XMLString::equals(flag,xmlconstants::XML_ZERO)) {\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
+#endif\r
m_filestamp=stat_buf.st_mtime;\r
else\r
- m_local=false;\r
-#endif\r
+ throw IOException("Unable to access local file ($1)", params(1,m_source.c_str()));\r
+ m_lock=RWLock::create();\r
}\r
- m_lock=RWLock::create();\r
+ log.debug("using local resource (%s), will %smonitor for changes", m_source.c_str(), m_lock ? "" : "not ");\r
+ }\r
+ else {\r
+ log.debug("using remote resource (%s)", m_source.c_str());\r
+ source = e->getAttributeNS(NULL,backingFilePath);\r
+ if (source && *source) {\r
+ auto_ptr_char temp2(source);\r
+ m_backing=temp2.get();\r
+ log.debug("backup remote resource with (%s)", m_backing.c_str());\r
+ }\r
+ source = e->getAttributeNS(NULL,reloadInterval);\r
+ if (source && *source) {\r
+ m_reloadInterval = XMLString::parseInt(source);\r
+ if (m_reloadInterval > 0) {\r
+ m_log.debug("will reload remote resource at most every %d seconds", m_reloadInterval);\r
+ m_lock=RWLock::create();\r
+ }\r
+ }\r
+ m_filestamp = time(NULL); // assume it gets loaded initially\r
}\r
-\r
- log.debug("using external resource (%s), will %smonitor for changes", m_source.c_str(), m_lock ? "" : "not ");\r
}\r
- else\r
+ else {\r
log.debug("no resource uri/path/name supplied, will load inline configuration");\r
+ }\r
}\r
\r
-pair<bool,DOMElement*> ReloadableXMLFile::load()\r
+pair<bool,DOMElement*> ReloadableXMLFile::load(bool backup)\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
+ m_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
+ if (backup)\r
+ m_log.warn("using local backup of remote resource");\r
+ else\r
+ m_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
+ auto_ptr_XMLCh widenit(backup ? m_backing.c_str() : m_source.c_str());\r
+ if (m_local || backup) {\r
LocalFileInputSource src(widenit.get());\r
Wrapper4InputSource dsrc(&src,false);\r
if (m_validate)\r
doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);\r
}\r
\r
- log.infoStream() << "loaded XML resource (" << m_source << ")" << CategoryStream::ENDLINE;\r
+ m_log.infoStream() << "loaded XML resource (" << (backup ? m_backing : m_source) << ")" << logging::eol;\r
+\r
+ if (!backup && !m_backing.empty()) {\r
+ m_log.debug("backing up remote resource to (%s)", m_backing.c_str());\r
+ try {\r
+ ofstream backer(m_backing.c_str());\r
+ backer << *(doc->getDocumentElement());\r
+ }\r
+ catch (exception& ex) {\r
+ m_log.crit("exception while backing up resource: %s", ex.what());\r
+ }\r
+ }\r
+\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
+ m_log.critStream() << "Xerces error while loading resource (" << (backup ? m_backing : m_source) << "): "\r
+ << msg.get() << logging::eol;\r
+ if (!backup && !m_backing.empty())\r
+ return load(true);\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
+ catch (exception& e) {\r
+ m_log.critStream() << "error while loading configuration from ("\r
+ << (m_source.empty() ? "inline" : (backup ? m_backing : m_source)) << "): " << e.what() << logging::eol;\r
+ if (!backup && !m_backing.empty())\r
+ return load(true);\r
throw;\r
}\r
}\r
\r
// Update the timestamp regardless. No point in repeatedly trying.\r
m_filestamp=stat_buf.st_mtime;\r
+ m_log.info("change detected, reloading local resource...");\r
}\r
else {\r
- if (isValid())\r
+ time_t now = time(NULL);\r
+\r
+ // Time to reload? If we have no data, filestamp is zero\r
+ // and there's no way current time is less than the interval.\r
+ if (now - m_filestamp < m_reloadInterval)\r
return this;\r
\r
// Elevate lock and recheck.\r
m_lock->unlock();\r
m_lock->wrlock();\r
- if (isValid()) {\r
+ if (now - m_filestamp < m_reloadInterval) {\r
// Somebody else handled it, just downgrade.\r
m_lock->unlock();\r
m_lock->rdlock();\r
return this;\r
}\r
+\r
+ m_filestamp = now;\r
+ m_log.info("reloading remote resource...");\r
}\r
\r
// Do this once...\r
- do {\r
+ try {\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
+ } catch (exception& ex) {\r
+ m_log.crit("maintaining existing configuration, error reloading resource (%s): %s", m_source.c_str(), ex.what());\r
+ }\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