From 2e10f8b7b3d69f47849af62a8aae185e4b394d01 Mon Sep 17 00:00:00 2001 From: cantor Date: Wed, 3 Jan 2007 21:52:04 +0000 Subject: [PATCH] Base class for reloadable local and remote configurations. git-svn-id: https://svn.middleware.georgetown.edu/cpp-xmltooling/trunk@228 de75baf8-a10c-0410-a50a-987c0e22f00f --- .project | 54 ++++---- xmltooling/Makefile.am | 2 + xmltooling/util/ReloadableXMLFile.cpp | 233 ++++++++++++++++++++++++++++++++++ xmltooling/util/ReloadableXMLFile.h | 103 +++++++++++++++ xmltooling/xmltooling.vcproj | 8 ++ 5 files changed, 373 insertions(+), 27 deletions(-) create mode 100644 xmltooling/util/ReloadableXMLFile.cpp create mode 100644 xmltooling/util/ReloadableXMLFile.h diff --git a/.project b/.project index 40ff0eb..b3666fd 100644 --- a/.project +++ b/.project @@ -10,30 +10,18 @@ clean,full,incremental, - org.eclipse.cdt.make.core.build.arguments - - - - org.eclipse.cdt.core.errorOutputParser - org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser; + org.eclipse.cdt.make.core.enableCleanBuild + true - org.eclipse.cdt.make.core.environment - + org.eclipse.cdt.make.core.append_environment + true - org.eclipse.cdt.make.core.enableAutoBuild + org.eclipse.cdt.make.core.stopOnError false - org.eclipse.cdt.make.core.build.target.inc - all - - - org.eclipse.cdt.make.core.enableFullBuild - true - - org.eclipse.cdt.make.core.enabledIncrementalBuild true @@ -42,32 +30,44 @@ make - org.eclipse.cdt.make.core.build.target.clean - clean + org.eclipse.cdt.make.core.build.target.inc + all - org.eclipse.cdt.make.core.enableCleanBuild - true + org.eclipse.cdt.make.core.build.target.full + clean all - org.eclipse.cdt.make.core.append_environment + org.eclipse.cdt.make.core.build.arguments + + + + org.eclipse.cdt.make.core.useDefaultBuildCmd true - org.eclipse.cdt.make.core.build.target.full - clean all + org.eclipse.cdt.make.core.environment + - org.eclipse.cdt.make.core.useDefaultBuildCmd + org.eclipse.cdt.make.core.enableFullBuild true + org.eclipse.cdt.make.core.enableAutoBuild + false + + org.eclipse.cdt.make.core.build.target.auto all - org.eclipse.cdt.make.core.stopOnError - false + org.eclipse.cdt.make.core.build.target.clean + clean + + + org.eclipse.cdt.core.errorOutputParser + org.eclipse.cdt.core.MakeErrorParser;org.eclipse.cdt.core.GCCErrorParser;org.eclipse.cdt.core.GASErrorParser;org.eclipse.cdt.core.GLDErrorParser;org.eclipse.cdt.core.VCErrorParser; diff --git a/xmltooling/Makefile.am b/xmltooling/Makefile.am index ee9038c..b4a5c80 100644 --- a/xmltooling/Makefile.am +++ b/xmltooling/Makefile.am @@ -87,6 +87,7 @@ utilinclude_HEADERS = \ util/DateTime.h \ util/NDC.h \ util/ParserPool.h \ + util/ReloadableXMLFile.h \ util/ReplayCache.h \ util/StorageService.h \ util/TemplateEngine.h \ @@ -156,6 +157,7 @@ libxmltooling_la_SOURCES = \ util/DateTime.cpp \ util/NDC.cpp \ util/ParserPool.cpp \ + util/ReloadableXMLFile.cpp \ util/ReplayCache.cpp \ util/StorageService.cpp \ util/TemplateEngine.cpp \ diff --git a/xmltooling/util/ReloadableXMLFile.cpp b/xmltooling/util/ReloadableXMLFile.cpp new file mode 100644 index 0000000..3aab81a --- /dev/null +++ b/xmltooling/util/ReloadableXMLFile.cpp @@ -0,0 +1,233 @@ +/* + * Copyright 2001-2005 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file ReloadableXMLFile.cpp + * + * Base class for file-based XML configuration. + */ + +#include "internal.h" +#include "util/NDC.h" +#include "util/ReloadableXMLFile.h" +#include "util/XMLConstants.h" +#include "util/XMLHelper.h" + +#include +#include + +#include +#include +#include +#include +#include + +using namespace xmltooling; +using namespace log4cpp; +using namespace std; + +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); +static const XMLCh pathname[] = UNICODE_LITERAL_8(p,a,t,h,n,a,m,e); +static const XMLCh file[] = UNICODE_LITERAL_4(f,i,l,e); +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); + +ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e) + : m_root(e), m_local(true), m_validate(false), m_filestamp(0), m_lock(NULL) +{ +#ifdef _DEBUG + NDC ndc("ReloadableXMLFile"); +#endif + Category& log=Category::getInstance(XMLTOOLING_LOGCAT".ReloadableXMLFile"); + + // Establish source of data... + const XMLCh* source=e->getAttributeNS(NULL,uri); + if (!source || !*source) { + source=e->getAttributeNS(NULL,url); + if (!source || !*source) { + source=e->getAttributeNS(NULL,path); + if (!source || !*source) { + source=e->getAttributeNS(NULL,pathname); + if (!source || !*source) { + source=e->getAttributeNS(NULL,file); + if (!source || !*source) { + source=e->getAttributeNS(NULL,filename); + } + } + } + } + else + m_local=false; + } + else + m_local=false; + + if (source && *source) { + const XMLCh* flag=e->getAttributeNS(NULL,validate); + m_validate=(XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE)); + + auto_ptr_char temp(source); + m_source=temp.get(); + + if (!m_local && !strstr(m_source.c_str(),"://")) { + log.warn("deprecated usage of uri/url attribute for a local resource, use path instead"); + m_local=true; + } + + flag=e->getAttributeNS(NULL,reloadChanges); + if (!flag || XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE)) { + if (m_local) { +#ifdef WIN32 + struct _stat stat_buf; + if (_stat(m_source.c_str(), &stat_buf) == 0) + m_filestamp=stat_buf.st_mtime; + else + m_local=false; +#else + struct stat stat_buf; + if (stat(m_source.c_str(), &stat_buf) == 0) + m_filestamp=stat_buf.st_mtime; + else + m_local=false; +#endif + } + m_lock=RWLock::create(); + } + + log.debug("using external resource (%s), will %smonitor for changes", m_source.c_str(), m_lock ? "" : "not "); + } + else + log.debug("no resource uri/path/name supplied, will load inline configuration"); +} + +pair ReloadableXMLFile::load() +{ +#ifdef _DEBUG + NDC ndc("init"); +#endif + Category& log=Category::getInstance(XMLTOOLING_LOGCAT".ReloadableXMLFile"); + + try { + if (m_source.empty()) { + // Data comes from the DOM we were handed. + log.debug("loading inline configuration..."); + return make_pair(false,XMLHelper::getFirstChildElement(m_root)); + } + else { + // Data comes from a file we have to parse. + log.debug("loading configuration from external resource..."); + + DOMDocument* doc=NULL; + auto_ptr_XMLCh widenit(m_source.c_str()); + if (m_local) { + LocalFileInputSource src(widenit.get()); + Wrapper4InputSource dsrc(&src,false); + if (m_validate) + doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc); + else + doc=XMLToolingConfig::getConfig().getParser().parse(dsrc); + } + else { + URLInputSource src(widenit.get()); + Wrapper4InputSource dsrc(&src,false); + if (m_validate) + doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc); + else + doc=XMLToolingConfig::getConfig().getParser().parse(dsrc); + } + + log.infoStream() << "loaded XML resource (" << m_source << ")" << CategoryStream::ENDLINE; + return make_pair(true,doc->getDocumentElement()); + } + } + catch (XMLException& e) { + auto_ptr_char msg(e.getMessage()); + log.errorStream() << "Xerces error while loading resource (" << m_source << "): " + << msg.get() << CategoryStream::ENDLINE; + throw XMLParserException(msg.get()); + } + catch (XMLToolingException& e) { + log.errorStream() << "error while loading configuration from (" + << (m_source.empty() ? "inline" : m_source) << "): " << e.what() << CategoryStream::ENDLINE; + throw; + } +} + +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_lock->unlock(); + m_lock->wrlock(); + if (m_filestamp>=stat_buf.st_mtime) { + // Somebody else handled it, just downgrade. + m_lock->unlock(); + m_lock->rdlock(); + return this; + } + + // Update the timestamp regardless. No point in repeatedly trying. + m_filestamp=stat_buf.st_mtime; + } + else { + if (isValid()) + return this; + + // Elevate lock and recheck. + m_lock->unlock(); + m_lock->wrlock(); + if (isValid()) { + // Somebody else handled it, just downgrade. + m_lock->unlock(); + m_lock->rdlock(); + return this; + } + } + + // Do this once... + do { + // 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(); + } while(0); + + // If we made it here, the swap may or may not have worked, but we need to relock. + m_lock->rdlock(); + return this; +} diff --git a/xmltooling/util/ReloadableXMLFile.h b/xmltooling/util/ReloadableXMLFile.h new file mode 100644 index 0000000..019f9b2 --- /dev/null +++ b/xmltooling/util/ReloadableXMLFile.h @@ -0,0 +1,103 @@ +/* + * Copyright 2001-2006 Internet2 + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * @file xmltooling/util/ReloadableXMLFile.h + * + * Base class for file-based XML configuration. + */ + +#ifndef __xmltooling_reloadable_h__ +#define __xmltooling_reloadable_h__ + +#include +#include + +#include +#include + +namespace xmltooling { + + /** + * Base class for file-based XML configuration. + */ + class XMLTOOL_API ReloadableXMLFile : protected virtual Lockable + { + MAKE_NONCOPYABLE(ReloadableXMLFile); + + protected: + /** + * Constructor taking a DOM element supporting the following content: + * + *
+ *
file | filename | path | pathname
+ *
identifies a local file
+ *
uri | url
+ *
identifies a remote resource
+ *
validate
+ *
use a validating parser
+ *
reloadChanges
+ *
enables monitoring of resources for changes
+ *
+ * + * @param e DOM to supply configuration + */ + ReloadableXMLFile(const DOMElement* e); + + virtual ~ReloadableXMLFile() { + delete m_lock; + } + + public: + Lockable* lock(); + + void unlock() { + if (m_lock) + m_lock->unlock(); + } + + /** + * Loads configuration material. + * + *

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. + * + * @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(); + + /** + * Overrideable method to determine whether a remote resource remains valid. + * + * @return true iff the resource remains valid and should not be reloaded + */ + virtual bool isValid() const { + return true; + } + + private: + const DOMElement* m_root; + bool m_local, m_validate; + std::string m_source; + time_t m_filestamp; + RWLock* m_lock; + }; + +}; + +#endif /* __xmltooling_reloadable_h__ */ diff --git a/xmltooling/xmltooling.vcproj b/xmltooling/xmltooling.vcproj index 3efe188..f92fdb0 100644 --- a/xmltooling/xmltooling.vcproj +++ b/xmltooling/xmltooling.vcproj @@ -243,6 +243,10 @@ > + + @@ -537,6 +541,10 @@ > + + -- 2.1.4