froehlich 01/12/12 16:04:34
Added: scratchpad/src/org/apache/cocoon/jispstore
MRUMemoryStore.java JispKey.java
JispFilesystemStore.java FilesystemQueueImpl.java
Log:
started to implement a Jisp based FilesystemStore, stand by for more.
Revision Changes Path
1.1
xml-cocoon2/scratchpad/src/org/apache/cocoon/jispstore/MRUMemoryStore.java
Index: MRUMemoryStore.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.jispstore;
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.thread.ThreadSafe;
import org.apache.cocoon.Constants;
import org.apache.cocoon.components.store.FilesystemQueue;
import org.apache.cocoon.components.store.FilesystemQueueObject;
import org.apache.cocoon.components.store.Store;
import org.apache.cocoon.components.store.StoreJanitor;
import org.apache.cocoon.util.ClassUtils;
import org.apache.cocoon.util.IOUtils;
import org.apache.regexp.RE;
import org.apache.regexp.RESyntaxException;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.LinkedList;
/**
* 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 idea was taken from the "Writing Advanced Application Tutorial" from
* javasoft. Many thanx to the writers!
*
* @author <a href="mailto:[EMAIL PROTECTED]">Gerhard Froehlich</a>
* @author <a href="mailto:[EMAIL PROTECTED]">Davanum Srinivas</a>
*/
public final class MRUMemoryStore
extends AbstractLoggable
implements Store,
Configurable,
ThreadSafe,
Composable,
Disposable {
private int maxobjects;
private boolean filesystem;
private Hashtable cache;
private LinkedList mrulist;
private File cachefile;
private Store fsstore;
private StoreJanitor storejanitor;
private FilesystemQueue filesystemQueue;
private ComponentManager manager;
/**
* Get components of the ComponentManager
*
* @param the ComponentManager
*/
public void compose(ComponentManager manager) throws ComponentException {
this.manager = manager;
getLogger().debug("Looking up " + Store.ROLE + "/JispFilesystemStore");
this.fsstore = (Store)manager.lookup(Store.ROLE + "/JispFilesystemStore");
getLogger().debug("Looking up " + StoreJanitor.ROLE);
this.storejanitor = (StoreJanitor)manager.lookup(StoreJanitor.ROLE);
getLogger().debug("Looking up " + FilesystemQueue.ROLE);
this.filesystemQueue =
(FilesystemQueue)manager.lookup(FilesystemQueue.ROLE);
}
/**
* Initialize the MRUMemoryStore.
* 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>
* </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);
if ((this.maxobjects < 1)) {
throw new ConfigurationException("MRUMemoryStore maxobjects must be at
least 1 milli second!");
}
this.cache = new Hashtable((int)(this.maxobjects * 1.2));
this.mrulist = new LinkedList();
this.storejanitor.register(this);
}
/**
* Dispose the component
*/
public void dispose() {
if (this.manager != null) {
this.manager.release(this.storejanitor);
this.storejanitor = null;
this.manager.release(this.filesystemQueue);
this.filesystemQueue = null;
this.manager.release(this.fsstore);
this.fsstore = null;
}
}
/**
* Store the given object in a persistent state. It is up to the
* caller to ensure that the key has a persistent state across
* different JVM executions.
*
* @param the key for the object to store
* @param the object to store
*/
public void store(Object key, Object value) {
this.hold(key,value);
}
/**
* This method holds the requested object in a HashMap combined
* with a LinkedList to create the MRU.
* It also stores objects onto the filesystem if configured.
*
* @param the key of the object to be stored
* @param the object to be stored
*/
public void hold(Object key, Object value) {
getLogger().debug("Holding object in memory. key: " + key);
getLogger().debug("Holding object in memory. value: " + value);
/** ...first test if the max. objects in cache is reached... */
while (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.checkSerializable(value) &&
!this.fsstore.containsKey(key)) {
this.getLogger().debug("Storing object on fs");
try {
this.filesystemQueue.insert(new
FilesystemQueueObject(key,value));
} catch(Exception e) {
this.getLogger().error("Error storing Object on fs",e);
}
}
}
/** ..put the new object in the cache, on the top of course ... */
this.cache.put(key, value);
this.mrulist.remove(key);
this.mrulist.addFirst(key);
this.getLogger().debug("Cache size=" + cache.size());
}
/**
* Get the object associated to the given unique key.
*
* @param the key of the requested object
* @return the requested object
*/
public Object get(Object key) {
this.getLogger().debug("Getting object from memory. Key: " + key);
Object tmpobject = this.cache.get(key);
if ( tmpobject != null ) {
/** put the accessed key on top of the linked list */
this.mrulist.remove(key);
this.mrulist.addFirst(key);
return tmpobject;
}
this.getLogger().debug("Object not found in memory");
/** try to fetch from filesystem */
if(this.filesystem) {
tmpobject = this.fsstore.get(key);
if (tmpobject == null) {
this.getLogger().debug( "Object was NOT found on fs. Looked for: "
+ key);
return null;
} else {
this.getLogger().debug("Object was found on fs");
try {
tmpobject = IOUtils.deserializeObject((File)tmpobject);
if(!this.cache.containsKey(key)) {
this.hold(key,tmpobject);
}
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 null;
}
}
}
return null;
}
/**
* Remove the object associated to the given key.
*
* @param the key of to be removed object
*/
public void remove(Object key) {
this.getLogger().debug("Removing object from store");
this.cache.remove(key);
this.mrulist.remove(key);
if(this.filesystem && key != null) {
this.fsstore.remove(key);
}
}
/**
* Indicates if the given key is associated to a contained object.
*
* @param the key of the object
* @return true if the key exists
*/
public boolean containsKey(Object key) {
if(filesystem) {
return (this.cache.containsKey(key) || this.fsstore.containsKey(key));
} else {
return this.cache.containsKey(key);
}
}
/**
* Returns the list of used keys as an Enumeration.
*
* @return the enumeration of the cache
*/
public Enumeration keys() {
return null;
}
/**
* Frees some of the fast memory used by this store.
* It removes the last element in the store.
*/
public void free() {
try {
if(this.cache.size() > 0) {
this.getLogger().debug("Freeing cache");
this.cache.remove(this.mrulist.getLast());
this.mrulist.removeLast();
this.getLogger().debug("Cache size=" + cache.size());
}
} catch (Exception e) {
this.getLogger().error("Error in free()", e);
}
}
/**
* This method checks if an object is seriazable.
* FIXME: In the moment only CachedEventObject or
* CachedStreamObject are stored.
*
* @param the object to be checked
* @return true if the object is storeable
*/
private boolean checkSerializable(Object object) {
try {
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")))
{
return true;
} else {
return false;
}
} catch (Exception e) {
this.getLogger().error("Error in checkSerializable()!", e);
return false;
}
}
}
1.1
xml-cocoon2/scratchpad/src/org/apache/cocoon/jispstore/JispKey.java
Index: JispKey.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.jispstore;
import com.coyotegulch.jisp.KeyObject;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
final class JispKey extends KeyObject{
private String mKey;
public JispKey() {
mKey = null;
}
public JispKey(String keyValue) {
mKey = keyValue;
}
public int compareTo(KeyObject key) {
int code = -1;
if (key instanceof JispKey) {
code = System.identityHashCode(this) -
System.identityHashCode(key);
if(code < 0) {
code = KEY_LESS;
} else if(code > 0) {
code = KEY_MORE;
} else {
code = KEY_EQUAL;
}
}
return code;
}
public KeyObject makeNullKey() {
JispKey nullKey = new JispKey();
return nullKey;
}
public void writeExternal(ObjectOutput out) throws IOException {
out.writeObject(mKey);
}
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
mKey = (String)in.readObject();
}
public boolean equals(Object obj)
{
if ((obj != null) && (obj instanceof JispKey))
return (mKey.equals(((JispKey)obj).mKey));
else
return false;
}
public String toString() {
return "Key: " + mKey;
}
}
1.1
xml-cocoon2/scratchpad/src/org/apache/cocoon/jispstore/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.jispstore;
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.activity.Initializable;
import org.apache.avalon.framework.logger.AbstractLoggable;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.Constants;
import org.apache.cocoon.util.IOUtils;
import org.apache.cocoon.components.store.Store;
import com.coyotegulch.jisp.IndexedObjectDatabase;
import com.coyotegulch.jisp.BTreeIndex;
import com.coyotegulch.jisp.KeyObject;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.Enumeration;
public final class JispFilesystemStore
extends AbstractLoggable
implements Store,
Contextualizable,
ThreadSafe,
Initializable {
/** The directory repository */
protected File directoryFile;
protected volatile String directoryPath;
/** some statics for the moment */
private static String DATABASE_NAME = "cocoon.data";
private static String INDEX_NAME = "cocoon.idx";
private static int ORDER = 33;
/** The database */
private IndexedObjectDatabase database;
private BTreeIndex index;
/**
* Sets the repository's location
*/
public void setDirectory(final String directory)
throws IOException {
this.setDirectory(new File(directory));
}
public void contextualize(final Context context)
throws ContextException {
try {
setDirectory((File) context.get(Constants.CONTEXT_WORK_DIR));
} catch (Exception e) {
// ignore
}
}
public void initialize() {
/** determine datafile */
getLogger().debug("initialize() JispFilesystemStore");
try {
getLogger().debug("Path to data file=" + this.getDirectoryPath() +
DATABASE_NAME);
File myFile = new File(this.getDirectoryPath() + DATABASE_NAME);
getLogger().debug("File exists=" + myFile.exists());
if (myFile.exists()) {
this.getLogger().debug("Data file exists");
database = new IndexedObjectDatabase(getDirectoryPath() +
DATABASE_NAME,false);
index = new BTreeIndex(this.getDirectoryPath() + INDEX_NAME);
database.attachIndex(index);
index.dumpTree();
} else {
this.getLogger().debug("Data file not exists");
database = new IndexedObjectDatabase(getDirectoryPath() +
DATABASE_NAME,false);
index = new BTreeIndex(this.getDirectoryPath() + INDEX_NAME,
ORDER, new JispKey(),false);
database.attachIndex(index);
index.dumpTree();
}
} catch (Exception e) {
getLogger().error("initialize(..) Exception",e);
}
}
/**
* Sets the repository's location
*/
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;
/* Does directory exist? */
if (!this.directoryFile.exists()) {
/* Create it anew */
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"
);
}
}
/**
* Returns the repository's full pathname
*/
public String getDirectoryPath() {
return this.directoryPath;
}
public Object get(Object key) {
this.getLogger().debug("get(): Get file with key: " + key.toString());
Object readObj = null;
try {
readObj = database.read(new JispKey(key.toString()),index);
if(readObj != null) {
this.getLogger().debug("get(): FOUND!!= " + readObj);
} else {
this.getLogger().debug("get(): NOT FOUND!!= " + readObj);
}
} catch (Exception e) {
getLogger().error("get(..): Exception",e);
}
return readObj;
}
public void store(Object key, Object value) throws IOException {
this.getLogger().debug("store(): Store file with key: " + key.toString());
//this.remove(key);
try {
KeyObject[] keyArray = new JispKey[3];
keyArray[0] = new JispKey(key.toString());
keyArray[1] = new JispKey(key.toString());
keyArray[2] = new JispKey(key.toString());
database.write(keyArray,(Serializable)value);
} catch (Exception e) {
this.getLogger().error("store(..): Exception",e);
}
}
public void hold(Object key, Object value) throws IOException {
this.store(key,value);
}
public void free() {
}
public void remove(Object key) {
this.getLogger().debug("remove(..) Remove item");
try {
KeyObject[] keyArray = new JispKey[3];
keyArray[0] = new JispKey(key.toString());
keyArray[1] = new JispKey(key.toString());
keyArray[2] = new JispKey(key.toString());
database.remove(keyArray);
} catch (Exception e) {
this.getLogger().error("remove(..): Exception",e);
}
}
public boolean containsKey(Object key) {
return false;
}
public Enumeration keys() {
return null;
}
}
1.1
xml-cocoon2/scratchpad/src/org/apache/cocoon/jispstore/FilesystemQueueImpl.java
Index: FilesystemQueueImpl.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.jispstore;
import org.apache.avalon.framework.activity.Disposable;
import org.apache.avalon.framework.activity.Startable;
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.logger.AbstractLoggable;
import org.apache.avalon.framework.parameters.Parameters;
import org.apache.avalon.framework.thread.ThreadSafe;
import org.apache.cocoon.Constants;
import org.apache.cocoon.components.store.FilesystemQueue;
import org.apache.cocoon.components.store.FilesystemQueueObject;
import org.apache.cocoon.components.store.Store;
import org.apache.cocoon.util.IOUtils;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.LinkedList;
import java.util.ListIterator;
import java.util.NoSuchElementException;
/**
* This class provides a asynchron queue for storing objects on the filesystem.
* Every store component can uses this component to store object in the
* configured cache directory.
*
* @author <a href="mailto:[EMAIL PROTECTED]">Gerhard Froehlich</a>
*/
public final class FilesystemQueueImpl
extends AbstractLoggable
implements FilesystemQueue,
Configurable,
Runnable,
ThreadSafe,
Composable,
Disposable,
Startable {
private int handlerinterval;
private int threadpriority;
private int maxobjects;
private LinkedList linkedList;
private Store fsstore;
private Thread fsQueueHandler;
private static boolean doRun = false;
private ComponentManager manager;
/**
* Get components of the ComponentManager
*
* @param the ComponentManager
*/
public void compose(ComponentManager manager) throws ComponentException {
this.manager = manager;
if (this.getLogger().isDebugEnabled() == true) {
getLogger().debug("Looking up " + Store.ROLE + "/JispFilesystemStore");
}
this.fsstore = (Store)manager.lookup(Store.ROLE + "/JispFilesystemStore");
}
/**
* Dispose the component
*/
public void dispose() {
if (this.manager != null) {
this.manager.release(this.fsstore);
this.fsstore = null;
}
}
/**
* Configure the Filesystem Queue:
* <UL>
* <LI>handlerinterval = Interval of the Queue Handler Thread</LI>
* <LI>threadpriority = Priority of the Queue Handler Thread</LI>
* >LI>maxobjects = Defines the max. numbers of Objects in the queue</LI>
* </UL>
*/
public void configure(Configuration conf) throws ConfigurationException {
Parameters params = Parameters.fromConfiguration(conf);
this.handlerinterval = params.getParameterAsInteger("handlerinterval",10);
this.threadpriority = params.getParameterAsInteger("threadpriority",5);
this.maxobjects = params.getParameterAsInteger("maxobjects",100);
if (this.getLogger().isDebugEnabled() == true) {
this.getLogger().debug("Configure Filesystem Queue");
this.getLogger().debug("handlerinterval=" + handlerinterval);
this.getLogger().debug("threadpriority=" + threadpriority);
}
this.linkedList = new LinkedList();
}
public void start() {
doRun = true;
this.fsQueueHandler = new Thread(this);
this.fsQueueHandler.setDaemon(true);
this.fsQueueHandler.setPriority(this.threadpriority);
this.fsQueueHandler.setName("fsQueueHandler");
this.fsQueueHandler.start();
}
public void stop() {
doRun = false;
}
public void run() {
while (doRun) {
while(!this.isEmpty()) {
FilesystemQueueObject filesystemQueueObject =
(FilesystemQueueObject)this.pop();
try {
this.fsstore.store(filesystemQueueObject.getKey(),
filesystemQueueObject.getObject()
);
} catch(IOException e) {
this.getLogger().error("Error in fsQueueHandler",e);
}
}
try {
Thread.currentThread().sleep(this.handlerinterval * 1000);
} catch (InterruptedException ignore) {}
}
}
/**
* Clear all elements from queue.
*/
public void clear() {
if (this.getLogger().isDebugEnabled() == true) {
this.getLogger().debug("Clearing the FilesystemQueue");
}
ListIterator listIterator = linkedList.listIterator(0);
while(listIterator.hasNext()) {
this.linkedList.remove(listIterator.nextIndex());
}
}
/**
* Test if queue is empty.
*
* @return true if queue is empty else false.
*/
public boolean isEmpty() {
return (this.linkedList.size() == 0);
}
/**
* Insert an element into queue.
*
* @param element the element to be inserted
*/
public void insert(Object element) {
if(this.linkedList.size() < maxobjects) {
if (this.getLogger().isDebugEnabled() == true) {
this.getLogger().debug("Insert Element in FilesystemQueue");
}
this.linkedList.addFirst(element);
this.reportSize();
} else {
this.getLogger().warn("Filesystem Queue full!");
}
}
/**
* Return element on top of heap but don't remove it.
*
* @return the element at top of heap
* @exception NoSuchElementException if isEmpty() == true
*/
public Object peek() throws NoSuchElementException {
if (this.getLogger().isDebugEnabled() == true) {
this.getLogger().debug("Peek Element in FilesystemQueue");
}
return (Comparable)linkedList.getLast();
}
/**
* Return element on top of heap and remove it.
*
* @return the element at top of heap
* @exception NoSuchElementException if isEmpty() == true
*/
public Object pop() throws NoSuchElementException {
if (this.getLogger().isDebugEnabled() == true) {
this.getLogger().debug("Pop Element in FilesystemQueue");
}
return (Comparable)linkedList.removeLast();
}
public void reportSize() {
if (this.getLogger().isDebugEnabled() == true) {
this.getLogger().debug("Size of FilesystemQueue=" +
this.linkedList.size());
}
}
}
----------------------------------------------------------------------
In case of troubles, e-mail: [EMAIL PROTECTED]
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]