+/*\r
+ * Copyright 2001-2005 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
+ * You may obtain a copy of the License at\r
+ *\r
+ * http://www.apache.org/licenses/LICENSE-2.0\r
+ *\r
+ * Unless required by applicable law or agreed to in writing, software\r
+ * distributed under the License is distributed on an "AS IS" BASIS,\r
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
+ * See the License for the specific language governing permissions and\r
+ * limitations under the License.\r
+ */\r
+\r
+/**\r
+ * @file ReloadableXMLFile.cpp\r
+ * \r
+ * Base class for file-based XML configuration.\r
+ */\r
+\r
+#include "internal.h"\r
+#include "util/NDC.h"\r
+#include "util/ReloadableXMLFile.h"\r
+#include "util/XMLConstants.h"\r
+#include "util/XMLHelper.h"\r
+\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;\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
+{\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
+ if (!source || !*source) {\r
+ source=e->getAttributeNS(NULL,url);\r
+ if (!source || !*source) {\r
+ source=e->getAttributeNS(NULL,path);\r
+ if (!source || !*source) {\r
+ source=e->getAttributeNS(NULL,pathname);\r
+ if (!source || !*source) {\r
+ source=e->getAttributeNS(NULL,file);\r
+ if (!source || !*source) {\r
+ source=e->getAttributeNS(NULL,filename);\r
+ }\r
+ }\r
+ }\r
+ }\r
+ else\r
+ m_local=false;\r
+ }\r
+ else\r
+ m_local=false;\r
+ \r
+ if (source && *source) {\r
+ const XMLCh* flag=e->getAttributeNS(NULL,validate);\r
+ m_validate=(XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE));\r
+\r
+ auto_ptr_char temp(source);\r
+ m_source=temp.get();\r
+ \r
+ if (!m_local && !strstr(m_source.c_str(),"://")) {\r
+ log.warn("deprecated usage of uri/url attribute for a local resource, use path instead");\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
+#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
+ m_filestamp=stat_buf.st_mtime;\r
+ else\r
+ m_local=false;\r
+#endif\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
+ log.debug("no resource uri/path/name supplied, will load inline configuration");\r
+}\r
+\r
+pair<bool,DOMElement*> ReloadableXMLFile::load()\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
+ 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
+\r
+ DOMDocument* doc=NULL;\r
+ auto_ptr_XMLCh widenit(m_source.c_str());\r
+ if (m_local) {\r
+ LocalFileInputSource src(widenit.get());\r
+ Wrapper4InputSource dsrc(&src,false);\r
+ if (m_validate)\r
+ doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);\r
+ else\r
+ doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);\r
+ }\r
+ else {\r
+ URLInputSource src(widenit.get());\r
+ Wrapper4InputSource dsrc(&src,false);\r
+ if (m_validate)\r
+ doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);\r
+ else\r
+ doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);\r
+ }\r
+\r
+ log.infoStream() << "loaded XML resource (" << m_source << ")" << CategoryStream::ENDLINE;\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
+ 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
+ throw;\r
+ }\r
+}\r
+\r
+Lockable* ReloadableXMLFile::lock()\r
+{\r
+ if (!m_lock)\r
+ return this;\r
+ \r
+ m_lock->rdlock();\r
+\r
+ // Check if we need to refresh.\r
+ if (m_local) {\r
+#ifdef WIN32\r
+ struct _stat stat_buf;\r
+ if (_stat(m_source.c_str(), &stat_buf) != 0)\r
+ return this;\r
+#else\r
+ struct stat stat_buf;\r
+ if (stat(m_source.c_str(), &stat_buf) != 0)\r
+ return this;\r
+#endif\r
+ if (m_filestamp>=stat_buf.st_mtime)\r
+ return this;\r
+ \r
+ // Elevate lock and recheck.\r
+ m_lock->unlock();\r
+ m_lock->wrlock();\r
+ if (m_filestamp>=stat_buf.st_mtime) {\r
+ // Somebody else handled it, just downgrade.\r
+ m_lock->unlock();\r
+ m_lock->rdlock();\r
+ return this;\r
+ }\r
+\r
+ // Update the timestamp regardless. No point in repeatedly trying.\r
+ m_filestamp=stat_buf.st_mtime;\r
+ }\r
+ else {\r
+ if (isValid())\r
+ return this;\r
+\r
+ // Elevate lock and recheck.\r
+ m_lock->unlock();\r
+ m_lock->wrlock();\r
+ if (isValid()) {\r
+ // Somebody else handled it, just downgrade.\r
+ m_lock->unlock();\r
+ m_lock->rdlock();\r
+ return this;\r
+ }\r
+ }\r
+ \r
+ // Do this once...\r
+ do {\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
+ \r
+ // If we made it here, the swap may or may not have worked, but we need to relock.\r
+ m_lock->rdlock();\r
+ return this;\r
+}\r