/**
* Copyright (C) 2010 Orbeon, Inc.
*
* This program is free software; you can redistribute it and/or modify it under the terms of the
* GNU Lesser General Public License as published by the Free Software Foundation; either version
* 2.1 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Lesser General Public License for more details.
*
* The full text of the license is available at http://www.gnu.org/copyleft/lesser.html
*/
package org.orbeon.oxf.properties;
import org.apache.log4j.Logger;
import org.orbeon.dom.Document;
import org.orbeon.dom.QName;
import org.orbeon.oxf.common.OXFException;
import org.orbeon.oxf.pipeline.api.PipelineContext;
import org.orbeon.oxf.processor.DOMSerializer;
import org.orbeon.oxf.processor.Processor;
import org.orbeon.oxf.processor.ProcessorImpl;
import org.orbeon.oxf.util.LoggerFactory;
import org.orbeon.oxf.util.PipelineUtils;
import java.util.Set;
/**
* This class provides access to global, configurable properties, as well as to processor-specific properties. This is
* an example of properties file:
*
* <properties xmlns:xs="http://www.w3.org/2001/XMLSchema"
* xmlns:oxf="http://www.orbeon.com/oxf/processors">
*
* <property as="xs:integer" name="oxf.cache.size" value="200"/>
* <property as="xs:string" processor-name="oxf:page-flow" name="instance-passing" value="redirect"/>
*
* </properties>
*/
public class Properties {
private static final Logger logger = LoggerFactory.createLogger(Properties.class);
public static final String DEFAULT_PROPERTIES_URI = "oxf:/properties.xml";
public static final String PROPERTIES_SCHEMA_URI = "http://www.orbeon.com/oxf/properties";
private static final int RELOAD_DELAY = 5 * 1000;
/**
* The global Properties instance.
*/
private static Properties instance;
private static String propertiesURI = DEFAULT_PROPERTIES_URI;
private static boolean initializing = false;
/**
* The property store.
*/
private PropertyStore propertyStore = null;
// Used for refresh
private Processor urlGenerator;
private DOMSerializer domSerializer;
private long lastUpdate = Long.MIN_VALUE;
private Properties() {
// Don't allow creation from outside
}
/**
* Set URI of the resource we will read the properties from.
*/
public static void init(String propertiesURI) {
Properties.propertiesURI = propertiesURI;
instance();
}
/**
* Invalidate all properties (for testing).
*/
public static void invalidate() {
instance = null;
}
/**
* Return the global Properties.
*
* @return Properties
*/
public static Properties instance() {
if (instance == null) {
instance = new Properties();
instance.update();
}
return instance;
}
/**
* Make sure we have the latest properties, and if we don't (resource changed), reload them.
*/
private void update() {
if (! initializing) {
done:
try {
initializing = true;
final long current = System.currentTimeMillis();
if (lastUpdate + RELOAD_DELAY >= current) break done;
// Create mini-pipeline to read properties if needed
if (urlGenerator == null) {
urlGenerator = PipelineUtils.createURLGenerator(propertiesURI, true);// enable XInclude too
domSerializer = new DOMSerializer();
PipelineUtils.connect(urlGenerator, ProcessorImpl.OUTPUT_DATA, domSerializer, ProcessorImpl.INPUT_DATA);
}
// Initialize pipeline
// Candidate for Scala withPipelineContext
final PipelineContext pipelineContext = new PipelineContext();
boolean success = false;
try {
urlGenerator.reset(pipelineContext);
domSerializer.reset(pipelineContext);
// Find whether we can skip reloading
if (propertyStore != null && domSerializer.findInputLastModified(pipelineContext) <= lastUpdate) {
if (logger.isDebugEnabled()) {
logger.debug("Not reloading properties because they have not changed.");
}
lastUpdate = current;
success = true;
break done;
}
if (logger.isDebugEnabled()) {
logger.debug("Reloading properties because timestamp indicates they may have changed.");
}
// Read updated properties document
final Document document = domSerializer.runGetDocument(pipelineContext);
if (document == null || document.content() == null || document.content().size() == 0) {
throw new OXFException("Failure to initialize Orbeon Forms properties");
}
propertyStore = new PropertyStore(document);
lastUpdate = current;
success = true;
} catch (Throwable t) {
LoggerFactory.logger.error(t);
} finally {
pipelineContext.destroy(success);
}
} finally {
initializing = false;
}
}
}
public PropertySet getPropertySet() {
if (propertyStore == null)
return null;
update();
return propertyStore.getGlobalPropertySet();
}
public PropertySet getPropertySet(final QName processorName) {
if (propertyStore == null)
return null;
update();
return propertyStore.getProcessorPropertySet(processorName);
}
public Set keySet() {
if (propertyStore == null)
return null;
return propertyStore.getGlobalPropertySet().keySet();
}
}