You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by un...@apache.org on 2004/11/11 17:24:45 UTC
svn commit: rev 57444 - in cocoon/branches/BRANCH_2_1_X: . src/blocks/scratchpad/conf src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl
Author: unico
Date: Thu Nov 11 08:24:43 2004
New Revision: 57444
Added:
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/InspectableTraversableCachingSource.java
Modified:
cocoon/branches/BRANCH_2_1_X/blocks.properties
cocoon/branches/BRANCH_2_1_X/gump.xml
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/conf/caching-source.xconf
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSource.java
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSourceFactory.java
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/DelayRefresher.java
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/TraversableCachingSource.java
cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/UpdateTarget.java
cocoon/branches/BRANCH_2_1_X/status.xml
Log:
port changes to CachingSource from trunk
Modified: cocoon/branches/BRANCH_2_1_X/blocks.properties
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/blocks.properties (original)
+++ cocoon/branches/BRANCH_2_1_X/blocks.properties Thu Nov 11 08:24:43 2004
@@ -59,7 +59,7 @@
#-----[dependency]: "fop" is needed by "tour".
#include.block.fop=false
#-----[dependency]: "hsqldb" depends on "databases".
-#-----[dependency]: "hsqldb" is needed by "jms", "petstore".
+#-----[dependency]: "hsqldb" is needed by "jms", "ojb", "petstore".
#include.block.hsqldb=false
#-----[dependency]: "html" is needed by "portal".
#include.block.html=false
@@ -82,7 +82,7 @@
#-----[dependency]: "session-fw" depends on "xsp".
#-----[dependency]: "session-fw" is needed by "authentication-fw", "portal", "portal-fw".
#include.block.session-fw=false
-#-----[dependency]: "velocity" is needed by "petstore", "scratchpad".
+#-----[dependency]: "velocity" is needed by "petstore".
#include.block.velocity=false
#include.block.web3=false
#-----[dependency]: "xmldb" depends on "databases".
@@ -110,10 +110,9 @@
#include.block.cron=false
#include.block.deli=false
#-----[dependency]: "eventcache" depends on "jms", "xsp" (for samples).
-#-----[dependency]: "eventcache" is needed by "repository".
+#-----[dependency]: "eventcache" is needed by "repository", "scratchpad".
#include.block.eventcache=false
#-----[dependency]: "faces" depends on "portal", "taglib".
-#-----[dependency]: "faces" is needed by "scratchpad".
#include.block.faces=false
#-----[dependency]: "forms" depends on "xsp" (for samples).
#-----[dependency]: "forms" is needed by "apples", "javaflow", "lucene", "ojb", "petstore", "tour".
@@ -124,10 +123,10 @@
#-----[dependency]: "jms" is needed by "eventcache", "slide".
#include.block.jms=false
#include.block.linotype=false
-#-----[dependency]: "mail" depends on "asciiart", "scratchpad".
+#-----[dependency]: "mail" depends on "asciiart".
#include.block.mail=false
#include.block.midi=false
-#-----[dependency]: "ojb" depends on "databases", "forms" (for samples).
+#-----[dependency]: "ojb" depends on "databases" (for samples), "forms" (for samples), "hsqldb" (for samples).
#-----[dependency]: "ojb" is needed by "javaflow".
#include.block.ojb=false
#-----[dependency]: "petstore" depends on "databases", "forms", "hsqldb", "velocity".
@@ -135,10 +134,9 @@
#include.block.proxy=false
#include.block.qdox=false
#-----[dependency]: "repository" depends on "databases", "eventcache".
-#-----[dependency]: "repository" is needed by "slide", "webdav".
+#-----[dependency]: "repository" is needed by "scratchpad", "slide", "webdav".
#include.block.repository=false
-#-----[dependency]: "scratchpad" depends on "axis", "batik" (for samples), "cron", "faces", "velocity", "xsp".
-#-----[dependency]: "scratchpad" is needed by "mail".
+#-----[dependency]: "scratchpad" depends on "axis", "batik" (for samples), "cron", "eventcache", "repository", "xsp".
#include.block.scratchpad=false
#include.block.serializers=false
#-----[dependency]: "slide" depends on "jms", "repository".
Modified: cocoon/branches/BRANCH_2_1_X/gump.xml
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/gump.xml (original)
+++ cocoon/branches/BRANCH_2_1_X/gump.xml Thu Nov 11 08:24:43 2004
@@ -155,6 +155,8 @@
<depend project="cocoon-block-axis"/>
<depend project="cocoon-block-batik" type="samples"/>
<depend project="cocoon-block-cron"/>
+ <depend project="cocoon-block-eventcache"/>
+ <depend project="cocoon-block-repository"/>
<depend project="cocoon-block-xsp"/>
<depend project="apache-garbage"/>
Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/conf/caching-source.xconf
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/conf/caching-source.xconf (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/conf/caching-source.xconf Thu Nov 11 08:24:43 2004
@@ -42,7 +42,7 @@
| A Refresher is used when asynchronic caching is turned on. It is responsible for
| updating the cached contents in the background.
| - 'default-expires' (-1). The expires value if it is not specified on the source
- | itself.
+ | itself. The default value of -1 means to never expire.
+-->
<component-instance name="cached"
class="org.apache.cocoon.components.source.impl.CachingSourceFactory"
@@ -50,9 +50,9 @@
<!--
<parameter name="async" value="true"/>
<parameter name="cache-role" value="org.apache.cocoon.caching.Cache"/>
- <parameter name="refresher-role" value="org.apache.cocoon.components.source.impl.Refresher/Delay"/>
<parameter name="default-expires" value="-1"/>
-->
+ <parameter name="refresher-role" value="org.apache.cocoon.components.source.impl.Refresher/Delay"/>
</component-instance>
</xconf>
Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSource.java
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSource.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSource.java Thu Nov 11 08:24:43 2004
@@ -1,12 +1,12 @@
/*
* Copyright 1999-2004 The Apache Software Foundation.
- *
+ *
* 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.
@@ -20,19 +20,19 @@
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
-import java.util.Collection;
-import java.util.Iterator;
import org.apache.avalon.framework.activity.Initializable;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
-import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.service.ServiceException;
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.CascadingIOException;
import org.apache.cocoon.ProcessingException;
import org.apache.cocoon.caching.Cache;
+import org.apache.cocoon.caching.EventAware;
import org.apache.cocoon.caching.IdentifierCacheKey;
+import org.apache.cocoon.caching.validity.EventValidity;
+import org.apache.cocoon.caching.validity.NamedEvent;
import org.apache.cocoon.components.sax.XMLDeserializer;
import org.apache.cocoon.components.sax.XMLSerializer;
import org.apache.cocoon.xml.ContentHandlerWrapper;
@@ -40,9 +40,7 @@
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceException;
import org.apache.excalibur.source.SourceNotFoundException;
-import org.apache.excalibur.source.SourceResolver;
import org.apache.excalibur.source.SourceValidity;
-import org.apache.excalibur.source.TraversableSource;
import org.apache.excalibur.source.impl.validity.ExpiresValidity;
import org.apache.excalibur.source.impl.validity.TimeStampValidity;
import org.apache.excalibur.xml.sax.XMLizable;
@@ -54,7 +52,7 @@
* This class implements a proxy like source that uses another source
* to get the content. This implementation can cache the content for
* a given period of time
- *
+ *
* <h2>Syntax for Protocol</h2>
* <p>
* cached:http://www.apache.org/[?cocoon:cache-expires=60&cocoon:cache-name=main]
@@ -62,95 +60,95 @@
* <p>
* The above examples show how the real source <code>http://www.apache.org</code>
* is wrapped and the cached contents is used for <code>60</code> seconds.
- * The second querystring parameter instructs that the cache key be extended with the string
+ * The second querystring parameter instructs that the cache key be extended with the string
* <code>main</code>. This allows the use of multiple cache entries for the same source.
* </p>
* <p>
- * The value of the expires parameter holds some additional semantics.
+ * The value of the expires parameter holds some additional semantics.
* Specifying <code>-1</code> will yield the cached response to be considered valid
- * always. <code>0</code> can be used to achieve the exact opposite. That is to say,
+ * always. <code>0</code> can be used to achieve the exact opposite. That is to say,
* the cached contents will be thrown out and updated immediately and unconditionally.
* <p>
- * @version CVS $Id: CachingSource.java,v 1.11 2004/04/15 08:05:56 cziegeler Exp $
+ * @version CVS $Id$
*/
public class CachingSource extends AbstractLogEnabled
implements Source, Serviceable, Initializable, XMLizable {
+
+ // ---------------------------------------------------- Constants
- /** The ServiceManager */
- protected ServiceManager manager;
+ public static final String CACHE_EXPIRES_PARAM = "cache-expires";
+ public static final String CACHE_NAME_PARAM = "cache-name";
- /** The SourceResolver to resolve the wrapped Source */
- protected SourceResolver resolver;
+ // ---------------------------------------------------- Instance variables
+ /** The ServiceManager */
+ protected ServiceManager manager;
+
/** The current cache */
protected Cache cache;
-
- /** The refresher for asynchronous updates */
- protected Refresher refresher;
-
+
/** The source object for the real content */
protected Source source;
-
+
/** The cached response (if any) */
protected CachedSourceResponse response;
-
+
/** Did we just update meta info? */
protected boolean freshMeta;
-
+
/** The full location string */
final protected String uri;
-
+
/** The used protocol */
final protected String protocol;
-
+
/** The key used in the store */
final protected IdentifierCacheKey cacheKey;
/** number of seconds before cached object becomes invalid */
final protected int expires;
- /** Parameters */
- final protected Parameters parameters;
+ /** cache key extension */
+ final protected String cacheName;
/** asynchronic refresh strategy ? */
final protected boolean async;
-
+
/**
* Construct a new object.
*/
public CachingSource(final String protocol,
final String uri,
final Source source,
- final Parameters parameters,
final int expires,
+ final String cacheName,
final boolean async) {
this.protocol = protocol;
this.uri = uri;
this.source = source;
this.expires = expires;
+ this.cacheName = cacheName;
this.async = async;
- this.parameters = parameters;
String key = "source:" + source.getURI();
- String cacheName = parameters.getParameter("cache-name", null);
if (cacheName != null) {
key += ":" + cacheName;
}
this.cacheKey = new IdentifierCacheKey(key, false);
}
-
+
/**
* Set the ServiceManager.
*/
public void service(final ServiceManager manager) throws ServiceException {
this.manager = manager;
}
-
+
/**
* Initialize the Source.
*/
public void initialize() throws Exception {
-
+
boolean checkValidity = true;
if (this.expires == -1) {
if (getLogger().isDebugEnabled()) {
@@ -158,115 +156,75 @@
}
checkValidity = false;
}
-
+
if (this.async && this.expires != 0) {
if (getLogger().isDebugEnabled()) {
- getLogger().debug("Not invalidating cached response " +
"for asynch source " + getSourceURI());
+ getLogger().debug("Using cached response if available.");
}
checkValidity = false;
}
this.response = (CachedSourceResponse) this.cache.get(this.cacheKey);
+
if (this.response == null) {
if (getLogger().isDebugEnabled()) {
- getLogger().debug("No cached response found " +
"for source " + getSourceURI());
+ getLogger().debug("No cached response found.");
}
checkValidity = false;
}
-
- if (checkValidity) {
-
- final ExpiresValidity cacheValidity = (ExpiresValidity) this.response.getValidityObjects()[0];
- final SourceValidity sourceValidity = this.response.getValidityObjects()[1];
-
- boolean remove = false;
- if (this.expires == 0) {
- if (getLogger().isDebugEnabled()) {
- getLogger().debug("Force invalidation of cached response" +
" of source " + getSourceURI());
- }
- remove = true;
- }
- else {
- boolean expired = cacheValidity.isValid() != SourceValidity.VALID;
- if (expired) {
- if (getLogger().isDebugEnabled()) {
- getLogger().debug("Cached response of source "
- + getSourceURI() + " is expired.");
- }
- boolean invalid = !isValid(sourceValidity, this.source);
- if (invalid) {
- if (getLogger().isDebugEnabled()) {
- getLogger().debug("Cached response of source "
- + getSourceURI() + " is invalid.");
- }
- remove = true;
- }
- else {
- if (getLogger().isDebugEnabled()) {
- getLogger().debug("Cached response of source "
- + getSourceURI() + " is still valid.");
- }
- // set new expiration period
- this.response.getValidityObjects()[0] = new ExpiresValidity(getExpiration());
- }
- }
- }
-
- if (remove) {
- this.response = null;
- // remove it if it no longer exists
- if (!exists()) {
- this.cache.remove(this.cacheKey);
- }
+
+ if (this.expires == 0) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Not using cached response.");
}
+ this.response = null;
+ checkValidity = false;
}
- if (this.async && this.expires > 0) {
- // schedule it with the refresher
- this.refresher.refresh(this.cacheKey,
- getSourceURI(),
- this.parameters.getParameter("cache-role", null),
- this.parameters);
+
+ if (checkValidity && !checkValidity()) {
+ this.response = null;
+ // remove it if it no longer exists
+ if (!this.source.exists()) {
+ remove();
+ }
}
+
}
-
+
/**
* Cleanup.
*/
public void dispose() {
- if (this.source != null) {
- this.resolver.release(this.source);
- this.source = null;
- }
+ this.source = null;
this.manager = null;
- this.resolver = null;
this.cache = null;
}
-
+
/**
* Initialize the cached response with meta info.
- *
+ *
* @throws IOException if an the binary response could not be initialized
*/
protected void initMetaResponse() throws IOException {
boolean storeResponse = false;
CachedSourceResponse response = this.response;
if (response == null) {
- if (this.expires != 0) {
- final SourceValidity cacheValidity = new ExpiresValidity(getExpiration());
- final SourceValidity sourceValidity = source.getValidity();
- response = new CachedSourceResponse(new SourceValidity[] {cacheValidity, sourceValidity});
- storeResponse = true;
+ SourceValidity[] validities;
+ if (this.cache instanceof EventAware) {
+ validities = new SourceValidity[] { new EventValidity(new NamedEvent(this.source.getURI())) };
}
else {
- response = new CachedSourceResponse(null);
+ validities = new SourceValidity[] { new ExpiresValidity(getExpiration()), source.getValidity() };
}
+ response = new CachedSourceResponse(validities);
+ storeResponse = true;
}
if (response.getExtra() == null) {
response.setExtra(readMeta(this.source));
this.freshMeta = true;
}
+ this.response = response;
if (storeResponse) {
- this.response = response;
try {
this.cache.store(this.cacheKey, this.response);
}
@@ -275,10 +233,10 @@
}
}
}
-
+
/**
* Initialize the cached response with binary contents.
- *
+ *
* @throws IOException if an the binary response could not be initialized
*/
protected void initBinaryResponse() throws IOException {
@@ -286,25 +244,19 @@
/* delay caching the response until we have a valid new one */
CachedSourceResponse response = this.response;
if (response == null) {
- if (this.expires != 0) {
- final SourceValidity cacheValidity = new ExpiresValidity(getExpiration());
- final SourceValidity sourceValidity = source.getValidity();
- response = new CachedSourceResponse(new SourceValidity[] {cacheValidity, sourceValidity});
- storeResponse = true;
- }
- else {
- response = new CachedSourceResponse(null);
- }
+ response = new CachedSourceResponse(new SourceValidity[] { new ExpiresValidity(getExpiration()), source.getValidity()});
+ storeResponse = true;
}
if (response.getBinaryResponse() == null) {
response.setBinaryResponse(readBinaryResponse(this.source));
if (!this.freshMeta) {
/* always refresh meta in this case */
response.setExtra(readMeta(this.source));
+ this.freshMeta = true;
}
}
+ this.response = response;
if (storeResponse) {
- this.response = response;
try {
this.cache.store(this.cacheKey, this.response);
}
@@ -313,10 +265,10 @@
}
}
}
-
+
/**
* Initialize the cached response with XML contents.
- *
+ *
* @param refresh whether to force refresh.
* @throws SAXException if something happened during xml processing
* @throws IOException if an IO level error occured
@@ -327,15 +279,8 @@
/* delay caching the response until we have a valid new one */
CachedSourceResponse response = this.response;
if (response == null) {
- if (this.expires != 0) {
- final SourceValidity cacheValidity = new ExpiresValidity(getExpiration());
- final SourceValidity sourceValidity = source.getValidity();
- response = new CachedSourceResponse(new SourceValidity[] {cacheValidity, sourceValidity});
- storeResponse = true;
- }
- else {
- response = new CachedSourceResponse(null);
- }
+ response = new CachedSourceResponse(new SourceValidity[] { new ExpiresValidity(getExpiration()), source.getValidity() });
+ storeResponse = true;
}
if (response.getXMLResponse() == null || refresh) {
byte[] binary = response.getBinaryResponse();
@@ -343,10 +288,11 @@
if (!this.freshMeta) {
/* always refresh meta in this case */
response.setExtra(readMeta(this.source));
+ this.freshMeta = true;
}
}
+ this.response = response;
if (storeResponse) {
- this.response = response;
try {
this.cache.store(this.cacheKey, this.response);
}
@@ -355,9 +301,9 @@
}
}
}
-
+
// ---------------------------------------------------- Source implementation
-
+
/**
* Return the protocol identifier.
*/
@@ -400,7 +346,7 @@
}
return ((SourceMeta) this.response.getExtra()).getMimeType();
}
-
+
/**
* Return an <code>InputStream</code> object to read from the source.
*/
@@ -424,9 +370,9 @@
* @see org.apache.excalibur.source.Source#exists()
*/
public boolean exists() {
- return this.source.exists();
+ return this.source.exists();
}
-
+
/**
* Get the Validity object. This can either wrap the last modification
* date or the expires information or...
@@ -440,18 +386,62 @@
}
return null;
}
-
+
/**
* Refresh this object and update the last modified date
- * and content length.
+ * and content length. This method will try to refresh the
+ * cached contents.
*/
public void refresh() {
- this.response = null;
this.source.refresh();
+ if (response != null && checkValidity()) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Cached response is still valid for source " + this.uri + ".");
+ }
+ }
+ else {
+ if (this.source.exists()) {
+ CachedSourceResponse response = this.response;
+ try {
+ if (response == null) {
+ // create a new cached response
+ response = new CachedSourceResponse(new SourceValidity[] {
+ new ExpiresValidity(getExpiration()), source.getValidity()});
+ }
+ // only create objects that are cached
+ if (response.getBinaryResponse() != null) {
+ response.setBinaryResponse(readBinaryResponse(source));
+ }
+ if (response.getXMLResponse() != null) {
+ response.setXMLResponse(readXMLResponse(
+ source, response.getBinaryResponse(), this.manager));
+ }
+ // always refresh meta data
+ response.setExtra(readMeta(source));
+ this.response = response;
+ cache.store(this.cacheKey, response);
+ }
+ catch (Exception e) {
+ getLogger().warn("Error refreshing source " + this.uri +
+ "Cached response (if any) may be stale.", e);
+ }
+ }
+ else if (this.response != null) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Source " + this.uri + " no longer exists." +
+ " Throwing out cached response.");
+ }
+ remove();
+ }
+ }
}
- // ---------------------------------------------------- XMLizable implementation
+ protected void remove() {
+ this.cache.remove(this.cacheKey);
+ }
+ // ---------------------------------------------------- XMLizable implementation
+
/**
* Generates SAX events representing the object's state.
*/
@@ -476,46 +466,46 @@
this.manager.release(deserializer);
}
}
-
+
// ---------------------------------------------------- CachingSource specific accessors
-
+
/**
* Return the uri of the cached source.
*/
protected String getSourceURI() {
return this.source.getURI();
}
-
+
/**
* Return the used key.
*/
protected IdentifierCacheKey getCacheKey() {
return this.cacheKey;
}
-
+
/**
* Expires (in milli-seconds)
*/
protected long getExpiration() {
return this.expires * 1000;
}
-
+
/**
* Read XML content from source.
- *
- * @return content from source
- * @throws SAXException
- * @throws IOException
- * @throws CascadingIOException
- */
- protected static byte[] readXMLResponse(Source source, byte[] binary, ServiceManager manager)
+ *
+ * @return content from source
+ * @throws SAXException
+ * @throws IOException
+ * @throws CascadingIOException
+ */
+ protected byte[] readXMLResponse(Source source, byte[] binary, ServiceManager manager)
throws SAXException, IOException, CascadingIOException {
XMLSerializer serializer = null;
XMLizer xmlizer = null;
byte[] result = null;
- try {
- serializer = (XMLSerializer) manager.lookup(XMLSerializer.ROLE);
-
+ try {
+ serializer = (XMLSerializer) manager.lookup(XMLSerializer.ROLE);
+
if (source instanceof XMLizable) {
((XMLizable) source).toSAX(serializer);
}
@@ -532,75 +522,54 @@
serializer);
}
}
- result = (byte[]) serializer.getSAXFragment();
- } catch (ServiceException se) {
- throw new CascadingIOException("Missing service dependency.", se);
- } finally {
+ result = (byte[]) serializer.getSAXFragment();
+ } catch (ServiceException se) {
+ throw new CascadingIOException("Missing service dependency.", se);
+ } finally {
manager.release(xmlizer);
- manager.release(serializer);
- }
- return result;
- }
+ manager.release(serializer);
+ }
+ return result;
+ }
- /**
+ /**
* Read binary content from source.
- *
- * @return content from source
- * @throws IOException
- * @throws SourceNotFoundException
- */
- protected static byte[] readBinaryResponse(Source source)
+ *
+ * @return content from source
+ * @throws IOException
+ * @throws SourceNotFoundException
+ */
+ protected byte[] readBinaryResponse(Source source)
throws IOException, SourceNotFoundException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
- final byte[] buffer = new byte[2048];
- final InputStream inputStream = source.getInputStream();
- int length;
- while ((length = inputStream.read(buffer)) > -1) {
- baos.write(buffer, 0, length);
- }
- baos.flush();
- inputStream.close();
- return baos.toByteArray();
- }
-
+ final byte[] buffer = new byte[2048];
+ final InputStream inputStream = source.getInputStream();
+ int length;
+ while ((length = inputStream.read(buffer)) > -1) {
+ baos.write(buffer, 0, length);
+ }
+ baos.flush();
+ inputStream.close();
+ return baos.toByteArray();
+ }
+
/**
* Read meta data from source.
- *
+ *
* @return source meta data
* @throws IOException
*/
- protected static SourceMeta readMeta(Source source) throws IOException {
- SourceMeta meta;
-
- if (source instanceof TraversableSource) {
-
- final TraversableSourceMeta tmeta = new TraversableSourceMeta();
- final TraversableSource tsource = (TraversableSource) source;
-
- tmeta.setName(tsource.getName());
- tmeta.setIsCollection(tsource.isCollection());
-
- if (tmeta.isCollection()) {
- final Collection children = tsource.getChildren();
- if (children != null) {
- final String[] names = new String[children.size()];
- final Iterator iter = children.iterator();
- int count = 0;
- while(iter.hasNext()) {
- TraversableSource child = (TraversableSource) iter.next();
- names[count] = child.getName();
- count++;
- }
- tmeta.setChildren(names);
- }
- }
-
- meta = tmeta;
- }
- else {
- meta = new SourceMeta();
- }
-
+ protected final SourceMeta readMeta(Source source) throws IOException {
+ SourceMeta meta = createMeta();
+ initMeta(meta, source);
+ return meta;
+ }
+
+ protected SourceMeta createMeta() {
+ return new SourceMeta();
+ }
+
+ protected void initMeta(SourceMeta meta, Source source) throws IOException {
final long lastModified = source.getLastModified();
if (lastModified > 0) {
meta.setLastModified(lastModified);
@@ -609,80 +578,89 @@
meta.setLastModified(System.currentTimeMillis());
}
meta.setMimeType(source.getMimeType());
+ }
+
+ private boolean checkValidity() {
+ if (this.response == null) return false;
- return meta;
+ final SourceValidity[] validities = this.response.getValidityObjects();
+ boolean valid = true;
+ if (validities.length == 2) {
+ final ExpiresValidity expiresValidity = (ExpiresValidity) validities[0];
+ final SourceValidity sourceValidity = validities[1];
+
+ if (expiresValidity.isValid() != SourceValidity.VALID) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Cached response of source " + getSourceURI() + " is expired.");
+ }
+ if (!isValid(sourceValidity, source.getValidity())) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Cached response of source " + getSourceURI() + " is invalid.");
+ }
+ valid = false;
+ }
+ else {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Cached response of source " + getSourceURI() + " is still valid.");
+ }
+ // set new expiration period
+ this.response.getValidityObjects()[0] = new ExpiresValidity(getExpiration());
+ }
+ }
+ else {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Cached response of source " + getSourceURI() + " is NOT expired.");
+ }
+ }
+ }
+ else {
+ // assert(validities.length == 1 && validities[0] instanceof EventValidity)
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Cached response of source does not expire");
+ }
+ }
+ return valid;
}
- protected static boolean isValid(SourceValidity validity, Source source) {
- if (validity == null) return false;
- return validity.isValid() == SourceValidity.VALID ||
- (validity.isValid() == SourceValidity.UNKNOWN &&
- validity.isValid(source.getValidity()) == SourceValidity.VALID);
+ private static boolean isValid(SourceValidity oldValidity, SourceValidity newValidity) {
+ return (oldValidity.isValid() == SourceValidity.VALID ||
+ (oldValidity.isValid() == SourceValidity.UNKNOWN &&
+ oldValidity.isValid(newValidity) == SourceValidity.VALID));
}
-
+
/**
* Data holder for caching Source meta info.
*/
protected static class SourceMeta implements Serializable {
-
+
private String m_mimeType;
private long m_lastModified;
private boolean m_exists;
-
+
protected String getMimeType() {
return m_mimeType;
}
-
+
protected void setMimeType(String mimeType) {
m_mimeType = mimeType;
}
-
+
protected long getLastModified() {
return m_lastModified;
}
-
+
protected void setLastModified(long lastModified) {
m_lastModified = lastModified;
}
-
+
protected boolean exists() {
return m_exists;
}
-
+
protected void setExists(boolean exists) {
m_exists = exists;
}
-
- }
-
- protected static class TraversableSourceMeta extends SourceMeta {
- private String m_name;
- private boolean m_isCollection;
- private String[] m_children;
-
- protected String getName() {
- return m_name;
- }
-
- protected void setName(String name) {
- m_name = name;
- }
-
- protected boolean isCollection() {
- return m_isCollection;
- }
-
- protected void setIsCollection(boolean isCollection) {
- m_isCollection = isCollection;
- }
-
- protected String[] getChildren() {
- return m_children;
- }
-
- protected void setChildren(String[] children) {
- m_children = children;
- }
+
}
}
Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSourceFactory.java
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSourceFactory.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSourceFactory.java Thu Nov 11 08:24:43 2004
@@ -1,12 +1,12 @@
/*
* Copyright 1999-2004 The Apache Software Foundation.
- *
+ *
* 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.
@@ -26,12 +26,14 @@
import org.apache.avalon.framework.configuration.ConfigurationException;
import org.apache.avalon.framework.container.ContainerUtil;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
+import org.apache.avalon.framework.logger.Logger;
import org.apache.avalon.framework.parameters.Parameters;
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.cocoon.caching.Cache;
+import org.apache.cocoon.components.source.InspectableSource;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceException;
import org.apache.excalibur.source.SourceFactory;
@@ -43,36 +45,36 @@
/**
* This class implements a proxy like source caches the contents of the source
- * it wraps. This implementation can cache the content either
+ * it wraps. This implementation can cache the content either
* for a given period of time or until an external event invalidates
* the cached response.
* <p>
* When using the timeout approach you have a choice between two separate
* revalidation strategies.
* </p>
- * 1) Synchronously. This means that the cached contents are checked for validity
+ * 1) Synchronously. This means that the cached contents are checked for validity
* and thrown out on the current thread.<br>
* 2) Asynchronously. A cronjob is scheduled to invalidate and update the cached response
* in the backgound.<br><br>
- *
+ *
* <h2>Protocol syntax</h2>
* <p>
* The URL needs to contain the URL of the cached source, an expiration
- * period in seconds, and optionally a cache key:
+ * period in seconds, and optionally a cache key:
* <code>cached:http://www.apache.org/[?cocoon:cache-expires=60][&cocoon:cache-name=main]</code>.
* </p>
* <p>
* The above examples shows how the real source <code>http://www.apache.org/</code>
* is wrapped and the cached contents is used for <code>60</code> seconds.
- * The second querystring parameter instructs that the cache key be extended with the string
+ * The second querystring parameter instructs that the cache key be extended with the string
* <code>main</code>. This allows the use of multiple cache entries for the same source.
* </p>
* <p>
- * This factory creates either instances of {@link org.apache.cocoon.components.source.impl.CachingSource}
+ * This factory creates either instances of {@link org.apache.cocoon.components.source.impl.CachingSource}
* or {@link org.apache.cocoon.components.source.impl.TraversableCachingSource}
* depending on the whether the wrapped Source is an instance of TraversableSource.
* </p>
- *
+ *
* <h2>Parameters</h2>
* <table><tbody>
* <tr>
@@ -104,29 +106,39 @@
* <td><code>-1</code></td>
* </tr>
* </tbody></table>
- *
- * @version CVS $Id: CachingSourceFactory.java,v 1.10 2004/05/07 17:32:59 joerg Exp $
+ *
+ * @version CVS $Id$
* @since 2.1.1
*/
public final class CachingSourceFactory extends AbstractLogEnabled
implements SourceFactory, URIAbsolutizer, Serviceable, Configurable, Disposable, ThreadSafe
{
+ // ---------------------------------------------------- Constants
+
+ public static final String ASYNC_PARAM = "async";
+ public static final String FAILSAFE_PARAM = "failsafe";
+ public static final String CACHE_ROLE_PARAM = "cache-role";
+ public static final String REFRESHER_ROLE_PARAM = "refresher-role";
+ public static final String DEFAULT_EXPIRES_PARAM = "default-expires";
+
+ // ---------------------------------------------------- Instance variables
+
/** Protocol prefix / factory name */
private String scheme;
-
+
/** Asynchronous ? */
private boolean async;
-
+
/** The role of the cache */
private String cacheRole;
-
+
/** The role of the refresher */
private String refresherRole;
-
+
/** Default expires value */
private int defaultExpires;
-
+
/** Has the lazy initialization been done? */
private boolean isInitialized;
@@ -135,53 +147,53 @@
/** The {@link SourceResolver} */
protected SourceResolver resolver;
-
+
/** The refresher */
protected Refresher refresher;
-
+
/** The cache */
protected Cache cache;
-
+
// ---------------------------------------------------- Lifecycle
-
+
public CachingSourceFactory() {
}
-
- public void service(ServiceManager manager) throws ServiceException {
+
+ public void service(ServiceManager manager) {
this.manager = manager;
- // due to cyclic dependencies we can't lookup the resolver the refresher
- // or the cache until after the factory is initialized.
+ // Due to cyclic dependencies we can't lookup the resolver,
+ // the refresher or the cache until after the factory is
+ // initialized.
}
-
+
public void configure(Configuration configuration) throws ConfigurationException {
this.scheme = configuration.getAttribute("name");
Parameters parameters = Parameters.fromConfiguration(configuration);
+
+ // 'async' parameter
+ this.async = parameters.getParameterAsBoolean(ASYNC_PARAM, false);
- // 'async' parameter
- this.async = parameters.getParameterAsBoolean("async", false);
-
// 'cache-role' parameter
- this.cacheRole = parameters.getParameter("cache-role", Cache.ROLE);
+ this.cacheRole = parameters.getParameter(CACHE_ROLE_PARAM, Cache.ROLE);
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("Using cache " + this.cacheRole);
}
-
+
// 'refresher-role' parameter
if (this.async) {
- this.refresherRole = parameters.getParameter("refresher-role", Refresher.ROLE);
+ this.refresherRole = parameters.getParameter(REFRESHER_ROLE_PARAM, Refresher.ROLE);
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("Using refresher " + this.refresherRole);
}
}
-
- this.defaultExpires = parameters.getParameterAsInteger("default-expires",-1);
-
+
+ this.defaultExpires = parameters.getParameterAsInteger(DEFAULT_EXPIRES_PARAM, -1);
}
-
+
/**
* Lazy initialization of resolver and refresher because of
* cyclic dependencies.
- *
+ *
* @throws SourceException
*/
private synchronized void lazyInitialize() throws SourceException {
@@ -214,7 +226,7 @@
}
this.isInitialized = true;
}
-
+
/* (non-Javadoc)
* @see org.apache.avalon.framework.activity.Disposable#dispose()
*/
@@ -227,32 +239,32 @@
this.resolver = null;
}
}
-
+
// ---------------------------------------------------- SourceFactory implementation
-
+
/**
* Get a <code>Source</code> object.
* @param parameters This is optional.
*/
public Source getSource(final String location, final Map parameters)
throws MalformedURLException, IOException {
-
+
if (this.getLogger().isDebugEnabled() ) {
this.getLogger().debug("Creating source object for " + location);
}
-
+
// we must do lazy initialization because of cyclic dependencies
if (!this.isInitialized) {
lazyInitialize();
}
-
+
// snip the cache protocol
int index = location.indexOf(':');
if (index == -1) {
throw new MalformedURLException("This Source requires a subprotocol to be specified.");
}
String uri = location.substring(index+1);
-
+
// parse the query string
SourceParameters sp = null;
String queryString = null;
@@ -262,7 +274,7 @@
uri = uri.substring(0,index);
sp = new SourceParameters(queryString);
}
-
+
// put caching source specific query string parameters
// into a Parameters object
final Parameters params = new Parameters();
@@ -280,52 +292,92 @@
uri += "?" + queryString;
}
}
+
+ int expires = params.getParameterAsInteger(CachingSource.CACHE_EXPIRES_PARAM, this.defaultExpires);
+ String cacheName = params.getParameter(CachingSource.CACHE_NAME_PARAM, null);
+
+ final CachingSource source = newCachingSource(this.resolver.resolveURI(uri),
+ this.scheme,
+ location,
+ expires,
+ cacheName,
+ this.async,
+ this.cache,
+ getLogger(),
+ manager);
- int expires = params.getParameterAsInteger("cache-expires", -1);
- if (expires == -1) {
- expires = this.defaultExpires;
- params.setParameter("cache-expires", String.valueOf(this.defaultExpires));
+ if (this.async && expires > 0) {
+
+ params.setParameter(CachingSource.CACHE_EXPIRES_PARAM, String.valueOf(expires));
+ params.setParameter(CachingSource.CACHE_NAME_PARAM, cacheName);
+ params.setParameter(CACHE_ROLE_PARAM, this.cacheRole);
+
+ // schedule it with the refresher
+ this.refresher.refresh(source.getCacheKey(),
+ source.getSourceURI(),
+ this.cacheRole,
+ params);
}
- params.setParameter("cache-role", this.cacheRole);
- final Source wrappedSource = this.resolver.resolveURI(uri);
+ return source;
+ }
+
+ /**
+ * Factory method for creating a new CachingSource.
+ */
+ public static CachingSource newCachingSource(Source wrappedSource,
+ String scheme,
+ String uri,
+ int expires,
+ String cacheName,
+ boolean async,
+ Cache cache,
+ Logger logger,
+ ServiceManager manager)
+ throws SourceException {
+
CachingSource source;
if (wrappedSource instanceof TraversableSource) {
- source = new TraversableCachingSource(scheme,
- location,
- (TraversableSource) wrappedSource,
- params,
- expires,
- this.async);
- }
- else {
+ if (wrappedSource instanceof InspectableSource) {
+ source = new InspectableTraversableCachingSource(scheme,
+ uri,
+ (InspectableSource) wrappedSource,
+ expires,
+ cacheName,
+ async);
+ } else {
+ source = new TraversableCachingSource(scheme,
+ uri,
+ (TraversableSource) wrappedSource,
+ expires,
+ cacheName,
+ async);
+ }
+ } else {
source = new CachingSource(scheme,
- location,
+ uri,
wrappedSource,
- params,
expires,
- this.async);
+ cacheName,
+ async);
}
// set the required components directly for speed
- source.cache = this.cache;
- source.resolver = this.resolver;
- source.refresher = this.refresher;
+ source.cache = cache;
- ContainerUtil.enableLogging(source, this.getLogger());
+ ContainerUtil.enableLogging(source, logger);
try {
// call selected avalon lifecycle interfaces. Mmmh.
- ContainerUtil.service(source, this.manager);
+ ContainerUtil.service(source, manager);
ContainerUtil.initialize(source);
} catch (ServiceException se) {
throw new SourceException("Unable to initialize source.", se);
} catch (Exception e) {
throw new SourceException("Unable to initialize source.", e);
}
-
return source;
}
-
+
/**
* Release a {@link Source} object.
*/
@@ -334,11 +386,12 @@
if (this.getLogger().isDebugEnabled() ) {
this.getLogger().debug("Releasing source " + source.getURI());
}
+ resolver.release(((CachingSource) source).source);
((CachingSource) source).dispose();
}
}
-
- // ---------------------------------------------------- URIAbsolutizer implementation
+
+ // ---------------------------------------------------- URIAbsolutizer implementation
/*
* (non-Javadoc)
Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/DelayRefresher.java
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/DelayRefresher.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/DelayRefresher.java Thu Nov 11 08:24:43 2004
@@ -57,7 +57,7 @@
* Default implementation of the refresher.
*
* @since 2.1.1
- * @version CVS $Id: DelayRefresher.java,v 1.6 2004/04/25 20:01:36 haul Exp $
+ * @version CVS $Id$
*/
public class DelayRefresher extends AbstractLogEnabled
implements Contextualizable, Serviceable, Parameterizable, Disposable, ThreadSafe, Refresher, CronJob {
@@ -267,8 +267,7 @@
}
/**
- * @param childs
- * @param i
+ * @param conf
* @throws ConfigurationException
* @throws CascadingException
*/
@@ -331,7 +330,7 @@
/**
* @param writer
- * @param iter
+ * @param c
* @throws IOException
*/
private void writeRefreshJobConfiguration(Writer writer, final TargetConfiguration c) throws IOException {
Added: cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/InspectableTraversableCachingSource.java
==============================================================================
--- (empty file)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/InspectableTraversableCachingSource.java Thu Nov 11 08:24:43 2004
@@ -0,0 +1,184 @@
+/*
+ * Copyright 1999-2004 The Apache Software Foundation.
+ *
+ * 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.
+ */
+package org.apache.cocoon.components.source.impl;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
+import org.apache.cocoon.components.source.InspectableSource;
+import org.apache.cocoon.components.source.helpers.SourceProperty;
+import org.apache.excalibur.source.Source;
+import org.apache.excalibur.source.SourceException;
+import org.apache.excalibur.source.TraversableSource;
+
+/**
+ * TraversableCachingSource that adds support for SourceProperty caching.
+ */
+public class InspectableTraversableCachingSource extends TraversableCachingSource
+implements InspectableSource {
+
+ private InspectableSource isource;
+
+ public InspectableTraversableCachingSource(String protocol,
+ String uri,
+ InspectableSource source,
+ int expires,
+ String cacheName,
+ boolean async) {
+ super(protocol, uri, (TraversableSource) source, expires, cacheName, async);
+ this.isource = source;
+ }
+
+ public SourceProperty getSourceProperty(String namespace, String name) throws SourceException {
+ try {
+ initMetaResponse();
+ }
+ catch (IOException e) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Failure initializing inspectable response", e);
+ }
+ return null;
+ }
+ final InspectableSourceMeta imeta = ((InspectableSourceMeta) super.response.getExtra());
+ SourceProperty property = imeta.getSourceProperty(namespace, name);
+ if (property == null) {
+ // In the case of webdav the source cannot
+ // determine all available properties beforehand.
+ // Therefore, although we initialized the cached
+ // response by calling getSourceProperties(),
+ // this does not mean this particular property
+ // was returned and cached. Hence we try to
+ // get it here still and remember if it was null.
+ property = isource.getSourceProperty(namespace, name);
+ if (property == null) {
+ // remember that this property is null
+ property = InspectableSourceMeta.NULL_PROPERTY;
+ }
+ imeta.setSourceProperty(property);
+ }
+ if (InspectableSourceMeta.NULL_PROPERTY.equals(property)) {
+ return null;
+ }
+ return property;
+ }
+
+ public void setSourceProperty(SourceProperty property) throws SourceException {
+ isource.setSourceProperty(property);
+ try {
+ initMetaResponse();
+ }
+ catch (IOException e) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Failure initializing inspectable response", e);
+ }
+ }
+ final InspectableSourceMeta imeta = ((InspectableSourceMeta) super.response.getExtra());
+ imeta.setSourceProperty(property);
+ }
+
+ public SourceProperty[] getSourceProperties() throws SourceException {
+ try {
+ initMetaResponse();
+ }
+ catch (IOException e) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Failure initializing inspectable response", e);
+ }
+ return null;
+ }
+ final InspectableSourceMeta imeta = ((InspectableSourceMeta) super.response.getExtra());
+ return imeta.getSourceProperties();
+ }
+
+ public void removeSourceProperty(String namespace, String name) throws SourceException {
+ isource.removeSourceProperty(namespace, name);
+ try {
+ initMetaResponse();
+ }
+ catch (IOException e) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Failure initializing inspectable response", e);
+ }
+ }
+ final InspectableSourceMeta imeta = ((InspectableSourceMeta) super.response.getExtra());
+ imeta.removeSourceProperty(namespace, name);
+ }
+
+ protected SourceMeta createMeta() {
+ return new InspectableSourceMeta();
+ }
+
+ protected void initMeta(SourceMeta meta, Source source) throws IOException {
+ super.initMeta(meta, source);
+ final InspectableSourceMeta imeta = ((InspectableSourceMeta) meta);
+ imeta.setSourceProperties(isource.getSourceProperties());
+ }
+
+ protected TraversableCachingSource newSource(String uri, Source wrapped) {
+ return new InspectableTraversableCachingSource(super.protocol,
+ uri,
+ (InspectableSource) wrapped,
+ super.expires,
+ super.cacheName,
+ super.async);
+ }
+
+ protected static class InspectableSourceMeta extends TraversableSourceMeta {
+
+ protected static final SourceProperty NULL_PROPERTY = new SourceProperty("cocoon", "isnull");
+
+ private Map properties;
+
+ protected SourceProperty getSourceProperty(String namespace, String name) {
+ if (properties == null) return null;
+ final String key = namespace + "#" + name;
+ return (SourceProperty) properties.get(key);
+ }
+
+ protected void setSourceProperty(SourceProperty property) {
+ if (this.properties == null) {
+ this.properties = Collections.synchronizedMap(new HashMap(11));
+ }
+ final String key = property.getNamespace() + "#" + property.getName();
+ properties.put(key, property);
+ }
+
+ protected SourceProperty[] getSourceProperties() {
+ if (this.properties == null) return null;
+ final Collection values = this.properties.values();
+ return (SourceProperty[]) values.toArray(new SourceProperty[values.size()]);
+ }
+
+ protected void setSourceProperties(SourceProperty[] props) {
+ if (this.properties == null) {
+ this.properties = Collections.synchronizedMap(new HashMap(props.length));
+ }
+ for (int i = 0; i < props.length; i++) {
+ setSourceProperty(props[i]);
+ }
+ }
+
+ protected void removeSourceProperty(String namespace, String name) {
+ if (this.properties != null) {
+ final String key = namespace + "#" + name;
+ properties.remove(key);
+ }
+ }
+ }
+}
Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/TraversableCachingSource.java
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/TraversableCachingSource.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/TraversableCachingSource.java Thu Nov 11 08:24:43 2004
@@ -18,9 +18,9 @@
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Iterator;
import org.apache.avalon.framework.container.ContainerUtil;
-import org.apache.avalon.framework.parameters.Parameters;
import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceException;
import org.apache.excalibur.source.TraversableSource;
@@ -33,12 +33,12 @@
private TraversableSource tsource;
public TraversableCachingSource(String protocol,
- String location,
+ String uri,
TraversableSource source,
- Parameters params,
int expires,
+ String cacheName,
boolean async) {
- super(protocol, location, source, params, expires, async);
+ super(protocol, uri, source, expires, cacheName, async);
this.tsource = source;
}
@@ -148,18 +148,26 @@
// ---------------------------------------------------- helper methods
- private TraversableCachingSource createSource(String uri, Source wrapped)
+
+
+ protected final TraversableCachingSource createSource(String uri, Source wrapped)
throws SourceException {
- final TraversableCachingSource source =
- new TraversableCachingSource(super.protocol,
- uri,
- (TraversableSource) wrapped,
- new Parameters().merge(super.parameters),
- super.expires,
- super.async);
+ final TraversableCachingSource source = newSource(uri, wrapped);
+ initializeSource(source);
+ return source;
+ }
+
+ protected TraversableCachingSource newSource(String uri, Source wrapped) {
+ return new TraversableCachingSource(super.protocol,
+ uri,
+ (TraversableSource) wrapped,
+ super.expires,
+ super.cacheName,
+ super.async);
+ }
+
+ protected void initializeSource(TraversableCachingSource source) throws SourceException {
source.cache = super.cache;
- source.resolver = super.resolver;
- source.refresher = super.refresher;
ContainerUtil.enableLogging(source, getLogger());
try {
ContainerUtil.service(source, super.manager);
@@ -167,14 +175,64 @@
} catch (Exception e) {
throw new SourceException("Unable to initialize source.", e);
}
- return source;
}
+ protected SourceMeta createMeta() {
+ return new TraversableSourceMeta();
+ }
+
+ protected void initMeta(SourceMeta meta, Source source) throws IOException {
+ super.initMeta(meta, source);
+
+ final TraversableSource tsource = (TraversableSource) source;
+ final TraversableSourceMeta tmeta = (TraversableSourceMeta) meta;
+
+ tmeta.setName(tsource.getName());
+ tmeta.setIsCollection(tsource.isCollection());
+
+ if (tmeta.isCollection()) {
+ final Collection children = tsource.getChildren();
+ if (children != null) {
+ final String[] names = new String[children.size()];
+ final Iterator iter = children.iterator();
+ int count = 0;
+ while(iter.hasNext()) {
+ TraversableSource child = (TraversableSource) iter.next();
+ names[count] = child.getName();
+ count++;
+ }
+ tmeta.setChildren(names);
+ }
+ }
+
+ }
+
+ protected void remove() {
+ remove(true);
+ }
+
+ /**
+ * The parent's cached response needs to be removed from cache
+ * as well because it's cached list of children is no longer valid.
+ */
+ private void remove(boolean flag) {
+ super.remove();
+ if (flag) {
+ try {
+ TraversableCachingSource parent = (TraversableCachingSource) getParent();
+ parent.remove(false);
+ }
+ catch (SourceException e) {
+ getLogger().error("Error removing parent's cached response");
+ }
+ }
+ }
+
/**
* Calculate the cached child URI based on a parent URI
* and a child name.
*/
- private String getChildURI(String parentURI, String childName) {
+ private static String getChildURI(String parentURI, String childName) {
// separate query string from rest of parentURI
String rest, qs;
@@ -203,7 +261,7 @@
/**
* Calculate the cached parent URI based on a child URI.
*/
- private String getParentURI(String childURI) {
+ private static String getParentURI(String childURI) {
// separate query string from rest of uri
String rest, qs;
@@ -228,6 +286,36 @@
}
return parentUri + qs;
+ }
+
+ protected static class TraversableSourceMeta extends SourceMeta {
+ private String m_name;
+ private boolean m_isCollection;
+ private String[] m_children;
+
+ protected String getName() {
+ return m_name;
+ }
+
+ protected void setName(String name) {
+ m_name = name;
+ }
+
+ protected boolean isCollection() {
+ return m_isCollection;
+ }
+
+ protected void setIsCollection(boolean isCollection) {
+ m_isCollection = isCollection;
+ }
+
+ protected String[] getChildren() {
+ return m_children;
+ }
+
+ protected void setChildren(String[] children) {
+ m_children = children;
+ }
}
}
Modified: cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/UpdateTarget.java
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/UpdateTarget.java (original)
+++ cocoon/branches/BRANCH_2_1_X/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/UpdateTarget.java Thu Nov 11 08:24:43 2004
@@ -15,6 +15,7 @@
*/
package org.apache.cocoon.components.source.impl;
+import java.io.IOException;
import java.util.Map;
import org.apache.avalon.framework.logger.AbstractLogEnabled;
@@ -23,12 +24,8 @@
import org.apache.avalon.framework.service.ServiceManager;
import org.apache.avalon.framework.service.Serviceable;
import org.apache.cocoon.caching.Cache;
-import org.apache.cocoon.caching.IdentifierCacheKey;
import org.apache.cocoon.components.cron.ConfigurableCronJob;
-import org.apache.excalibur.source.Source;
import org.apache.excalibur.source.SourceResolver;
-import org.apache.excalibur.source.SourceValidity;
-import org.apache.excalibur.source.impl.validity.ExpiresValidity;
/**
* A target updating a cache entry.
@@ -48,17 +45,13 @@
* The time in seconds the cached content is valid
* </li>
* <li>
- * <code>fail-safe (boolean)</code>
- * Whether to invalidate the cached response when updating it failed.
- * </li>
- * <li>
- * <code>cache-key (SimpleCacheKey)</code>:
+ * <code>cache-name (String)</code>:
* The key used to cache the content
* </li>
* </ul>
*
* @since 2.1.1
- * @version CVS $Id: UpdateTarget.java,v 1.6 2004/04/15 08:05:56 cziegeler Exp $
+ * @version CVS $Id$
*/
public class UpdateTarget extends AbstractLogEnabled
implements Serviceable, ConfigurableCronJob {
@@ -71,10 +64,7 @@
private String uri;
private String cacheRole;
private int expires;
- private boolean failSafe;
-
- // the key under which to store the CachedResponse in the Cache
- private IdentifierCacheKey cacheKey;
+ private String cacheName;
// ---------------------------------------------------- Lifecycle
@@ -96,14 +86,13 @@
public void setup(Parameters pars, Map objects) {
this.uri = pars.getParameter("uri", null);
this.cacheRole = pars.getParameter("cache-role", Cache.ROLE);
- this.expires = pars.getParameterAsInteger("cache-expires", 60);
- this.failSafe = pars.getParameterAsBoolean("fail-safe", true);
- this.cacheKey = (IdentifierCacheKey) objects.get("cache-key");
+ this.expires = pars.getParameterAsInteger("cache-expires", 0);
+ this.cacheName = pars.getParameter("cache-name", null);
}
// ---------------------------------------------------- CronJob implementation
-
+
/* (non-Javadoc)
* @see org.apache.avalon.cornerstone.services.scheduler.Target#targetTriggered(java.lang.String)
*/
@@ -112,78 +101,36 @@
if (this.getLogger().isDebugEnabled()) {
this.getLogger().debug("Refreshing " + this.uri);
}
-
- Source source = null;
Cache cache = null;
+ CachingSource source = null;
try {
-
- cache = (Cache) this.manager.lookup(this.cacheRole);
- source = this.resolver.resolveURI(this.uri);
-
- // check if the source is really expired and invalid
- CachedSourceResponse response = (CachedSourceResponse) cache.get(this.cacheKey);
- if (response != null) {
- final SourceValidity sourceValidity = response.getValidityObjects()[1];
- if (CachingSource.isValid(sourceValidity, source)) {
- if (getLogger().isDebugEnabled()) {
- getLogger().debug("Cached response is still valid " +
"for source " + this.uri + ".");
- }
- response.getValidityObjects()[0] = new ExpiresValidity(this.expires * 1000);
- return;
- }
- }
-
- if (source.exists()) {
-
- // what is in the cached response?
- byte[] binary = null;
- byte[] xml = null;
- if (response != null) {
- binary = response.getBinaryResponse();
- xml = response.getXMLResponse();
- }
-
- // create a new cached response
- final ExpiresValidity cacheValidity = new ExpiresValidity(this.expires * 1000);
- final SourceValidity sourceValidity = source.getValidity();
- response = new CachedSourceResponse(new SourceValidity[] {cacheValidity, sourceValidity});
-
- // only create objects that have previously been used
- if (binary != null) {
- binary = CachingSource.readBinaryResponse(source);
- response.setBinaryResponse(binary);
- }
- if (xml != null) {
- xml = CachingSource.readXMLResponse(source, binary, this.manager);
- response.setXMLResponse(xml);
- }
- // meta info is always set
- response.setExtra(CachingSource.readMeta(source));
- cache.store(this.cacheKey, response);
- }
- else if (response != null) {
- // FIXME: There is a potential problem when the parent
- // source has not yet been updated thus listing this
- // source still as one of its children. We'll have to remove
- // the parent's cached response here too.
- if (getLogger().isDebugEnabled()) {
- getLogger().debug("Source " + this.uri + " no longer exists." +
" Throwing out cached response.");
- }
- cache.remove(this.cacheKey);
- }
- } catch (Exception e) {
- if (!failSafe) {
- // the content expires, so remove it
- cache.remove(cacheKey);
- getLogger().warn("Exception during updating of source " + this.uri, e);
+ cache = (Cache) manager.lookup(cacheRole);
+ source = CachingSourceFactory.newCachingSource(
+ this.resolver.resolveURI(this.uri),
+ "cached",
+ "cached:" + uri,
+ expires,
+ cacheName,
+ true,
+ cache,
+ getLogger(),
+ this.manager);
+
+ source.refresh();
+ }
+ catch (IOException e) {
+ getLogger().error("Error refreshing source", e);
+ }
+ catch (ServiceException e) {
+ getLogger().error("Error refreshing source", e);
+ }
+ finally {
+ if (cache != null) {
+ manager.release(cache);
}
- else {
- getLogger().warn("Updating of source " + this.uri + " failed. " +
- "Cached response (if any) will be stale.", e);
+ if (source != null) {
+ this.resolver.release(source);
}
- } finally {
- this.resolver.release(source);
- this.manager.release(cache);
}
}
}
Modified: cocoon/branches/BRANCH_2_1_X/status.xml
==============================================================================
--- cocoon/branches/BRANCH_2_1_X/status.xml (original)
+++ cocoon/branches/BRANCH_2_1_X/status.xml Thu Nov 11 08:24:43 2004
@@ -465,6 +465,11 @@
AbstractMessageListener and AbstractMessagePublisher should be used as basis for
custom publish/subscribe components.
</action>
+ <action dev="UH" type="add">
+ Still in the scratchpad area at the time of this writing, added a
+ CachedSource proxy subclass for Sources that implement TraversableSource and
+ InspectableSource (for instance WebDAVSource).
+ </action>
<action dev="TC" type="add" fixes-bug="29935" due-to="Leszek Gawron" due-to-email="ouzo@wlkp.org">
Added support for stripping root elements in the CIncludeTransformer.
</action>