You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@cocoon.apache.org by vg...@apache.org on 2002/02/02 03:42:40 UTC

cvs commit: xml-cocoon2/src/webapp cocoon.xconf

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:fumagalli@exoffice.com">Pierpaolo Fumagalli</a> (Apache Software Foundation, Exoffice Technologies)
    * @author <a href="mailto:stefano@apache.org">Stefano Mazzocchi</a>
    * @author <a href="mailto:leo.sutic@inspireinfrastructure.com">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:vgritsenko@apache.org">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:g-froehlich@gmx.de">Gerhard Froehlich</a>
    * @author <a href="mailto:dims@yahoo.com">Davanum Srinivas</a>
  + * @author <a href="mailto:vgritsenko@apache.org">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:g-froehlich@gmx.de">Gerhard Froehlich</a>
   * @author <a href="mailto:vgritsenko@apache.org">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:g-froehlich@gmx.de">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:     webmaster@xml.apache.org
To unsubscribe, e-mail:          cocoon-cvs-unsubscribe@xml.apache.org
For additional commands, e-mail: cocoon-cvs-help@xml.apache.org