vgritsenko 02/02/01 18:42:40 Modified: . changes.xml src/java/org/apache/cocoon Cocoon.java cocoon.roles src/java/org/apache/cocoon/components/store FilesystemStore.java MRUMemoryStore.java src/scratchpad/src/org/apache/cocoon/jispstore MRUMemoryStore.java src/webapp cocoon.xconf Added: src/scratchpad/src/org/apache/cocoon/components/store JispFilesystemStore.java JispStringKey.java Removed: src/scratchpad/src/org/apache/cocoon/jispstore JispFilesystemStore.java JispStringKey.java cocoon.roles cocoon.xconf Log: Some refactoring of Stores: - Decouple Cocoon from FilesystemStore - Define FilesystemStore in the cocoon.xconf - Define cache-persistent and cache-transient shorthands - MRUMemoryStore to use cache-persistent component - Remove all directory - related code from the MRUMemoryStore - Add directory configuration to the FilesystemStore - Move Jisp store to store package - MRUMemoryStore now can work with any persistent store - filesystem or Jisp Revision Changes Path 1.94 +11 -1 xml-cocoon2/changes.xml Index: changes.xml =================================================================== RCS file: /home/cvs/xml-cocoon2/changes.xml,v retrieving revision 1.93 retrieving revision 1.94 diff -u -r1.93 -r1.94 --- changes.xml 1 Feb 2002 15:14:54 -0000 1.93 +++ changes.xml 2 Feb 2002 02:42:39 -0000 1.94 @@ -4,7 +4,7 @@ <!-- History of Cocoon changes - $Id: changes.xml,v 1.93 2002/02/01 15:14:54 sylvain Exp $ + $Id: changes.xml,v 1.94 2002/02/02 02:42:39 vgritsenko Exp $ --> <changes title="History of Changes"> @@ -31,6 +31,16 @@ </devs> <release version="@version@" date="@date@"> + <action dev="VG" type="update"> + Cache relies on two types of store components: (1) transient cache, + with cache-transient shorthand, and (2) persistent cache, with + cache-persistent shorthand. + </action> + <action dev="VG" type="update"> + FilesystemStore (used as programs repository) now is created as all other + components from the cocoon.xconf, and can be configured to use working + directory, cache directory, or any other directory. + </action> <action dev="SW" type="update"> Calling getInputStream() on a "cocoon:" source now returns the same output as an external call instead of always using an XML serializer. 1.7 +1 -14 xml-cocoon2/src/java/org/apache/cocoon/Cocoon.java Index: Cocoon.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/Cocoon.java,v retrieving revision 1.6 retrieving revision 1.7 diff -u -r1.6 -r1.7 --- Cocoon.java 1 Feb 2002 15:48:08 -0000 1.6 +++ Cocoon.java 2 Feb 2002 02:42:39 -0000 1.7 @@ -103,7 +103,7 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Pierpaolo Fumagalli</a> (Apache Software Foundation, Exoffice Technologies) * @author <a href="mailto:[EMAIL PROTECTED]">Stefano Mazzocchi</a> * @author <a href="mailto:[EMAIL PROTECTED]">Leo Sutic</a> - * @version CVS $Revision: 1.6 $ $Date: 2002/02/01 15:48:08 $ + * @version CVS $Revision: 1.7 $ $Date: 2002/02/02 02:42:39 $ */ public class Cocoon extends AbstractLoggable @@ -243,19 +243,6 @@ } catch (Exception e) { getLogger().error("Could not load parser, Cocoon object not created.", e); throw new ConfigurationException("Could not load parser " + parser, e); - } - - try { - if (getLogger().isDebugEnabled()) { - getLogger().debug("Creating Repository with this directory: " + this.workDir); - } - FilesystemStore repository = new FilesystemStore(); - repository.setLogger(getLogger()); - repository.setDirectory(this.workDir); - this.componentManager.addComponentInstance(Store.ROLE + "/Filesystem", repository); - } catch (IOException e) { - getLogger().error("Could not create repository!", e); - throw new ConfigurationException("Could not create the repository!", e); } if (getLogger().isDebugEnabled()) { 1.5 +5 -1 xml-cocoon2/src/java/org/apache/cocoon/cocoon.roles Index: cocoon.roles =================================================================== RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/cocoon.roles,v retrieving revision 1.4 retrieving revision 1.5 diff -u -r1.4 -r1.5 --- cocoon.roles 30 Jan 2002 17:13:24 -0000 1.4 +++ cocoon.roles 2 Feb 2002 02:42:39 -0000 1.5 @@ -41,11 +41,15 @@ default-class="org.apache.cocoon.sitemap.SitemapManager"/> <role name="org.apache.cocoon.components.store.Store" - shorthand="store" + shorthand="cache-transient" default-class="org.apache.cocoon.components.store.MRUMemoryStore"/> <role name="org.apache.cocoon.components.store.Store/Filesystem" shorthand="repository" + default-class="org.apache.cocoon.components.store.FilesystemStore"/> + + <role name="org.apache.cocoon.components.store.Store/PersistentCache" + shorthand="cache-persistent" default-class="org.apache.cocoon.components.store.FilesystemStore"/> <role name="org.apache.cocoon.components.store.StoreJanitor" 1.4 +58 -11 xml-cocoon2/src/java/org/apache/cocoon/components/store/FilesystemStore.java Index: FilesystemStore.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/store/FilesystemStore.java,v retrieving revision 1.3 retrieving revision 1.4 diff -u -r1.3 -r1.4 --- FilesystemStore.java 1 Feb 2002 13:27:32 -0000 1.3 +++ FilesystemStore.java 2 Feb 2002 02:42:39 -0000 1.4 @@ -12,6 +12,9 @@ import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.logger.AbstractLoggable; import org.apache.avalon.framework.thread.ThreadSafe; +import org.apache.avalon.framework.parameters.Parameterizable; +import org.apache.avalon.framework.parameters.Parameters; +import org.apache.avalon.framework.parameters.ParameterException; import org.apache.cocoon.Constants; import org.apache.cocoon.util.IOUtils; @@ -19,9 +22,20 @@ import java.io.IOException; import java.util.Enumeration; +/** + * Stores objects on the filesystem: String objects as text files, + * all other objects are serialized. + * + * @author ? + * @author <a href="mailto:[EMAIL PROTECTED]">Vadim Gritsenko</a> + * @version CVS $Revision: 1.4 $ $Date: 2002/02/02 02:42:39 $ + */ public final class FilesystemStore extends AbstractLoggable -implements Contextualizable, Store, ThreadSafe { +implements Store, Contextualizable, Parameterizable, ThreadSafe { + + protected File workDir; + protected File cacheDir; /** The directory repository */ protected File directoryFile; @@ -37,10 +51,37 @@ public void contextualize(final Context context) throws ContextException { + this.workDir = (File)context.get(Constants.CONTEXT_WORK_DIR); + this.cacheDir = (File)context.get(Constants.CONTEXT_CACHE_DIR); + } + + public void parameterize(Parameters params) + throws ParameterException { try { - setDirectory((File) context.get(Constants.CONTEXT_WORK_DIR)); - } catch (Exception e) { - // ignore + if (params.getParameterAsBoolean("use-cache-directory", false)) { + if (this.getLogger().isDebugEnabled()) + getLogger().debug("Using cache directory: " + cacheDir); + setDirectory(cacheDir); + } else if (params.getParameterAsBoolean("use-work-directory", false)) { + if (this.getLogger().isDebugEnabled()) + getLogger().debug("Using work directory: " + workDir); + setDirectory(workDir); + } else if (params.getParameter("directory", null) != null) { + String dir = params.getParameter("directory"); + dir = IOUtils.getContextFilePath(workDir.getPath(), dir); + if (this.getLogger().isDebugEnabled()) + getLogger().debug("Using directory: " + dir); + setDirectory(new File(dir)); + } else { + try { + // Legacy: use working directory by default + setDirectory(workDir); + } catch (IOException e) { + // Legacy: Always was ignored + } + } + } catch (IOException e) { + throw new ParameterException("Unable to set directory", e); } } @@ -85,18 +126,22 @@ } /** - * Get the file associated with the given unique key name. + * Get the File object associated with the given unique key name. */ public Object get(final Object key) { - if (this.getLogger().isDebugEnabled()) - getLogger().debug("FilesystemStore get(): Get file with key: " + key.toString()); final File file = fileFromKey(key); if (file != null && file.exists()) { - if (this.getLogger().isDebugEnabled()) - getLogger().debug("FilesystemStore get(): Found file with key: " + key.toString()); + if (this.getLogger().isDebugEnabled()) { + getLogger().debug("Found file: " + key); + } return file; + } else { + if (this.getLogger().isDebugEnabled()) { + getLogger().debug("NOT Found file: " + key); + } } + return null; } @@ -126,9 +171,11 @@ } file.mkdir(); - } else if (value instanceof String) { /* Text file */ + } else if (value instanceof String) { + /* Text file */ IOUtils.serializeString(file, (String) value); - } else { /* Serialized Object */ + } else { + /* Serialized Object */ IOUtils.serializeObject(file, value); } } 1.8 +120 -145 xml-cocoon2/src/java/org/apache/cocoon/components/store/MRUMemoryStore.java Index: MRUMemoryStore.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/java/org/apache/cocoon/components/store/MRUMemoryStore.java,v retrieving revision 1.7 retrieving revision 1.8 diff -u -r1.7 -r1.8 --- MRUMemoryStore.java 1 Feb 2002 14:40:45 -0000 1.7 +++ MRUMemoryStore.java 2 Feb 2002 02:42:39 -0000 1.8 @@ -1,5 +1,6 @@ /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * + * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * @@ -7,22 +8,16 @@ *****************************************************************************/ package org.apache.cocoon.components.store; -import org.apache.avalon.excalibur.collections.SynchronizedPriorityQueue; import org.apache.avalon.framework.activity.Disposable; -import org.apache.avalon.excalibur.collections.SynchronizedPriorityQueue; import org.apache.avalon.framework.component.ComponentException; import org.apache.avalon.framework.component.ComponentManager; import org.apache.avalon.framework.component.Composable; -import org.apache.avalon.framework.configuration.Configurable; -import org.apache.avalon.framework.configuration.Configuration; -import org.apache.avalon.framework.configuration.ConfigurationException; -import org.apache.avalon.framework.context.Context; -import org.apache.avalon.framework.context.ContextException; -import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.logger.AbstractLoggable; import org.apache.avalon.framework.parameters.Parameters; +import org.apache.avalon.framework.parameters.ParameterException; +import org.apache.avalon.framework.parameters.Parameterizable; import org.apache.avalon.framework.thread.ThreadSafe; -import org.apache.cocoon.Constants; + import org.apache.cocoon.util.ClassUtils; import org.apache.cocoon.util.IOUtils; @@ -32,6 +27,7 @@ import java.util.Enumeration; import java.util.Hashtable; import java.util.LinkedList; +import java.util.NoSuchElementException; /** * This class provides a cache algorithm for the requested documents. @@ -43,26 +39,18 @@ * * @author <a href="mailto:[EMAIL PROTECTED]">Gerhard Froehlich</a> * @author <a href="mailto:[EMAIL PROTECTED]">Davanum Srinivas</a> + * @author <a href="mailto:[EMAIL PROTECTED]">Vadim Gritsenko</a> */ public final class MRUMemoryStore extends AbstractLoggable -implements Store, - Configurable, - ThreadSafe, - Composable, - Disposable, - Contextualizable { +implements Store, Parameterizable, Composable, Disposable, ThreadSafe { private int maxobjects; - private boolean filesystem; + private boolean persistent; private Hashtable cache; private LinkedList mrulist; - private File cachefile; - private Store fsstore; - private StoreJanitor storejanitor; - private File cachedir; - private File workdir; - private String cachedirstr; + private Store persistentStore; + private StoreJanitor storeJanitor; private ComponentManager manager; /** @@ -72,55 +60,36 @@ */ public void compose(ComponentManager manager) throws ComponentException { this.manager = manager; - this.fsstore = (Store)manager.lookup(Store.ROLE + "/Filesystem"); - this.storejanitor = (StoreJanitor)manager.lookup(StoreJanitor.ROLE); - if (this.getLogger().isDebugEnabled()) { - getLogger().debug("Looking up " + Store.ROLE + "/Filesystem"); - getLogger().debug("Looking up " + StoreJanitor.ROLE); - } - } - - /** - * Get the context - * - * @param the Context of the application - */ - public void contextualize(Context context) throws ContextException { - this.cachedir = (File)context.get(Constants.CONTEXT_CACHE_DIR); - this.workdir = (File)context.get(Constants.CONTEXT_WORK_DIR); - this.cachedirstr = IOUtils.getContextFilePath(this.workdir.getPath(), - this.cachedir.getPath()); - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug("cachedir=" + this.cachedir); - this.getLogger().debug("workdir=" + this.workdir); - this.getLogger().debug("cachedirstr=" + this.cachedirstr); - this.getLogger().debug("Context path=" - + IOUtils.getContextFilePath(this.workdir.getPath(),this.cachedir.getPath())); + if (getLogger().isDebugEnabled()) { + getLogger().debug("Looking up " + Store.ROLE + "/PersistentCache"); + getLogger().debug("Looking up " + StoreJanitor.ROLE); } + this.persistentStore = (Store)manager.lookup(Store.ROLE + "/PersistentCache"); + this.storeJanitor = (StoreJanitor)manager.lookup(StoreJanitor.ROLE); } /** * Initialize the MRUMemoryStore. - * A few options can be used : + * A few options can be used: * <UL> - * <LI>maxobjects = how many objects will be stored in memory (Default: 10 objects)</LI> - * <LI>filesystem = use filesystem storage to keep object persistent (Default: false)</LI> + * <LI>maxobjects: Maximum number of objects stored in memory (Default: 100 objects)</LI> + * <LI>use-persistent-cache: Use persistent cache to keep objects persisted after + * container shutdown or not (Default: false)</LI> * </UL> * * @param the Configuration of the application * @exception ConfigurationException */ - public void configure(Configuration conf) throws ConfigurationException { - Parameters params = Parameters.fromConfiguration(conf); - this.maxobjects = params.getParameterAsInteger("maxobjects",100); - this.filesystem = params.getParameterAsBoolean("filesystem",false); + public void parameterize(Parameters params) throws ParameterException { + this.maxobjects = params.getParameterAsInteger("maxobjects", 100); + this.persistent = params.getParameterAsBoolean("use-persistent-cache", false); if ((this.maxobjects < 1)) { - throw new ConfigurationException("MRUMemoryStore maxobjects must be at least 1 milli second!"); + throw new ParameterException("MRUMemoryStore maxobjects must be at least 1!"); } this.cache = new Hashtable((int)(this.maxobjects * 1.2)); - this.mrulist = new LinkedList(); - this.storejanitor.register(this); + this.mrulist = new LinkedList(); + this.storeJanitor.register(this); } /** @@ -128,29 +97,38 @@ */ public void dispose() { if (this.manager != null) { - this.getLogger().debug("Disposing component!"); - this.manager.release(this.storejanitor); - this.storejanitor = null; - this.manager.release(this.fsstore); - - //save all cache entries to filesystem - this.getLogger().debug("Final cache size=" + this.cache.size()); - Enumeration enum = this.cache.keys(); - while(enum.hasMoreElements()) { - Object tmp = enum.nextElement(); - try { - if(tmp != null - && checkSerializable(this.cache.get(tmp))) { - this.fsstore.store(this.getFileName(tmp.toString()), - this.cache.remove(tmp)); + getLogger().debug("Disposing component!"); + + if (this.storeJanitor != null) + this.storeJanitor.unregister(this); + this.manager.release(this.storeJanitor); + this.storeJanitor = null; + + // save all cache entries to filesystem + if (this.persistent) { + getLogger().debug("Final cache size: " + this.cache.size()); + Enumeration enum = this.cache.keys(); + while (enum.hasMoreElements()) { + Object key = enum.nextElement(); + if (key == null) { + continue; + } + try { + Object value = this.cache.remove(key); + if(checkSerializable(value)) { + persistentStore.store(getFileName(key.toString()), + value); + } + } catch (IOException ioe) { + getLogger().error("Error in dispose()", ioe); } - } catch (IOException ioe) { - this.getLogger().error("Error in dispose():",ioe); } } - - this.fsstore = null; + this.manager.release(this.persistentStore); + this.persistentStore = null; } + + this.manager = null; } /** @@ -174,9 +152,10 @@ * @param the object to be stored */ public void hold(Object key, Object value) { - if (this.getLogger().isDebugEnabled()) { - getLogger().debug("Holding object in memory. key: " + key); - getLogger().debug("Holding object in memory. value: " + value); + if (getLogger().isDebugEnabled()) { + getLogger().debug("Holding object in memory:"); + getLogger().debug(" key: " + key); + getLogger().debug(" value: " + value); } /** ...first test if the max. objects in cache is reached... */ while (this.mrulist.size() >= this.maxobjects) { @@ -187,9 +166,6 @@ this.cache.put(key, value); this.mrulist.remove(key); this.mrulist.addFirst(key); - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug("Cache size=" + cache.size()); - } } /** @@ -199,48 +175,42 @@ * @return the requested object */ public Object get(Object key) { - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug("Getting object from memory. Key: " + key); - } - Object tmpobject = this.cache.get(key); - if ( tmpobject != null ) { + Object value = this.cache.get(key); + if (value != null) { /** put the accessed key on top of the linked list */ this.mrulist.remove(key); this.mrulist.addFirst(key); - return tmpobject; + if (getLogger().isDebugEnabled()) { + getLogger().debug("Found key: " + key.toString()); + } + return value; } - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug("Object not found in memory"); + if (getLogger().isDebugEnabled()) { + getLogger().debug("NOT Found key: " + key.toString()); } + /** try to fetch from filesystem */ - if(this.filesystem) { - tmpobject = this.fsstore.get(getFileName(key.toString())); - if (tmpobject == null) { - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug( "Object was NOT found on fs. Looked for: " - + getFileName(key.toString())); - } - return null; - } else { - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug("Object was found on fs"); - } + if (this.persistent) { + value = this.persistentStore.get(getFileName(key.toString())); + if (value != null) { try { - tmpobject = IOUtils.deserializeObject((File)tmpobject); + // FIXME (VG): This is hack for FilesystemStore which returns file + if (value instanceof File) { + value = IOUtils.deserializeObject((File)value); + } + if(!this.cache.containsKey(key)) { - this.hold(key,tmpobject); + this.hold(key, value); } - return tmpobject; - } catch (ClassNotFoundException ce) { - this.getLogger().error("Error in get()!", ce); - return null; - } catch (IOException ioe) { - this.getLogger().error("Error in get()!", ioe); + return value; + } catch (Exception e) { + getLogger().error("Error in get()!", e); return null; } } } + return null; } @@ -250,13 +220,14 @@ * @param the key of to be removed object */ public void remove(Object key) { - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug("Removing object from store"); + if (getLogger().isDebugEnabled()) { + getLogger().debug("Removing object from store"); + getLogger().debug(" key: " + key); } this.cache.remove(key); this.mrulist.remove(key); - if(this.filesystem && key != null) { - this.fsstore.remove(getFileName(key.toString())); + if(this.persistent && key != null) { + this.persistentStore.remove(getFileName(key.toString())); } } @@ -267,10 +238,10 @@ * @return true if the key exists */ public boolean containsKey(Object key) { - if(filesystem) { - return (this.cache.containsKey(key) || this.fsstore.containsKey(key)); + if(persistent) { + return (cache.containsKey(key) || persistentStore.containsKey(key)); } else { - return this.cache.containsKey(key); + return cache.containsKey(key); } } @@ -287,8 +258,7 @@ * Returns count of the objects in the store, or -1 if could not be * obtained. */ - public int size() - { + public int size() { return this.cache.size(); } @@ -298,29 +268,36 @@ */ public void free() { try { - if(this.cache.size() > 0) { - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug("Freeing cache"); + if (this.cache.size() > 0) { + // This can throw NoSuchElementException + Object key = this.mrulist.removeLast(); + Object value = this.cache.remove(key); + if (value == null) { + getLogger().warn("Concurrency condition in free()"); } - // Swapping object on fs. - if(this.filesystem) { - if(checkSerializable(this.cache.get(this.mrulist.getLast()))) { + + if (getLogger().isDebugEnabled()) { + getLogger().debug("Freeing cache."); + getLogger().debug(" key: " + key); + getLogger().debug(" value: " + value); + } + + if (this.persistent) { + // Swap object on fs. + if(checkSerializable(value)) { try { - this.fsstore.store(this.getFileName(this.mrulist.getLast().toString()), - this.cache.get(this.mrulist.getLast())); + this.persistentStore.store( + getFileName(key.toString()), value); } catch(Exception e) { - this.getLogger().error("Error storing Object on fs",e); + getLogger().error("Error storing object on fs", e); } } } - this.cache.remove(this.mrulist.getLast()); - this.mrulist.removeLast(); - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug("Cache size=" + cache.size()); - } } + } catch (NoSuchElementException e) { + getLogger().warn("Concurrency error in free()", e); } catch (Exception e) { - this.getLogger().error("Error in free()", e); + getLogger().error("Error in free()", e); } } @@ -333,19 +310,21 @@ * @return true if the object is storeable */ private boolean checkSerializable(Object object) { + + if (object == null) return false; + try { - if (this.getLogger().isDebugEnabled()) { - this.getLogger().debug("Object=" + object); - } - if((object.getClass().getName().equals("org.apache.cocoon.caching.CachedEventObject")) - || (object.getClass().getName().equals("org.apache.cocoon.caching.CachedStreamObject")) - || (ClassUtils.implementsInterface(object.getClass().getName(),"org.apache.cocoon.caching.CacheValidity"))) { + String clazz = object.getClass().getName(); + // FIXME (VG): Can class identity check work here (==)? It will be faster. + if((clazz.equals("org.apache.cocoon.caching.CachedEventObject")) + || (clazz.equals("org.apache.cocoon.caching.CachedStreamObject")) + || (ClassUtils.implementsInterface(clazz, "org.apache.cocoon.caching.CacheValidity"))) { return true; } else { return false; } } catch (Exception e) { - this.getLogger().error("Error in checkSerializable()!", e); + getLogger().error("Error in checkSerializable()!", e); return false; } } @@ -359,11 +338,7 @@ * @return the filename of the key */ private String getFileName(String key) { - return new StringBuffer() - .append(this.cachedirstr) - .append(File.separator) - .append(URLEncoder.encode(key.toString())) - .toString(); + return URLEncoder.encode(key.toString()); } } 1.1 xml-cocoon2/src/scratchpad/src/org/apache/cocoon/components/store/JispFilesystemStore.java Index: JispFilesystemStore.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.cocoon.components.store; import org.apache.avalon.framework.activity.Initializable; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.ContextException; import org.apache.avalon.framework.context.Contextualizable; import org.apache.avalon.framework.logger.AbstractLoggable; import org.apache.avalon.framework.parameters.ParameterException; import org.apache.avalon.framework.parameters.Parameterizable; import org.apache.avalon.framework.parameters.Parameters; import org.apache.avalon.framework.thread.ThreadSafe; import org.apache.cocoon.Constants; import org.apache.cocoon.util.IOUtils; import com.coyotegulch.jisp.BTreeIndex; import com.coyotegulch.jisp.IndexedObjectDatabase; import com.coyotegulch.jisp.KeyNotFound; import com.coyotegulch.jisp.KeyObject; import java.io.File; import java.io.IOException; import java.io.Serializable; import java.util.Enumeration; import java.util.Vector; /** * This store is based on the Jisp library * (http://www.coyotegulch.com/jisp/index.html). This store uses B-Tree indexes * to access variable-length serialized data stored in files. * * @author <a href="mailto:[EMAIL PROTECTED]">Gerhard Froehlich</a> * @author <a href="mailto:[EMAIL PROTECTED]">Vadim Gritsenko</a> */ public final class JispFilesystemStore extends AbstractLoggable implements Store, Contextualizable, ThreadSafe, Initializable, Parameterizable { protected File workDir; protected File cacheDir; /** * The directory repository */ protected File directoryFile; protected volatile String directoryPath; /** * The database */ private File databaseFile; private File indexFile; private int mOrder; private IndexedObjectDatabase mDatabase; private BTreeIndex mIndex; /** * Sets the repository's location * * @param directory the new directory value * @exception IOException */ public void setDirectory(final String directory) throws IOException { this.setDirectory(new File(directory)); } /** * Sets the repository's location * * @param directory the new directory value * @exception IOException */ public void setDirectory(final File directory) throws IOException { this.directoryFile = directory; /* Save directory path prefix */ this.directoryPath = IOUtils.getFullFilename(this.directoryFile); this.directoryPath += File.separator; if (!this.directoryFile.exists()) { /* Create it new */ if (!this.directoryFile.mkdir()) { throw new IOException("Error creating store directory '" + this.directoryPath + "'"); } } /* Is given file actually a directory? */ if (!this.directoryFile.isDirectory()) { throw new IOException("'" + this.directoryPath + "' is not a directory"); } /* Is directory readable and writable? */ if (!(this.directoryFile.canRead() && this.directoryFile.canWrite())) { throw new IOException("Directory '" + this.directoryPath + "' is not readable/writable"); } } /** * Contextualize the Component * * @param context the Context of the Application * @exception ContextException */ public void contextualize(final Context context) throws ContextException { this.workDir = (File)context.get(Constants.CONTEXT_WORK_DIR); this.cacheDir = (File)context.get(Constants.CONTEXT_CACHE_DIR); } /** * Configure the Component.<br> * A few options can be used * <UL> * <LI> datafile = the name of the data file (Default: cocoon.dat) * </LI> * <LI> indexfile = the name of the index file (Default: cocoon.idx) * </LI> * <LI> order = The page size of the B-Tree</LI> * </UL> * * @param params the configuration paramters * @exception ParameterException */ public void parameterize(Parameters params) throws ParameterException { try { if (params.getParameterAsBoolean("use-cache-directory", false)) { if (this.getLogger().isDebugEnabled()) getLogger().debug("Using cache directory: " + cacheDir); setDirectory(cacheDir); } else if (params.getParameterAsBoolean("use-work-directory", false)) { if (this.getLogger().isDebugEnabled()) getLogger().debug("Using work directory: " + workDir); setDirectory(workDir); } else if (params.getParameter("directory", null) != null) { String dir = params.getParameter("directory"); dir = IOUtils.getContextFilePath(workDir.getPath(), dir); if (this.getLogger().isDebugEnabled()) getLogger().debug("Using directory: " + dir); setDirectory(new File(dir)); } else { try { // Default setDirectory(workDir); } catch (IOException e) { // Ignored } } } catch (IOException e) { throw new ParameterException("Unable to set directory", e); } String databaseName = params.getParameter("datafile", "cocoon.dat"); String indexName = params.getParameter("indexfile", "cocoon.idx"); mOrder = params.getParameterAsInteger("order", 301); if (getLogger().isDebugEnabled()) { this.getLogger().debug("Database file name = " + databaseName); this.getLogger().debug("Index file name = " + indexName); this.getLogger().debug("Order=" + mOrder); } databaseFile = new File(directoryFile, databaseName); indexFile = new File(directoryFile, indexName); } /** * Initialize the Component */ public void initialize() { if (getLogger().isDebugEnabled()) { getLogger().debug("initialize() JispFilesystemStore"); } try { if (databaseFile.exists()) { if (getLogger().isDebugEnabled()) { this.getLogger().debug("initialize(): Datafile exists"); } mDatabase = new IndexedObjectDatabase(databaseFile.toString(), false); mIndex = new BTreeIndex(indexFile.toString()); mDatabase.attachIndex(mIndex); } else { if (getLogger().isDebugEnabled()) { this.getLogger().debug("initialize(): Datafile does not exist"); } mDatabase = new IndexedObjectDatabase(databaseFile.toString(), false); mIndex = new BTreeIndex(indexFile.toString(), mOrder, new JispStringKey(), false); mDatabase.attachIndex(mIndex); } } catch (KeyNotFound ignore) { } catch (Exception e) { getLogger().error("initialize(..) Exception", e); } } /** * Returns the repository's full pathname * * @return the directory as String */ public String getDirectoryPath() { return this.directoryPath; } /** * Returns a Object from the store associated with the Key Object * * @param key the Key object * @return the Object associated with Key Object */ public Object get(Object key) { Object value = null; try { value = mDatabase.read(this.wrapKeyObject(key), mIndex); if (getLogger().isDebugEnabled()) { if (value != null) { getLogger().debug("Found key: " + key); } else { getLogger().debug("NOT Found key: " + key); } } } catch (Exception e) { getLogger().error("get(..): Exception", e); } return value; } /** * Store the given object in the indexed data file. * * @param key the key object * @param value the value object * @exception IOException */ public void store(Object key, Object value) throws IOException { if (getLogger().isDebugEnabled()) { this.getLogger().debug("store(): Store file with key: " + key.toString()); this.getLogger().debug("store(): Store file with value: " + value.toString()); } if (value instanceof Serializable) { try { KeyObject[] keyArray = new KeyObject[1]; keyArray[0] = this.wrapKeyObject(key); mDatabase.write(keyArray, (Serializable) value); } catch (Exception e) { this.getLogger().error("store(..): Exception", e); } } else { throw new IOException("Object not Serializable"); } } /** * Holds the given object in the indexed data file. * * @param key the key object * @param value the value object * @exception IOException */ public void hold(Object key, Object value) throws IOException { this.store(key, value); } /** * Frees some values of the data file.<br> * TODO: implementation */ public void free() { //TODO: implementation } /** * Removes a value from the data file with the given key. * * @param key the key object */ public void remove(Object key) { if (getLogger().isDebugEnabled()) { this.getLogger().debug("remove(..) Remove item"); } try { KeyObject[] keyArray = new KeyObject[1]; keyArray[0] = this.wrapKeyObject(key); mDatabase.remove(keyArray); } catch (KeyNotFound ignore) { } catch (Exception e) { this.getLogger().error("remove(..): Exception", e); } } /** * Test if the the index file contains the given key * * @param key the key object * @return true if Key exists and false if not */ public boolean containsKey(Object key) { long res = -1; try { res = mIndex.findKey(this.wrapKeyObject(key)); if (getLogger().isDebugEnabled()) { this.getLogger().debug("containsKey(..): res=" + res); } } catch (KeyNotFound ignore) { } catch (Exception e) { this.getLogger().error("containsKey(..): Exception", e); } if (res > 0) { return true; } else { return false; } } /** * Returns a Enumeration of all Keys in the indexed file.<br> * * @return Enumeration Object with all existing keys */ public Enumeration keys() { // TODO: Implementation return new Vector(0).elements(); } public int size() { // TODO: Unsupported return 0; } /** * This method wraps around the key Object a Jisp KeyObject. * * @param key the key object * @return the wrapped key object */ private KeyObject wrapKeyObject(Object key) { // TODO: Implementation of Integer and Long keys String skey = String.valueOf(key); return new JispStringKey(key.toString()); } } 1.1 xml-cocoon2/src/scratchpad/src/org/apache/cocoon/components/store/JispStringKey.java Index: JispStringKey.java =================================================================== /***************************************************************************** * Copyright (C) The Apache Software Foundation. All rights reserved. * * ------------------------------------------------------------------------- * * This software is published under the terms of the Apache Software License * * version 1.1, a copy of which has been included with this distribution in * * the LICENSE file. * *****************************************************************************/ package org.apache.cocoon.components.store; import com.coyotegulch.jisp.KeyObject; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; /** * Wrapper class for String Keys to be compatible with the * Jisp KeyObject. * * @author <a href="mailto:[EMAIL PROTECTED]">Gerhard Froehlich</a> */ final class JispStringKey extends KeyObject { final static long serialVersionUID = -6894793231339165076L; private String mKey; /** * Constructor for the JispStringKey object */ public JispStringKey() { mKey = new String(""); } /** * Constructor for the JispStringKey object * * @param keyValue the Value of the Key as String */ public JispStringKey(String keyValue) { mKey = keyValue; } /** * Compares two String Keys * * @param key the KeyObject to be compared * @return 0 if equal, 1 if greater, -1 if less */ public int compareTo(KeyObject key) { if (key instanceof JispStringKey) { int comp = mKey.trim().compareTo(((JispStringKey) key).mKey.trim()); if (comp == 0) { return KEY_EQUAL; } else { if (comp < 0) { return KEY_LESS; } else { return KEY_MORE; } } } else { return KEY_ERROR; } } /** * Composes a null Kewy * * @return a null Key */ public KeyObject makeNullKey() { return new JispStringKey(); } /** * The object implements the writeExternal method to save its contents * by calling the methods of DataOutput for its primitive values or * calling the writeObject method of ObjectOutput for objects, strings, * and arrays. * * @param out the stream to write the object to * @exception IOException */ public void writeExternal(ObjectOutput out) throws IOException { String outKey; outKey = new String(mKey); out.writeUTF(outKey); } /** * The object implements the readExternal method to restore its contents * by calling the methods of DataInput for primitive types and readObject * for objects, strings and arrays. The readExternal method must read the * values in the same sequence and with the same types as were written by writeExternal. * * @param in the stream to read data from in order to restore the object * @exception IOException * @exception ClassNotFoundException */ public void readExternal(ObjectInput in) throws IOException, ClassNotFoundException { mKey = in.readUTF(); } /** * Overrides the toString() method * * @return the Key as String */ public String toString() { return mKey; } } 1.6 +4 -4 xml-cocoon2/src/scratchpad/src/org/apache/cocoon/jispstore/MRUMemoryStore.java Index: MRUMemoryStore.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/scratchpad/src/org/apache/cocoon/jispstore/MRUMemoryStore.java,v retrieving revision 1.5 retrieving revision 1.6 diff -u -r1.5 -r1.6 --- MRUMemoryStore.java 1 Feb 2002 13:27:32 -0000 1.5 +++ MRUMemoryStore.java 2 Feb 2002 02:42:40 -0000 1.6 @@ -61,7 +61,7 @@ */ public Object get(Object key) { if (getLogger().isDebugEnabled()) { - this.getLogger().debug("Getting object from memory. Key: " + key); + getLogger().debug("Getting object from memory. Key: " + key); } Object tmpobject = this.mCache.get(key); if (tmpobject != null) { @@ -70,18 +70,18 @@ return tmpobject; } - this.getLogger().debug("Object not found in memory"); + getLogger().debug("Object not found in memory"); tmpobject = this.mFsstore.get(key); if (tmpobject == null) { if (getLogger().isDebugEnabled()) { - this.getLogger().debug("Object was NOT found on fs. " + getLogger().debug("Object was NOT found on fs. " + "Looked for: " + key); } return null; } else { if (getLogger().isDebugEnabled()) { - this.getLogger().debug("Object was found on fs"); + getLogger().debug("Object was found on fs"); } if (!this.mCache.containsKey(key)) { this.hold(key, tmpobject); 1.15 +52 -10 xml-cocoon2/src/webapp/cocoon.xconf Index: cocoon.xconf =================================================================== RCS file: /home/cvs/xml-cocoon2/src/webapp/cocoon.xconf,v retrieving revision 1.14 retrieving revision 1.15 diff -u -r1.14 -r1.15 --- cocoon.xconf 28 Jan 2002 19:58:13 -0000 1.14 +++ cocoon.xconf 2 Feb 2002 02:42:40 -0000 1.15 @@ -27,21 +27,63 @@ <!-- ============================ STORE ============================ --> + <!-- Store for the generated code. Used by compiled sitemap engine and + XSP engine. Must be pointing to the work directory as generated + classes are expected to be in this directory. + --> + <repository class="org.apache.cocoon.components.store.FilesystemStore" + logger="core.store.repository"> + <parameter name="use-work-directory" value="true"/> + </repository> + + <!-- Persistent store for the cache. Two store implementations to choose + from: + * FilesystemStore: Simple. Dependable. Thorougly tested. + * JispFilesystemStore: Scalable. New kid on the block. + + Common configuration parameters: + use-cache-directory: Indicates that cache directory specified in + web.xml should be used. + use-work-directory: Indicates that work directory specified in + web.xml should be used. + directory: Specifies directory to use. Absolute or relative to the + work directory. + + JispFilesystemStore configuration: + datafile: name of the store file to use. + indexfile: name of the index file to use. + order: FIXME: put description here. + --> + <cache-persistent class="org.apache.cocoon.components.store.FilesystemStore" + logger="core.store.persistent"> + <parameter name="use-cache-directory" value="true"/> + </cache-persistent> + <!-- + <cache-persistent class="org.apache.cocoon.components.store.JispFilesystemStore" + logger="core.store.persistent"> + <parameter name="use-cache-directory" value="true"/> + <parameter name="datafile" value="cocoon-cache.dat"/> + <parameter name="indexfile" value="cocoon-cache.idx"/> + <parameter name="order" value="301"/> + </cache-persistent> + --> + <!-- Memory Storing: --> - <store class="org.apache.cocoon.components.store.MRUMemoryStore" - logger="core.store"> + <cache-transient class="org.apache.cocoon.components.store.MRUMemoryStore" + logger="core.store.transient"> <!-- Indicates how many objects will be hold in the cache. When the number of maxobjects has been reached. The last object in the cache will be thrown out. --> <parameter name="maxobjects" value="100"/> - <!-- Turns the filesystem swapping on and off --> - <parameter name="filesystem" value="true"/> - </store> + + <!-- Turns the swapping of the objects into persistent cache on + and off. --> + <parameter name="use-persistent-cache" value="true"/> + </cache-transient> <!-- Store Janitor: - Be careful with the heapsize and freememory paramters. Wrong values can - cause high cpu usage. - Example configuration: + Be careful with the heapsize and freememory parameters. Wrong values can + cause high cpu usage. Example configuration: Jvm settings: -Xms100000000 -Xmx200000000 store-janitor settings: @@ -55,7 +97,7 @@ amount of memory necessary for normal application operation. --> <store-janitor class="org.apache.cocoon.components.store.StoreJanitorImpl" - logger="core.store-janitor"> + logger="core.store.janitor"> <!-- How much free memory shall be available in the jvm --> <parameter name="freememory" value="1000000"/> <!-- Indicates the limit of the jvm memory consumption. The default max @@ -66,7 +108,7 @@ <!-- Indicates the thread priority of the cleanup thread --> <parameter name="threadpriority" value="5"/> <!-- How much percent of the elements of each registered Store shall - be removed. Default 10% --> + be removed when low on memory. Default 10% --> <parameter name="percent_to_free" value="10"/> </store-janitor> <!-- ============================ STORE END ========================= -->
---------------------------------------------------------------------- In case of troubles, e-mail: [EMAIL PROTECTED] To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]