dims 01/07/13 04:16:01 Modified: src/org/apache/cocoon/components/store Tag: cocoon_20_branch FilesystemStore.java MRUMemoryStore.java Log: Patches from "Gerhard Froehlich" <[EMAIL PROTECTED]> for "Big Bug" in File/MRU SystemStore Revision Changes Path No revision No revision 1.1.1.1.2.1 +3 -1 xml-cocoon2/src/org/apache/cocoon/components/store/FilesystemStore.java Index: FilesystemStore.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/org/apache/cocoon/components/store/FilesystemStore.java,v retrieving revision 1.1.1.1 retrieving revision 1.1.1.1.2.1 diff -u -r1.1.1.1 -r1.1.1.1.2.1 --- FilesystemStore.java 2001/05/09 20:50:00 1.1.1.1 +++ FilesystemStore.java 2001/07/13 11:15:47 1.1.1.1.2.1 @@ -9,6 +9,7 @@ import java.io.File; import java.io.IOException; +import java.net.URLEncoder; import java.util.Enumeration; import org.apache.avalon.framework.context.Context; import org.apache.avalon.framework.context.ContextException; @@ -82,12 +83,13 @@ * Get the file associated with the given unique key name. */ public Object get(Object key) { + getLogger().debug("FilesystemStore get(): Get file with key: " + key.toString()); File file = fileFromKey(key); if (file != null && file.exists()) { + getLogger().debug("FilesystemStore get(): Found file with key: " + key.toString()); return file; } - return null; } 1.2.2.1 +231 -70 xml-cocoon2/src/org/apache/cocoon/components/store/MRUMemoryStore.java Index: MRUMemoryStore.java =================================================================== RCS file: /home/cvs/xml-cocoon2/src/org/apache/cocoon/components/store/MRUMemoryStore.java,v retrieving revision 1.2 retrieving revision 1.2.2.1 diff -u -r1.2 -r1.2.2.1 --- MRUMemoryStore.java 2001/05/31 17:38:16 1.2 +++ MRUMemoryStore.java 2001/07/13 11:15:50 1.2.2.1 @@ -7,10 +7,21 @@ *****************************************************************************/ package org.apache.cocoon.components.store; +import java.net.URLDecoder; +import java.net.URLEncoder; +import java.io.File; import java.util.Enumeration; import java.util.HashMap; import java.util.LinkedList; +import java.util.Stack; + +import org.apache.cocoon.util.ClassUtils; +import org.apache.cocoon.util.IOUtils; + import org.apache.avalon.framework.component.Component; +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; @@ -22,11 +33,8 @@ * This class provides a cache algorithm for the requested documents. * It combines a HashMap and a LinkedList to create a so called MRU * (Most Recently Used) cache. - * The cached objects also have a "lifecycle". If the "lifecycle" of a - * object is over, it "dies" like in real life :-) and a new object will - * be born. - * Also could the number of objects in the cache be limited. If the Limit is - * reache, the last object in the cache will be removed. + * The objects can also be stored onto the filesystem to hold them in a + * persitent state over jvm restarts. * * The idea was token from the "Writing Advanced Applikation Tutorial" from * javasoft. Many thanx to the writers! @@ -35,7 +43,7 @@ * @author <a href="mailto:[EMAIL PROTECTED]">Davanum Srinivas</a> */ -public class MRUMemoryStore extends AbstractLoggable implements Store, Configurable, ThreadSafe, Runnable { +public class MRUMemoryStore extends AbstractLoggable implements Store, Configurable, ThreadSafe, Runnable, Composable { /** * Indicates how much memory should be left free in the JVM for * normal operation. @@ -51,12 +59,12 @@ /** * Indicates the time in seconds to sleep between memory checks. */ - private long interval; + private long cleanupthreadinterval; /** * Indicates whether we use a cleanup thread or not. */ - private boolean useThread; + private boolean usecleanupthread; /** * Indicates the daemon thread priority. @@ -64,14 +72,19 @@ private int priority; /** - * Indicates the object lifetime + * Indicates the max. object in the cache */ - private int ObjectLifeTime; + private int maxobjects; /** - * Indicates the max. object in the cache + * Sets the filesystem store on or off */ - private int maxobjects; + private boolean filesystem; + + /** + * Indicates the interval of the WriterThread + */ + private int writerthreadinterval; /** * The heart of the cache @@ -80,11 +93,22 @@ private LinkedList mrulist; private Runtime jvm; + + private File cachefile; + private Store fsstore; + private Stack writerstack; - public MRUMemoryStore() { - this.jvm = Runtime.getRuntime(); - this.cache = new HashMap(); - this.mrulist = new LinkedList(); + /** the component manager */ + protected ComponentManager manager; + + public void compose(ComponentManager manager) throws ComponentException { + try { + this.manager = manager; + getLogger().debug("Looking up FilesystemStore" + FilesystemStore.ROLE); + this.fsstore = (Store)manager.lookup(Store.ROLE + "/Filesystem"); + } catch(ComponentException e) { + getLogger().error("Error in MRUMemoryStore!",e); + } } /** @@ -93,57 +117,106 @@ * <UL> * <LI>freememory = How much memory to keep free for normal jvm operation. (Default: 1 Mb)</LI> * <LI>heapsize = The size of the heap before cleanup starts. (Default: 60 Mb)</LI> - * <LI>usethread = use a cleanup daemon thread. (Default: true)</LI> - * <LI>threadpriority = priority to run cleanup thread (1-10). (Default: 10)</LI> - * <LI>interval = time in seconds to sleep between memory checks (Default: 10 seconds)</LI> - * <LI>objectlifetime = Object lifetime in seconds + * <LI>cleanupthreadinterval = time in seconds to sleep between memory checks (Default: 10 seconds)</LI> + * <LI>maxobjects = how many objects will be stored in memory (Default: 10o objects)</LI> + * <LI>threadpriority = priority of the thread (1-10). (Default: 10)</LI> + * <LI>filesystem = use filesystem storage to keep object persistent (Default: false)</LI> + * <LI>writerthreadinterval = time in millis to sleep between writing onto the filesystem (Default: 100 millis)</LI> + * <LI>usecleanupthread = use a cleanup daemon thread. (Default: true)</LI> * </UL> */ - public void configure(Configuration conf) throws ConfigurationException { - Parameters params = Parameters.fromConfiguration(conf); - - this.freememory = params.getParameterAsInteger("freememory",1000000); - this.heapsize = params.getParameterAsInteger("heapsize",60000000); - this.ObjectLifeTime = params.getParameterAsInteger("objectlifetime",300); - this.interval = params.getParameterAsInteger("interval",10); - this.maxobjects = params.getParameterAsInteger("maxobjects",100); - this.priority = params.getParameterAsInteger("threadpriority",Thread.currentThread().getPriority()); - - if ((this.priority < 1) || (this.priority > 10)) { - throw new ConfigurationException("Thread priority must be between 1 and 10"); - } + this.jvm = Runtime.getRuntime(); + this.cache = new HashMap(); + this.mrulist = new LinkedList(); + this.writerstack = new Stack(); + + Parameters params = Parameters.fromConfiguration(conf); + this.freememory = params.getParameterAsInteger("freememory",1000000); + this.heapsize = params.getParameterAsInteger("heapsize",60000000); + this.cleanupthreadinterval = params.getParameterAsInteger("cleanupthreadinterval",10); + this.maxobjects = params.getParameterAsInteger("maxobjects",100); + this.priority = params.getParameterAsInteger("threadpriority",Thread.currentThread().getPriority()); + this.filesystem = params.getParameterAsBoolean("filesystem",false); + this.writerthreadinterval = params.getParameterAsInteger("writerthreadinterval",100); + if ((this.priority < 1) || (this.priority > 10)) { + throw new ConfigurationException("MRUMemoryStore cleanup thread priority must be between 1 and 10!"); + } + if ((this.writerthreadinterval < 1)) { + throw new ConfigurationException("MRUMemoryStore writer thread interval must be at least 1 millis!"); + } + if ((this.maxobjects < 1)) { + throw new ConfigurationException("MRUMemoryStore maxobjects must be at least 1 milli second!"); + } + if ((this.cleanupthreadinterval < 1)) { + throw new ConfigurationException("MRUMemoryStore cleanup thread interval must be at least 1 second!"); + } - this.useThread = params.getParameter("usethread","true").equals("true"); - if (this.useThread) { - Thread checker = new Thread(this); - checker.setPriority(this.priority); - checker.setDaemon(true); - checker.start(); - } + this.usecleanupthread = params.getParameter("usecleanupthread","true").equals("true"); + + if (this.usecleanupthread) { + getLogger().debug("MRUMemoryStore intializing checker thread"); + Thread checker = new Thread(this); + checker.setPriority(this.priority); + checker.setDaemon(true); + checker.setName("checker"); + checker.start(); + } + + if (this.filesystem) { + getLogger().debug("MRUMemoryStore intializing writer thread"); + Thread writer = new Thread(this); + writer.setPriority(this.priority); + writer.setDaemon(true); + writer.setName("writer"); + writer.start(); + } } /** - * Background memory check. - * Checks that memory is not running too low in the JVM because of the Store. + * Background threads. + * Thread checker checks that memory is not running too low in the JVM because of the Store. * It will try to keep overall memory usage below the requested levels. + * Thread writer writes objects from the writer stack onto the filesystem. */ - public void run() { - while (true) { - if (this.jvm.totalMemory() > this.heapsize) { - this.jvm.runFinalization(); - this.jvm.gc(); - synchronized (this) { - while ((this.cache.size() > 0) && (this.jvm.freeMemory() < this.freememory)) { - this.free(); - } - } - } - try { - Thread.currentThread().sleep(this.interval * 1000); - } catch (InterruptedException ignore) {} - } - } + public void run() { + while (true) { + if(Thread.currentThread().getName().equals("checker")) { + if (this.jvm.totalMemory() > this.heapsize) { + this.jvm.runFinalization(); + this.jvm.gc(); + synchronized (this) { + while ((this.cache.size() > 0) && (this.jvm.freeMemory() < this.freememory)) { + this.free(); + } + } + } + try { + Thread.currentThread().sleep(this.cleanupthreadinterval * 1000); + } catch (InterruptedException ignore) {} + } else if(Thread.currentThread().getName().equals("writer")) { + if(!writerstack.empty()) { + try { + TmpStackObject tmpstackobject = new TmpStackObject(); + Object key = new Object(); + Object object = new Object(); + tmpstackobject = (TmpStackObject)this.writerstack.pop(); + key = tmpstackobject.getKey(); + object = tmpstackobject.getObject(); + this.fsstore.store(URLEncoder.encode(key.toString()),object); + key = null; + object = null; + tmpstackobject = null; + } catch(java.io.IOException e) { + getLogger().error("Error in MRUMemoryStore",e); + } + } + try { + Thread.currentThread().sleep(this.writerthreadinterval); + } catch (InterruptedException ignore) {} + } + } + } /** * Store the given object in a persistent state. It is up to the @@ -157,16 +230,33 @@ /** * This method holds the requested object in a HashMap combined with a LinkedList to * create the MRU. + * It also can store the objects onto the filesystem if configured. */ public void hold(Object key, Object value) { - getLogger().debug("Holding object in memory. Key: " + key); + getLogger().debug("MRUMemoryStore holding object in memory. Key: " + key); + boolean serialisedFlag; + /** ...first test if the max. objects in cache is reached... */ if(this.mrulist.size() >= this.maxobjects) { /** ...ok, heapsize is reached, remove the last element... */ this.free(); } + + /** put the object on the filesystem */ + if(this.filesystem) { + if(this.checkSeriazable(value)) { + getLogger().debug("MRUMemoryStore storing object on fs"); + this.writerstack.push(new TmpStackObject(key,value)); + getLogger().debug("MRUMemoryStore stack size=" + writerstack.size()); + serialisedFlag = true; + } else { + serialisedFlag = false; + } + } else { + serialisedFlag = false; + } /** ..put the new object in the cache, on the top of course ... */ - this.cache.put(key, new CacheObject(value,System.currentTimeMillis())); + this.cache.put(key, new CacheObject(value,System.currentTimeMillis(),serialisedFlag)); this.mrulist.addFirst(key); } @@ -174,19 +264,40 @@ * Get the object associated to the given unique key. */ public Object get(Object key) { + getLogger().debug("MRUMemoryStore getting object from memory. Key: " + key); + //CacheObject tmpobject = new CacheObject(); + Object tmpobject = new Object(); + try { - long TimeDiff = System.currentTimeMillis() - ((CacheObject)this.cache.get(key)).getCreateTime(); - /** ...check if the object life time is reached... */ - if(TimeDiff >= (this.ObjectLifeTime * 1000)) { - this.remove(key); - return null; - } /** put the accessed key on top of the linked list */ this.mrulist.remove(key); this.mrulist.addFirst(key); return ((CacheObject)this.cache.get(key)).getCacheObject(); } catch(NullPointerException e) { - return null; + getLogger().debug("MRUMemoryStore object not found in memory"); + /** try to fetch from filesystem */ + if(this.filesystem) { + tmpobject = this.fsstore.get(URLEncoder.encode(key.toString())); + if (tmpobject == null) { + return null; + } else { + getLogger().debug("MRUMemoryStor found object on fs"); + + try { + tmpobject = IOUtils.deserializeObject((File)tmpobject); + this.hold(key,tmpobject); + return tmpobject; + } catch (ClassNotFoundException ce) { + getLogger().error("Error in MRUMemoryStore!",e); + return null; + } catch (java.io.IOException ioe) { + getLogger().error("Error in MRUMemoryStore!",e); + return null; + } + } + } else { + return null; + } } } @@ -195,8 +306,10 @@ * the object associated to the given key or null if not found. */ public void remove(Object key) { + getLogger().debug("MRUMemoryStore removing object from store"); this.cache.remove(key); this.mrulist.remove(key); + this.fsstore.remove(URLEncoder.encode(key.toString())); } /** @@ -221,9 +334,30 @@ * It removes the last element in the cache. */ public void free() { + if(this.checkSeriazable(cache.get(this.mrulist.getLast()))) { + this.writerstack.push(new TmpStackObject(this.mrulist.getLast(),cache.get(this.mrulist.getLast()))); + } this.cache.remove(this.mrulist.getLast()); this.mrulist.removeLast(); } + + /** + * This method checks if an object is seriazable + */ + private boolean checkSeriazable(Object object) { + try { + if((object.getClass().getName().equals("CachedEventObject")) + || (object.getClass().getName().equals("org.apache.cocoon.caching.CachedStreamObject")) + || (ClassUtils.implementsInterface(object.getClass().getName(),"org.apache.cocoon.caching.CacheValidity"))) { + return true; + } else { + return false; + } + } catch (Exception e) { + getLogger().error("Error in MRUMemoryStore!",e); + return false; + } + } /** * Container object for the documents. @@ -231,18 +365,45 @@ class CacheObject { private long time = -1; private Object cacheObject; - - public CacheObject(Object ToCacheObject, long lTime) { + private boolean serialised; + + public CacheObject(Object ToCacheObject, long lTime, boolean serialised) { this.cacheObject = ToCacheObject; this.time = lTime; + this.serialised = serialised; } - + public Object getCacheObject() { return this.cacheObject; } public long getCreateTime() { return this.time; + } + + public boolean getSerialisedFlag() { + return this.serialised; + } + } + + /** Temporary container object for the writerstack */ + class TmpStackObject { + private Object object; + private Object key; + + public TmpStackObject (Object key, Object object) { + this.object = object; + this.key = key; + } + + public TmpStackObject() {} + + public Object getKey() { + return this.key; + } + + public Object getObject() { + return this.object; } } } ---------------------------------------------------------------------- In case of troubles, e-mail: [EMAIL PROTECTED] To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]