2 * Copyright 2001-2007 Internet2
\r
4 * Licensed under the Apache License, Version 2.0 (the "License");
\r
5 * you may not use this file except in compliance with the License.
\r
6 * You may obtain a copy of the License at
\r
8 * http://www.apache.org/licenses/LICENSE-2.0
\r
10 * Unless required by applicable law or agreed to in writing, software
\r
11 * distributed under the License is distributed on an "AS IS" BASIS,
\r
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
\r
13 * See the License for the specific language governing permissions and
\r
14 * limitations under the License.
\r
18 * @file ReloadableXMLFile.cpp
\r
20 * Base class for file-based XML configuration.
\r
23 #include "internal.h"
\r
24 #include "util/NDC.h"
\r
25 #include "util/ReloadableXMLFile.h"
\r
26 #include "util/XMLConstants.h"
\r
27 #include "util/XMLHelper.h"
\r
30 #include <sys/types.h>
\r
31 #include <sys/stat.h>
\r
33 #include <xercesc/framework/LocalFileInputSource.hpp>
\r
34 #include <xercesc/framework/Wrapper4InputSource.hpp>
\r
35 #include <xercesc/framework/URLInputSource.hpp>
\r
36 #include <xercesc/util/XMLUniDefs.hpp>
\r
38 using namespace xmltooling::logging;
\r
39 using namespace xmltooling;
\r
40 using namespace std;
\r
42 static const XMLCh uri[] = UNICODE_LITERAL_3(u,r,i);
\r
43 static const XMLCh url[] = UNICODE_LITERAL_3(u,r,l);
\r
44 static const XMLCh path[] = UNICODE_LITERAL_4(p,a,t,h);
\r
45 static const XMLCh pathname[] = UNICODE_LITERAL_8(p,a,t,h,n,a,m,e);
\r
46 static const XMLCh file[] = UNICODE_LITERAL_4(f,i,l,e);
\r
47 static const XMLCh filename[] = UNICODE_LITERAL_8(f,i,l,e,n,a,m,e);
\r
48 static const XMLCh validate[] = UNICODE_LITERAL_8(v,a,l,i,d,a,t,e);
\r
49 static const XMLCh reloadChanges[] = UNICODE_LITERAL_13(r,e,l,o,a,d,C,h,a,n,g,e,s);
\r
50 static const XMLCh reloadInterval[] = UNICODE_LITERAL_14(r,e,l,o,a,d,I,n,t,e,r,v,a,l);
\r
51 static const XMLCh backingFilePath[] = UNICODE_LITERAL_15(b,a,c,k,i,n,g,F,i,l,e,P,a,t,h);
\r
53 ReloadableXMLFile::ReloadableXMLFile(const DOMElement* e, Category& log)
\r
54 : m_root(e), m_local(true), m_validate(false), m_filestamp(0), m_reloadInterval(0), m_lock(NULL), m_log(log)
\r
57 NDC ndc("ReloadableXMLFile");
\r
60 // Establish source of data...
\r
61 const XMLCh* source=e->getAttributeNS(NULL,uri);
\r
62 if (!source || !*source) {
\r
63 source=e->getAttributeNS(NULL,url);
\r
64 if (!source || !*source) {
\r
65 source=e->getAttributeNS(NULL,path);
\r
66 if (!source || !*source) {
\r
67 source=e->getAttributeNS(NULL,pathname);
\r
68 if (!source || !*source) {
\r
69 source=e->getAttributeNS(NULL,file);
\r
70 if (!source || !*source) {
\r
71 source=e->getAttributeNS(NULL,filename);
\r
82 if (source && *source) {
\r
83 const XMLCh* flag=e->getAttributeNS(NULL,validate);
\r
84 m_validate=(XMLString::equals(flag,xmlconstants::XML_TRUE) || XMLString::equals(flag,xmlconstants::XML_ONE));
\r
86 auto_ptr_char temp(source);
\r
87 m_source=temp.get();
\r
89 if (!m_local && !strstr(m_source.c_str(),"://")) {
\r
90 log.warn("deprecated usage of uri/url attribute for a local resource, use path instead");
\r
95 flag=e->getAttributeNS(NULL,reloadChanges);
\r
96 if (!XMLString::equals(flag,xmlconstants::XML_FALSE) && !XMLString::equals(flag,xmlconstants::XML_ZERO)) {
\r
98 struct _stat stat_buf;
\r
99 if (_stat(m_source.c_str(), &stat_buf) == 0)
\r
101 struct stat stat_buf;
\r
102 if (stat(m_source.c_str(), &stat_buf) == 0)
\r
104 m_filestamp=stat_buf.st_mtime;
\r
106 throw IOException("Unable to access local file ($1)", params(1,m_source.c_str()));
\r
107 m_lock=RWLock::create();
\r
109 log.debug("using local resource (%s), will %smonitor for changes", m_source.c_str(), m_lock ? "" : "not ");
\r
112 log.debug("using remote resource (%s)", m_source.c_str());
\r
113 source = e->getAttributeNS(NULL,backingFilePath);
\r
114 if (source && *source) {
\r
115 auto_ptr_char temp2(source);
\r
116 m_backing=temp2.get();
\r
117 log.debug("backup remote resource with (%s)", m_backing.c_str());
\r
119 source = e->getAttributeNS(NULL,reloadInterval);
\r
120 if (source && *source) {
\r
121 m_reloadInterval = XMLString::parseInt(source);
\r
122 if (m_reloadInterval > 0) {
\r
123 m_log.debug("will reload remote resource at most every %d seconds", m_reloadInterval);
\r
124 m_lock=RWLock::create();
\r
127 m_filestamp = time(NULL); // assume it gets loaded initially
\r
131 log.debug("no resource uri/path/name supplied, will load inline configuration");
\r
135 pair<bool,DOMElement*> ReloadableXMLFile::load(bool backup)
\r
142 if (m_source.empty()) {
\r
143 // Data comes from the DOM we were handed.
\r
144 m_log.debug("loading inline configuration...");
\r
145 return make_pair(false,XMLHelper::getFirstChildElement(m_root));
\r
148 // Data comes from a file we have to parse.
\r
150 m_log.warn("using local backup of remote resource");
\r
152 m_log.debug("loading configuration from external resource...");
\r
154 DOMDocument* doc=NULL;
\r
155 auto_ptr_XMLCh widenit(backup ? m_backing.c_str() : m_source.c_str());
\r
156 if (m_local || backup) {
\r
157 LocalFileInputSource src(widenit.get());
\r
158 Wrapper4InputSource dsrc(&src,false);
\r
160 doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);
\r
162 doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
\r
165 URLInputSource src(widenit.get());
\r
166 Wrapper4InputSource dsrc(&src,false);
\r
168 doc=XMLToolingConfig::getConfig().getValidatingParser().parse(dsrc);
\r
170 doc=XMLToolingConfig::getConfig().getParser().parse(dsrc);
\r
173 m_log.infoStream() << "loaded XML resource (" << (backup ? m_backing : m_source) << ")" << logging::eol;
\r
175 if (!backup && !m_backing.empty()) {
\r
176 m_log.debug("backing up remote resource to (%s)", m_backing.c_str());
\r
178 ofstream backer(m_backing.c_str());
\r
179 backer << *(doc->getDocumentElement());
\r
181 catch (exception& ex) {
\r
182 m_log.crit("exception while backing up resource: %s", ex.what());
\r
186 return make_pair(true,doc->getDocumentElement());
\r
189 catch (XMLException& e) {
\r
190 auto_ptr_char msg(e.getMessage());
\r
191 m_log.critStream() << "Xerces error while loading resource (" << (backup ? m_backing : m_source) << "): "
\r
192 << msg.get() << logging::eol;
\r
193 if (!backup && !m_backing.empty())
\r
195 throw XMLParserException(msg.get());
\r
197 catch (exception& e) {
\r
198 m_log.critStream() << "error while loading configuration from ("
\r
199 << (m_source.empty() ? "inline" : (backup ? m_backing : m_source)) << "): " << e.what() << logging::eol;
\r
200 if (!backup && !m_backing.empty())
\r
206 Lockable* ReloadableXMLFile::lock()
\r
213 // Check if we need to refresh.
\r
216 struct _stat stat_buf;
\r
217 if (_stat(m_source.c_str(), &stat_buf) != 0)
\r
220 struct stat stat_buf;
\r
221 if (stat(m_source.c_str(), &stat_buf) != 0)
\r
224 if (m_filestamp>=stat_buf.st_mtime)
\r
227 // Elevate lock and recheck.
\r
230 if (m_filestamp>=stat_buf.st_mtime) {
\r
231 // Somebody else handled it, just downgrade.
\r
237 // Update the timestamp regardless. No point in repeatedly trying.
\r
238 m_filestamp=stat_buf.st_mtime;
\r
239 m_log.info("change detected, reloading local resource...");
\r
242 time_t now = time(NULL);
\r
244 // Time to reload? If we have no data, filestamp is zero
\r
245 // and there's no way current time is less than the interval.
\r
246 if (now - m_filestamp < m_reloadInterval)
\r
249 // Elevate lock and recheck.
\r
252 if (now - m_filestamp < m_reloadInterval) {
\r
253 // Somebody else handled it, just downgrade.
\r
260 m_log.info("reloading remote resource...");
\r
265 // At this point we're holding the write lock, so make sure we pop it.
\r
266 SharedLock lockwrap(m_lock,false);
\r
267 pair<bool,DOMElement*> ret=load();
\r
269 ret.second->getOwnerDocument()->release();
\r
270 } catch (exception& ex) {
\r
271 m_log.crit("maintaining existing configuration, error reloading resource (%s): %s", m_source.c_str(), ex.what());
\r
274 // If we made it here, the swap may or may not have worked, but we need to relock.
\r