unico 2004/07/01 07:24:37
Modified:
src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl
TraversableCachingSource.java CachingSource.java
UpdateTarget.java CachingSourceFactory.java
Added:
src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl
InspectableTraversableCachingSource.java
Log:
caching source refactorings:
- add support for caching source properties
- move all refresh functionality to refresh() method
- remove dependency from CachingSource -> TraversableCachingSource
Revision Changes Path
1.6 +105 -17
cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/TraversableCachingSource.java
Index: TraversableCachingSource.java
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/TraversableCachingSource.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- TraversableCachingSource.java 24 Mar 2004 17:31:12 -0000 1.5
+++ TraversableCachingSource.java 1 Jul 2004 14:24:36 -0000 1.6
@@ -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;
+ }
}
}
1.13 +141 -184
cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSource.java
Index: CachingSource.java
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSource.java,v
retrieving revision 1.12
retrieving revision 1.13
diff -u -r1.12 -r1.13
--- CachingSource.java 11 Jun 2004 12:18:23 -0000 1.12
+++ CachingSource.java 1 Jul 2004 14:24:36 -0000 1.13
@@ -20,12 +20,9 @@
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;
@@ -40,9 +37,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;
@@ -76,18 +71,19 @@
public class CachingSource extends AbstractLogEnabled
implements Source, Serviceable, Initializable, XMLizable {
+ // ---------------------------------------------------- Constants
+
+ public static final String CACHE_EXPIRES_PARAM = "cache-expires";
+ public static final String CACHE_NAME_PARAM = "cache-name";
+
+ // ---------------------------------------------------- Instance
variables
+
/** The ServiceManager */
protected ServiceManager manager;
- /** The SourceResolver to resolve the wrapped Source */
- protected SourceResolver resolver;
-
/** The current cache */
protected Cache cache;
- /** The refresher for asynchronous updates */
- protected Refresher refresher;
-
/** The source object for the real content */
protected Source source;
@@ -105,13 +101,13 @@
/** 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;
@@ -121,18 +117,17 @@
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;
}
@@ -161,87 +156,44 @@
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 (this.expires == 0) {
+ if (getLogger().isDebugEnabled()) {
+ getLogger().debug("Not using cached response.");
}
+ this.response = null;
+ checkValidity = false;
+ }
- if (remove) {
- this.response = null;
- // remove it if it no longer exists
- if (!exists()) {
- this.cache.remove(this.cacheKey);
- }
+ if (checkValidity && !checkValidity()) {
+ this.response = null;
+ // remove it if it no longer exists
+ if (!this.source.exists()) {
+ remove();
}
}
- 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);
- }
+
}
/**
* 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;
}
@@ -254,22 +206,15 @@
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;
- }
- else {
- response = new CachedSourceResponse(null);
- }
+ response = new CachedSourceResponse(new SourceValidity[] { new
ExpiresValidity(getExpiration()), source.getValidity() });
+ 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);
}
@@ -289,25 +234,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);
}
@@ -330,15 +269,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();
@@ -346,10 +278,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);
}
@@ -446,13 +379,57 @@
/**
* 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();
+ }
+ }
}
-
+
+ protected void remove() {
+ this.cache.remove(this.cacheKey);
+ }
+
// ---------------------------------------------------- XMLizable
implementation
/**
@@ -511,7 +488,7 @@
* @throws IOException
* @throws CascadingIOException
*/
- protected static byte[] readXMLResponse(Source source, byte[] binary,
ServiceManager manager)
+ protected byte[] readXMLResponse(Source source, byte[] binary,
ServiceManager manager)
throws SAXException, IOException, CascadingIOException {
XMLSerializer serializer = null;
XMLizer xmlizer = null;
@@ -552,7 +529,7 @@
* @throws IOException
* @throws SourceNotFoundException
*/
- protected static byte[] readBinaryResponse(Source source)
+ protected byte[] readBinaryResponse(Source source)
throws IOException, SourceNotFoundException {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
final byte[] buffer = new byte[2048];
@@ -572,38 +549,17 @@
* @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);
@@ -612,15 +568,46 @@
meta.setLastModified(System.currentTimeMillis());
}
meta.setMimeType(source.getMimeType());
-
- return meta;
}
- 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 boolean checkValidity() {
+ if (this.response == null) return false;
+
+ final ExpiresValidity expiresValidity = (ExpiresValidity)
this.response.getValidityObjects()[0];
+ final SourceValidity sourceValidity =
this.response.getValidityObjects()[1];
+
+ boolean valid = true;
+
+ 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.");
+ }
+ }
+ return valid;
+ }
+
+ private static boolean isValid(SourceValidity oldValidity,
SourceValidity newValidity) {
+ return (oldValidity.isValid() == SourceValidity.VALID ||
+ (oldValidity.isValid() == SourceValidity.UNKNOWN &&
+ oldValidity.isValid(newValidity) == SourceValidity.VALID));
}
/**
@@ -656,36 +643,6 @@
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;
- }
}
}
1.7 +33 -86
cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/UpdateTarget.java
Index: UpdateTarget.java
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/UpdateTarget.java,v
retrieving revision 1.6
retrieving revision 1.7
diff -u -r1.6 -r1.7
--- UpdateTarget.java 15 Apr 2004 08:05:56 -0000 1.6
+++ UpdateTarget.java 1 Jul 2004 14:24:36 -0000 1.7
@@ -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,11 +45,7 @@
* 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>
@@ -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);
}
}
}
1.12 +83 -29
cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSourceFactory.java
Index: CachingSourceFactory.java
===================================================================
RCS file:
/home/cvs/cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/CachingSourceFactory.java,v
retrieving revision 1.11
retrieving revision 1.12
diff -u -r1.11 -r1.12
--- CachingSourceFactory.java 11 Jun 2004 12:18:23 -0000 1.11
+++ CachingSourceFactory.java 1 Jul 2004 14:24:36 -0000 1.12
@@ -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;
@@ -112,6 +114,16 @@
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;
@@ -159,23 +171,23 @@
Parameters parameters = Parameters.fromConfiguration(configuration);
// 'async' parameter
- this.async = parameters.getParameterAsBoolean("async", false);
-
+ this.async = parameters.getParameterAsBoolean(ASYNC_PARAM, 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);
}
/**
@@ -281,50 +293,91 @@
}
}
- int expires = params.getParameterAsInteger("cache-expires", -1);
- if (expires == -1) {
- expires = this.defaultExpires;
- params.setParameter("cache-expires",
String.valueOf(this.defaultExpires));
- }
- params.setParameter("cache-role", this.cacheRole);
+ int expires =
params.getParameterAsInteger(CachingSource.CACHE_EXPIRES_PARAM,
this.defaultExpires);
+ String cacheName =
params.getParameter(CachingSource.CACHE_NAME_PARAM, null);
- final Source wrappedSource = this.resolver.resolveURI(uri);
+ final CachingSource source =
newCachingSource(this.resolver.resolveURI(uri),
+ this.scheme,
+ location,
+ expires,
+ cacheName,
+ this.async,
+ this.cache,
+ getLogger(),
+ manager);
+
+ 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);
+ }
+
+ 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);
+ 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 [EMAIL PROTECTED] Source} object.
*/
@@ -333,6 +386,7 @@
if (this.getLogger().isDebugEnabled() ) {
this.getLogger().debug("Releasing source " +
source.getURI());
}
+ resolver.release(((CachingSource) source).source);
((CachingSource) source).dispose();
}
}
1.1
cocoon-2.1/src/blocks/scratchpad/java/org/apache/cocoon/components/source/impl/InspectableTraversableCachingSource.java
Index: InspectableTraversableCachingSource.java
===================================================================
/*
* 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) {
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());
SourceProperty[] properties = imeta.getSourceProperties();
if (properties == null) {
properties = isource.getSourceProperties();
imeta.setSourceProperties(properties);
}
return properties;
}
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 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;
/* flag for determining whether we have all properties */
private boolean all;
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 || !all) 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]);
}
all = true;
}
protected void removeSourceProperty(String namespace, String name) {
if (this.properties != null) {
final String key = namespace + "#" + name;
properties.remove(key);
}
}
}
}