Pulled out curl accessor, doesn't seem to work anyway.
[shibboleth/xmltooling.git] / xmltooling / util / ReloadableXMLFile.cpp
index 82264fe..054e3d2 100644 (file)
@@ -26,6 +26,7 @@
 #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
@@ -38,17 +39,19 @@ using namespace xmltooling;
 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
+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_lock(NULL), m_log(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
@@ -88,33 +91,47 @@ ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e, Category& log)
             m_local=true;\r
         }\r
 \r
-        flag=e->getAttributeNS(NULL,reloadChanges);\r
-        if (!XMLString::equals(flag,xmlconstants::XML_FALSE) && !XMLString::equals(flag,xmlconstants::XML_ZERO)) {\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
+            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_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
+    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
@@ -128,11 +145,14 @@ pair<bool,DOMElement*> ReloadableXMLFile::load()
         }\r
         else {\r
             // Data comes from a file we have to parse.\r
-            m_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
@@ -149,19 +169,35 @@ pair<bool,DOMElement*> ReloadableXMLFile::load()
                     doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);\r
             }\r
 \r
-            m_log.infoStream() << "loaded XML resource (" << m_source << ")" << CategoryStream::ENDLINE;\r
+            m_log.infoStream() << "loaded XML resource (" << (backup ? m_backing : m_source) << ")" << CategoryStream::ENDLINE;\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
-        m_log.critStream() << "Xerces error while loading resource (" << m_source << "): "\r
+        m_log.critStream() << "Xerces error while loading resource (" << (backup ? m_backing : m_source) << "): "\r
             << msg.get() << CategoryStream::ENDLINE;\r
+        if (!backup && !m_backing.empty())\r
+            return load(true);\r
         throw XMLParserException(msg.get());\r
     }\r
     catch (exception& e) {\r
         m_log.critStream() << "error while loading configuration from ("\r
-            << (m_source.empty() ? "inline" : m_source) << "): " << e.what() << CategoryStream::ENDLINE;\r
+            << (m_source.empty() ? "inline" : (backup ? m_backing : m_source)) << "): " << e.what() << CategoryStream::ENDLINE;\r
+        if (!backup && !m_backing.empty())\r
+            return load(true);\r
         throw;\r
     }\r
 }\r
@@ -202,19 +238,25 @@ Lockable* ReloadableXMLFile::lock()
         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
-        m_log.info("local copy invalid, reloading remote resource...");\r
+\r
+        m_filestamp = now;\r
+        m_log.info("reloading remote resource...");\r
     }\r
     \r
     // Do this once...\r