You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by rg...@apache.org on 2007/12/27 21:00:46 UTC
svn commit: r607115 - in /cocoon/branches/BRANCH_2_1_X: ./
src/java/org/apache/cocoon/
src/java/org/apache/cocoon/components/modules/input/ src/webapp/WEB-INF/
src/webapp/WEB-INF/properties/ src/webapp/samples/modules/
src/webapp/samples/modules/a/
Author: rgoers
Date: Thu Dec 27 12:00:45 2007
New Revision: 607115
URL: http://svn.apache.org/viewvc?rev=607115&view=rev
Log:
<action dev="RG" type="fix" fixes-bug="COCOON-1574">
Created XPathXMLFileModule to address issus with XMLFileModule. XPathXMLFileModule supports variable replacement and caching of documents in ehcache and expressions as soft references.
</action>
Added:
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XPathXMLFileModule.java
cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/a/
cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/a/forrestconf.xml
Modified:
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/AbstractJXPathModule.java
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathHelper.java
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathMetaModule.java
cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XMLFileModule.java
cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf
cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/properties/core.properties
cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/menu.xml
cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/sitemap.xmap
cocoon/branches/BRANCH_2_1_X/status.xml
Modified: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles (original)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/cocoon.roles Thu Dec 27 12:00:45 2007
@@ -87,6 +87,10 @@
shorthand="transient-store"
default-class="org.apache.cocoon.components.store.impl.DefaultTransientStore"/>
+ <role name="org.apache.excalibur.store.Store/XPathXMLFileTransientStore"
+ shorthand="xmlfile-store"
+ default-class="org.apache.cocoon.components.store.impl.DefaultTransientStore"/>
+
<!--
The persistent store is only an auxiliary store that shouldn't be
used by Cocoon users. It should only be used - if required - by
Modified: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/AbstractJXPathModule.java
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/AbstractJXPathModule.java?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/AbstractJXPathModule.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/AbstractJXPathModule.java Thu Dec 27 12:00:45 2007
@@ -104,7 +104,7 @@
if (modeConf != null) {
name = modeConf.getChild("parameter").getValue(this.parameter != null ? this.parameter : name);
}
- return JXPathHelper.getAttribute(name, modeConf, this.configuration, contextObj);
+ return JXPathHelper.getAttributeValue(name, modeConf, this.configuration, contextObj);
}
Modified: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathHelper.java
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathHelper.java?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathHelper.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathHelper.java Thu Dec 27 12:00:45 2007
@@ -81,6 +81,47 @@
}
}
+ /**
+ * Return the String value of the attribute or element identified in the XPath expression.
+ * @param name The XPath expression
+ * @param modeConf The Configuration.
+ * @param setup The JXPathHelperConfiguration.
+ * @param contextObj The root Element to search.
+ * @return The String value of the attribute or element identified.
+ * @throws ConfigurationException if an Exception occurs.
+ */
+ public static String getAttributeValue(String name,
+ Configuration modeConf,
+ JXPathHelperConfiguration setup,
+ Object contextObj)
+ throws ConfigurationException {
+
+ if (contextObj == null) {
+ return null;
+ }
+
+ try {
+ JXPathContext jxContext = JXPathContext.newContext(contextObj);
+ setup(setup, jxContext, modeConf);
+ Object obj = jxContext.getValue(name);
+ if (obj != null) {
+ return obj.toString();
+ }
+ return null;
+ } catch (Exception e) {
+ throw new ConfigurationException("Module does not support <" + name + ">" + "attribute.", e);
+ }
+ }
+
+ /**
+ * Return the String value of the attribute or the Node found using the XPath expression.
+ * @param name The XPath expression
+ * @param modeConf The Configuration.
+ * @param setup The JXPathHelperConfiguration.
+ * @param contextObj The root Element to search.
+ * @return The String value of the attribute or the Element located.
+ * @throws ConfigurationException if an Exception occurs.
+ */
public static Object getAttribute(String name,
Configuration modeConf,
JXPathHelperConfiguration setup,
Modified: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathMetaModule.java
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathMetaModule.java?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathMetaModule.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/JXPathMetaModule.java Thu Dec 27 12:00:45 2007
@@ -129,7 +129,7 @@
if (modeConf != null) {
name = modeConf.getChild("parameter").getValue(!this.parameter.equals("") ? this.parameter : name);
}
- return JXPathHelper.getAttribute(name, modeConf, this.configuration, contextObj);
+ return JXPathHelper.getAttributeValue(name, modeConf, this.configuration, contextObj);
}
Modified: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XMLFileModule.java
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XMLFileModule.java?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XMLFileModule.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XMLFileModule.java Thu Dec 27 12:00:45 2007
@@ -413,7 +413,7 @@
if (getValues){
result = JXPathHelper.getAttributeValues(name, modeConf, this.configuration, contextObj);
} else {
- result = JXPathHelper.getAttribute(name, modeConf, this.configuration, contextObj);
+ result = JXPathHelper.getAttributeValue(name, modeConf, this.configuration, contextObj);
}
if (this.cacheExpressions) {
cache.put(name, result);
Added: cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XPathXMLFileModule.java
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XPathXMLFileModule.java?rev=607115&view=auto
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XPathXMLFileModule.java (added)
+++ cocoon/branches/BRANCH_2_1_X/src/java/org/apache/cocoon/components/modules/input/XPathXMLFileModule.java Thu Dec 27 12:00:45 2007
@@ -0,0 +1,515 @@
+package org.apache.cocoon.components.modules.input;
+
+import org.apache.avalon.framework.service.ServiceException;
+import org.apache.avalon.framework.service.ServiceManager;
+import org.apache.avalon.framework.service.Serviceable;
+import org.apache.avalon.framework.thread.ThreadSafe;
+import org.apache.avalon.framework.configuration.Configuration;
+import org.apache.avalon.framework.configuration.ConfigurationException;
+import org.apache.excalibur.source.SourceResolver;
+import org.apache.excalibur.source.SourceValidity;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.store.Store;
+import org.apache.cocoon.components.source.SourceUtil;
+import org.apache.cocoon.components.treeprocessor.variables.VariableResolverFactory;
+import org.apache.cocoon.components.treeprocessor.variables.VariableResolver;
+
+import org.apache.cocoon.sitemap.PatternException;
+import org.apache.cocoon.ProcessingException;
+import org.apache.commons.collections.map.ReferenceMap;
+import org.apache.commons.collections.map.AbstractReferenceMap;
+import org.w3c.dom.Document;
+import org.xml.sax.SAXException;
+
+import java.util.Map;
+import java.net.MalformedURLException;
+import java.io.IOException;
+
+/**
+ * <grammar>
+ * <define name="input.module.config.contents" combine="choice">
+ * <optional><element name="cacheable"><data type="boolean"/></element></optional>
+ * <optional><element name="reloadable"><data type="boolean"/></element></optional>
+ * <optional>
+ * <ref name="org.apache.cocoon.components.modules.input.XPathXMLFileModule:file">
+ * </optional>
+ * <optional><element name="cache-role"><data type="String"/></element></optional>
+ * </define>
+ * <p/>
+ * <define name="input.module.runtime.contents" combine="choice">
+ * <optional>
+ * <ref name="org.apache.cocoon.components.modules.input.XPathXMLFileModule:file">
+ * </optional>
+ * </define>
+ * <p/>
+ * <define name="org.apache.cocoon.components.modules.input.XPathXMLFileModule:file">
+ * <element name="file">
+ * <attribute name="src"><data type="anyURI"/></attribute>
+ * <optional><attribute name="cacheable"><data type="boolean"/></attribute></optional>
+ * <optional><attribute name="reloadable"><data type="boolean"/></attribute></optional>
+ * </element>
+ * </define>
+ * </grammar>
+ * <p/>
+ * This module provides an Input Module interface to any XML document, by using
+ * XPath expressions as attribute keys.
+ * The XML can be obtained from any Cocoon <code>Source</code> (e.g.,
+ * <code>cocoon:/...</code>, <code>context://..</code>, and regular URLs).
+ * Sources can be cached in memory for better performance and reloaded if
+ * changed. The source can also contain references to other input modules to allow the source
+ * file name to be determined dynamically.
+ * <p/>
+ * Caching and reloading can be turned on / off (default: caching on,
+ * reloading off) through <code><reloadable>false</reloadable></code>
+ * and <code><cacheable>false</cacheable></code>. The file
+ * (source) to use is specified through <code><file
+ * src="protocol:path/to/file.xml" reloadable="true" cacheable="true"/></code>
+ * optionally overriding the defaults for caching and/or reloading. When specfied as attributes
+ * to the file element the values for cacheable and reloadable may be input module references which
+ * will be resolved on every call. These must resolve to 'true' or 'false'.
+ * </>
+ * The XML documents will be cached using the Store configured via the cache-role configuration
+ * element. If not specified the default Store as specified in this classes ROLE attribute will
+ * be used.
+ * <p/>
+ * In addition, xpath expressions can be cached for higher performance.
+ * Thus, if an expression has been evaluated for a file, the result
+ * is cached and will be reused, the expression is not evaluated
+ * a second time. This can be turned off using the <code>cache-expressions</code>
+ * configuration option.
+ *
+ * @version $Id: $
+ */
+public class XPathXMLFileModule extends AbstractInputModule
+ implements Serviceable, ThreadSafe
+{
+ public static final String ROLE = Store.ROLE + "/XPathXMLFileTransientStore";
+ /**
+ * Contains all globally registered extension classes and
+ * packages. Thus the lookup and loading of globally registered
+ * extensions is done only once.
+ */
+ protected JXPathHelperConfiguration configuration;
+
+ /**
+ * Static (cocoon.xconf) configuration location, for error reporting
+ */
+ String staticConfLocation;
+
+ /**
+ * Cached documents
+ */
+ private Store cache;
+
+ /**
+ * Determines whether the configured source document should be cached.
+ */
+ private String cacheParm;
+ private Boolean cacheSource;
+
+ /**
+ * Determines whether the configured source document should be reloaded.
+ */
+ private String reloadParm;
+ private Boolean reloadSource;
+
+ /**
+ * Default value for reloadability of sources. Defaults to false.
+ */
+ boolean reloadAll;
+ /**
+ * Default value for cacheability of xpath expressions. Defaults to true.
+ */
+ private boolean cacheExpressions;
+
+ /**
+ * Whether the source needs to be resolved.
+ */
+ private boolean needsResolve;
+
+ /**
+ * Overrides attribute name
+ */
+ protected String parameter;
+
+ /**
+ * Default src
+ */
+ private String src;
+
+ protected SourceResolver resolver;
+ protected ServiceManager manager;
+
+
+ /* (non-Javadoc)
+ * @see org.apache.avalon.framework.service.Serviceable#service(org.apache.avalon.framework.service.ServiceManager)
+ */
+ public void service(ServiceManager manager) throws ServiceException {
+ this.manager = manager;
+ this.resolver = (SourceResolver) manager.lookup(SourceResolver.ROLE);
+ }
+
+ /**
+ * Static (cocoon.xconf) configuration.
+ * Configuration is expected to be of the form:
+ * <...>
+ * <reloadable><b>true</b>|false</reloadable>
+ * <cacheable><b>true</b>|false</cacheable>
+ * <cache-role>org.apache.excalibur.store.Store/TransientStore</cache-role>
+ * <file src="<i>src</i>"/>
+ * ...
+ * </...>
+ * <p/>
+ * The <file/> element specifies a file pattern. Only one
+ * <file> can be specified, however it can contain references to input modules which will be resolved
+ * each time the module is used. The configured <i>src</i> is used if not
+ * overridden via a file parameter in the sitemap.
+ *
+ * @param config a <code>Configuration</code> value, as described above.
+ * @throws org.apache.avalon.framework.configuration.ConfigurationException
+ * if an error occurs
+ */
+ public void configure(Configuration config) throws ConfigurationException {
+ this.configuration = JXPathHelper.setup(config);
+ this.staticConfLocation = config.getLocation();
+ Configuration roleConfig = config.getChild("cache-role", true);
+ boolean cacheAll = config.getChild("cacheable").getValueAsBoolean(true);
+ this.reloadAll = config.getChild("reloadable").getValueAsBoolean(true);
+ String cacheRole = roleConfig.getValue(ROLE);
+
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Using cache " + cacheRole);
+ }
+
+ try {
+ this.cache = (Store) this.manager.lookup(cacheRole);
+ } catch (ServiceException ce) {
+ throw new ConfigurationException("Unable to lookup cache: " + cacheRole, ce);
+ }
+
+ Configuration fileConfig = config.getChild("file");
+
+ this.src = fileConfig.getAttribute("src");
+ this.cacheParm = fileConfig.getAttribute("cacheable", null);
+ this.reloadParm = fileConfig.getAttribute("reloadable", null);
+ if (this.cacheParm == null) {
+ this.cacheSource = Boolean.valueOf(cacheAll);
+ } else if (VariableResolverFactory.needsResolve(this.cacheParm)) {
+ this.cacheSource = null;
+ } else {
+ this.cacheSource = Boolean.valueOf(this.cacheParm);
+ }
+ if (this.reloadParm == null) {
+ this.reloadSource = Boolean.valueOf(this.reloadAll);
+ } else if (VariableResolverFactory.needsResolve(this.reloadParm)) {
+ this.reloadSource = null;
+ } else {
+ this.reloadSource = Boolean.valueOf(this.reloadParm);
+ }
+
+ // init caches
+ this.cacheExpressions = config.getChild("cache-expressions").getValueAsBoolean(true);
+ this.needsResolve = VariableResolverFactory.needsResolve(this.src);
+ }
+
+ /**
+ * Dispose this component
+ */
+ public void dispose() {
+ super.dispose();
+ if (this.manager != null) {
+ this.manager.release(this.resolver);
+ this.manager.release(this.cache);
+ this.resolver = null;
+ this.cache = null;
+ this.manager = null;
+ }
+ }
+
+ public Object getAttribute(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ return getAttribute(name, modeConf, objectModel, false);
+ }
+
+ public Object[] getAttributeValues(String name, Configuration modeConf, Map objectModel)
+ throws ConfigurationException {
+ Object result = getAttribute(name, modeConf, objectModel, true);
+ return (result != null ? (Object[]) result : null);
+ }
+ /**
+ * Get the DocumentInfo for the DOM object that JXPath will operate on when evaluating
+ * attributes. This DOM is loaded from a Source, specified in the
+ * modeConf, or (if modeConf is null) from the
+ * {@link #configure(org.apache.avalon.framework.configuration.Configuration)}.
+ *
+ * @param name The JXPath to retrieve
+ * @param modeConf The dynamic configuration for the current operation. May
+ * be <code>null</code>, in which case static (cocoon.xconf) configuration
+ * is used. Configuration is expected to have a <file> child node, and
+ * be of the form:
+ * <...>
+ * <file src="..." reloadable="true|false"/>
+ * </...>
+ * @param objectModel Object Model for the current module operation.
+ * @param getValues true if multiple values should be retrieve, false otherwise
+ * @return the result of the XPath query into the XML document
+ * @throws ConfigurationException if an error occurs.
+ */
+ private Object getAttribute(String name, Configuration modeConf, Map objectModel, boolean getValues)
+ throws ConfigurationException {
+
+ if (modeConf != null) {
+ name = modeConf.getChild("parameter").getValue(this.parameter != null ? this.parameter : name);
+ }
+
+ boolean hasDynamicConf = false; // whether we have a <file src="..."> dynamic configuration
+ Configuration fileConf = null; // the nested <file>, if any
+
+ if (modeConf != null && modeConf.getChildren().length > 0) {
+ fileConf = modeConf.getChild("file", false);
+ if (fileConf == null) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Missing 'file' child element at " + modeConf.getLocation());
+ }
+ } else {
+ hasDynamicConf = true;
+ }
+ }
+
+ String src = this.src;
+ Boolean cacheSource = this.cacheSource;
+ Boolean reloadSource = this.cacheSource;
+ boolean needsResolve = this.needsResolve;
+ String cacheParm = this.cacheParm;
+ String reloadParm = this.reloadParm;
+
+ if (hasDynamicConf) {
+ src = fileConf.getAttribute("src");
+ cacheParm = fileConf.getAttribute("cacheable", this.cacheParm);
+ reloadParm = fileConf.getAttribute("reloadable", this.reloadParm);
+ if (cacheParm == null) {
+ cacheSource = this.cacheSource;
+ } else if (VariableResolverFactory.needsResolve(cacheParm)) {
+ cacheSource = null;
+ if (cacheSource == null) {
+ try {
+ VariableResolver varResolver = VariableResolverFactory.getResolver(cacheParm, this.manager);
+ cacheSource = Boolean.valueOf(varResolver.resolve(objectModel));
+ } catch (PatternException pe) {
+ throw new ConfigurationException("Error resolving " + cacheParm, pe);
+ }
+ }
+ } else {
+ cacheSource = Boolean.valueOf(cacheParm);
+ }
+ if (reloadParm == null) {
+ reloadSource = this.reloadSource;
+ } else if (VariableResolverFactory.needsResolve(reloadParm)) {
+ reloadSource = null;
+ } else {
+ reloadSource = Boolean.valueOf(reloadParm);
+ }
+ needsResolve = true;
+ }
+ if (cacheSource == null) {
+ try {
+ VariableResolver varResolver = VariableResolverFactory.getResolver(cacheParm, this.manager);
+ cacheSource = Boolean.valueOf(varResolver.resolve(objectModel));
+ } catch (PatternException pe) {
+ throw new ConfigurationException("Error resolving " + cacheParm, pe);
+ }
+ }
+ if (reloadSource == null) {
+ try {
+ VariableResolver varResolver =
+ VariableResolverFactory.getResolver(reloadParm, this.manager);
+ reloadSource = Boolean.valueOf(varResolver.resolve(objectModel));
+ } catch (PatternException pe) {
+ throw new ConfigurationException("Error resolving " + reloadParm, pe);
+ }
+ }
+
+ if (src == null) {
+ throw new ConfigurationException(
+ "No source specified"
+ + (modeConf != null ? ", either dynamically in " + modeConf.getLocation() + ", or " : "")
+ + " statically in "
+ + staticConfLocation);
+ }
+
+ if (needsResolve) {
+ try {
+ VariableResolver varResolver = VariableResolverFactory.getResolver(src, this.manager);
+ src = varResolver.resolve(objectModel);
+ } catch (PatternException pe) {
+ throw new ConfigurationException("Error resolving variables for " + src, pe);
+ }
+ }
+
+ Object result;
+
+ if (cacheSource.booleanValue()) {
+ DocumentInfo info = (DocumentInfo) this.cache.get(src);
+ if (info == null || (reloadSource.booleanValue() && !info.isValid())) {
+ Source docSource = null;
+ try {
+ docSource = resolver.resolveURI(src);
+ DocumentInfo newInfo = new DocumentInfo(src, SourceUtil.toDOM(docSource),
+ docSource.getValidity(), this.cacheExpressions, this.resolver);
+ synchronized(this.cache) {
+ DocumentInfo cachedInfo = (DocumentInfo)this.cache.get(src);
+ if (cachedInfo == null || cachedInfo == info) {
+ this.cache.store(src, newInfo);
+ info = newInfo;
+ } else {
+ info = cachedInfo;
+ }
+ }
+ } catch (MalformedURLException mue) {
+ throw new ConfigurationException("Unable to resolve " + src, mue);
+ } catch (IOException ioe) {
+ throw new ConfigurationException("Unable to access" + src, ioe);
+ } catch (ProcessingException pe) {
+ throw new ConfigurationException("Unable to process " + src, pe);
+ } catch (SAXException se) {
+ throw new ConfigurationException("Error processing XML document " + src, se);
+ } finally {
+ if (docSource != null) {
+ resolver.release(docSource);
+ }
+ }
+ }
+ if (info.cacheExpressions) {
+ Map cache = getValues ? info.expressionValuesCache : info.expressionCache;
+ synchronized (cache) {
+ if (cache.containsKey(name)) {
+ result = cache.get(name);
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("for " + name + " using cached result " + result);
+ }
+ } else {
+ result = getResult(name, info.document, modeConf, getValues);
+ if (result != null) {
+ cache.put(name, result);
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("for " + name + " newly caching result " + result);
+ }
+ } else {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("for " + name + " result is null");
+ }
+ }
+ }
+ }
+ } else {
+ result = getResult(name, info.document, modeConf, getValues);
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("for " + name + " result is " + result);
+ }
+ }
+ } else {
+ Source docSource = null;
+ try {
+ docSource = resolver.resolveURI(src);
+ result = getResult(name, SourceUtil.toDOM(docSource), modeConf, getValues);
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("for " + name + " result is " + result);
+ }
+ } catch (MalformedURLException mue) {
+ throw new ConfigurationException("Unable to resolve " + src, mue);
+ } catch (IOException ioe) {
+ throw new ConfigurationException("Unable to access" + src, ioe);
+ } catch (ProcessingException pe) {
+ throw new ConfigurationException("Unable to process " + src, pe);
+ } catch (SAXException se) {
+ throw new ConfigurationException("Error processing XML document " + src, se);
+ } finally {
+ if (docSource != null) {
+ resolver.release(docSource);
+ }
+ }
+ }
+
+ return result;
+ }
+
+ private Object getResult(String name, Document document, Configuration modeConf, boolean getValues)
+ throws ConfigurationException {
+ Object result;
+
+ if (getValues) {
+ result = JXPathHelper.getAttributeValues(name, modeConf, this.configuration, document);
+ } else {
+ result = JXPathHelper.getAttributeValue(name, modeConf, this.configuration, document);
+ }
+ return result;
+ }
+
+ /**
+ * Used to keep track of the Document, its validity and any cached expressions.
+ */
+ private static class DocumentInfo
+ {
+ public DocumentInfo(String uri, Document doc, SourceValidity validity, boolean cacheExpressions,
+ SourceResolver resolver) {
+ this.cacheExpressions = cacheExpressions;
+ if (cacheExpressions) {
+ expressionCache = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT);
+ expressionValuesCache = new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT);
+ }
+ this.resolver = resolver;
+ this.uri = uri;
+ this.document = doc;
+ this.validity = validity;
+ }
+
+ private boolean cacheExpressions;
+
+ private final String uri;
+
+ private final SourceValidity validity;
+
+ private final SourceResolver resolver;
+
+ /**
+ * Source content cached as DOM Document
+ */
+ private final Document document;
+
+ private Map expressionCache;
+ private Map expressionValuesCache;
+
+ /**
+ * Returns true if the document is valid, false otherwise.
+ * <p/>
+ *
+ * @return returns true if the document is valid, false otherwise.
+ */
+ private boolean isValid() {
+ Source src = null;
+ boolean result = true;
+
+ try {
+ int valid = validity == null ? SourceValidity.INVALID : validity.isValid();
+ if (valid == SourceValidity.UNKNOWN) {
+ // Get new source and validity
+ src = resolver.resolveURI(this.uri);
+ SourceValidity newValidity = src.getValidity();
+ valid = validity.isValid(newValidity);
+ }
+ if (valid != SourceValidity.VALID) {
+ result = false;
+ }
+ }
+ catch (Exception ex) {
+ result = false;
+ }
+ finally {
+ if (src != null) {
+ resolver.release(src);
+ }
+ }
+ return result;
+ }
+ }
+}
\ No newline at end of file
Modified: cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf (original)
+++ cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/cocoon.xconf Thu Dec 27 12:00:45 2007
@@ -228,6 +228,12 @@
<component-instance logger="core.modules.input" name="slashdot" class="org.apache.cocoon.components.modules.input.XMLFileModule">
<file src="http://slashdot.org/slashdot.rss"/>
</component-instance>
+ <component-instance logger="core.modules.input" name="xmyxml" class="org.apache.cocoon.components.modules.input.XPathXMLFileModule">
+ <file src="context://samples/modules/forrestconf.xml"/>
+ </component-instance>
+ <component-instance logger="core.modules.input" name="varxml" class="org.apache.cocoon.components.modules.input.XPathXMLFileModule">
+ <file src="context://samples/modules/{request-param:dir}/forrestconf.xml"/>
+ </component-instance>
<component-instance logger="core.modules.input" name="random-100-to-500" class="org.apache.cocoon.components.modules.input.RandomNumberModule">
<min>100</min>
<max>500</max>
@@ -447,6 +453,19 @@
<transient-store logger="core.store.transient">
<parameter name="maxobjects" value="${transient-store.maxobjects}"/>
</transient-store>
+
+
+ <!--+
+ | Transient Store: holds objects that don't have to survive shutdown
+ |
+ | Common configuration parameters:
+ | maxobjects: Indicates how many objects will be held in the cache.
+ | When the number of maxobjects has been reached. The last object
+ | in the cache will be thrown out.
+ +-->
+ <xmlfile-store logger="core.store.transient">
+ <parameter name="maxobjects" value="${xmlfile-store.maxobjects}"/>
+ </xmlfile-store>
<!--+
| Store: generic store. The default implementation is an in-memory store
Modified: cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/properties/core.properties
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/properties/core.properties?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/properties/core.properties (original)
+++ cocoon/branches/BRANCH_2_1_X/src/webapp/WEB-INF/properties/core.properties Thu Dec 27 12:00:45 2007
@@ -20,6 +20,7 @@
continuations-manager.expirations-check.period = 180000
xml-parser.pool-max = 32
transient-store.maxobjects = 1000
+xmlfile-store.maxobjects = 100
store.maxobjects = 1000
store-janitor.freememory = 2048000
store-janitor.heapsize = 66600000
Added: cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/a/forrestconf.xml
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/a/forrestconf.xml?rev=607115&view=auto
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/a/forrestconf.xml (added)
+++ cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/a/forrestconf.xml Thu Dec 27 12:00:45 2007
@@ -0,0 +1,26 @@
+<?xml version="1.0"?>
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You 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.
+-->
+<!--+
+ | Demo file for XMLFileModule
+ |
+ | SVN $Id: forrestconf.xml 433543 2006-08-22 06:22:54Z crossley $
+ +-->
+<forrestconf version="1.0">
+ <skin>test-site</skin>
+ <base-url>http://localhost:8786/cocoon</base-url>
+</forrestconf>
Modified: cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/menu.xml
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/menu.xml?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/menu.xml (original)
+++ cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/menu.xml Thu Dec 27 12:00:45 2007
@@ -48,6 +48,7 @@
<menu-item label="URLEncodeModule" href="url-encode.html" desc="URLEncode passed parameter"/>
<menu-item label="URLDecodeModule" href="url-decode.html" desc="URLDecode passed parameter"/>
<menu-item label="XMLFileModule" href="xml.html" desc="XML node values"/>
+ <menu-item label="XPathXMLFileModule" href="xxml.html?dir=a" desc="XML node values"/>
</menu>
<menu label="Documentation">
Modified: cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/sitemap.xmap
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/sitemap.xmap?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/sitemap.xmap (original)
+++ cocoon/branches/BRANCH_2_1_X/src/webapp/samples/modules/sitemap.xmap Thu Dec 27 12:00:45 2007
@@ -220,6 +220,28 @@
<map:serialize/>
</map:match>
+ <map:match pattern="content/xxml.xml">
+ <map:generate type="jx" src="properties.xml">
+ <map:parameter name="/forrestconf/@version" value="{xmyxml:/forrestconf/@version}"/>
+ <map:parameter name="/forrestconf/skin" value="{xmyxml:/forrestconf/skin}"/>
+ <map:parameter name="/*/base-url" value="{xmyxml:/*/base-url}"/>
+ <map:parameter name="a/forestconf/@version" value="{varxml:/forrestconf/@version}"/>
+ <map:parameter name="a/forrestconf/skin" value="{varxml:/forrestconf/skin}"/>
+ <map:parameter name="a/*/base-url" value="{varxml:/*/base-url}"/>
+ <!--
+ <map:parameter name="slashdot-headline" value="{slashdot:/*:RDF/item[1]/title}"/>
+ -->
+ </map:generate>
+ <map:transform src="properties2html.xsl">
+ <map:parameter name="title" value="Xpath XML File Input Module (XPathXMLFileModule)"/>
+ <map:parameter name="description" value="XMLFileModule uses an XML
+ file as a data source, with XPath expressions as the key values. In
+ this example, the keys identify nodes in
+ context://samples/modules/forrestconf.xml"/>
+ </map:transform>
+ <map:serialize/>
+ </map:match>
+
<map:match pattern="content/xml.xml">
<map:generate type="jx" src="properties.xml">
<map:parameter name="/forrestconf/@version" value="{myxml:/forrestconf/@version}"/>
Modified: cocoon/branches/BRANCH_2_1_X/status.xml
URL: http://svn.apache.org/viewvc/cocoon/branches/BRANCH_2_1_X/status.xml?rev=607115&r1=607114&r2=607115&view=diff
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/status.xml (original)
+++ cocoon/branches/BRANCH_2_1_X/status.xml Thu Dec 27 12:00:45 2007
@@ -182,6 +182,10 @@
<changes>
<release version="2.1.11" date="TBD">
+ <action dev="RG" type="fix" fixes-bug="COCOON-1574">
+ Created XPathXMLFileModule to address issus with XMLFileModule. XPathXMLFileModule supports variable
+ replacement and caching of documents in ehcache and expressions as soft references.
+ </action>
<action dev="AG" type="fix" fixes-bug="COCOON-2052" due-to="Robin Wyles" due-to-email="rob@robinwyles.com">
Forms: Allow Ajax submission of forms with empty upload field.
</action>