// Resourcestoremanager.java
// $Id: ResourceStoreManager.java,v 1.33 2007/02/09 22:27:25 ylafon Exp $
// (c) COPYRIGHT MIT and INRIA, 1996.
// Please first read the full copyright statement in file COPYRIGHT.html
package org.w3c.tools.resources.store ;
import org.w3c.tools.resources.AttributeHolder;
import org.w3c.tools.resources.InvalidResourceException;
import org.w3c.tools.resources.Resource;
import org.w3c.tools.resources.ResourceContext;
import org.w3c.tools.resources.ResourceReference;
import org.w3c.tools.resources.ResourceSpace;
import org.w3c.tools.resources.ServerInterface;
import org.w3c.tools.resources.SpaceEntry;
import org.w3c.tools.resources.serialization.Serializer;
import org.w3c.tools.resources.event.ResourceEventQueue;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.FileOutputStream;
import java.io.OutputStreamWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.PrintStream;
import java.io.Reader;
import java.io.Serializable;
import java.io.Writer;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;
import org.w3c.util.AsyncLRUList;
import org.w3c.util.LRUAble;
import org.w3c.util.LRUList;
import org.w3c.util.Status;
class Reference implements ResourceReference {
public static boolean debug = false;
// Our entry
StoreEntry entry = null;
// The resource identifier
String identifier = null;
// The default attributs
Hashtable defs = null;
public void updateContext(ResourceContext ctxt) {
if (defs != null)
defs.put("context", ctxt);
}
/**
* The lock count associated to the reference.
*/
protected int lockCount = 0;
public int nbLock() {
return lockCount;
}
protected void invalidate() {
entry = null;
}
protected boolean isValid() {
return ( entry != null ) ;
}
public Resource unsafeLock()
throws InvalidResourceException
{
return lock();
}
/**
* Lock that reference in memory.
* @return A pointer to the underlying resource, after lock succeed.
*/
public synchronized Resource lock()
throws InvalidResourceException
{
lockCount++;
if (entry == null)
throw new InvalidResourceException(identifier,
"This reference was invalidate");
ResourceStore store = entry.getStore();
Resource resource = store.lookupResource(identifier);
if (debug) {
if (defs.get("context") == null)
System.out.println("**** Context null for : "+identifier);
else if (((ResourceContext)(defs.get("context"))).getServer()
== null)
System.out.println("**** Server null for "+
identifier+"'s context");
}
if (resource == null) {
resource = store.loadResource(identifier, defs);
}
if (debug)
System.out.println("[LOCK] locking ["+lockCount+"]: "+identifier);
return resource;
}
/**
* Unlock that resource reference.
*/
public void unlock() {
lockCount--;
if (debug)
System.out.println("[LOCK] unlocking ["+lockCount+"]: "+
identifier);
}
/**
* Is that resource locked ?
* @return A boolean, <strong>true</strong> if the resource is locked.
*/
public boolean isLocked() {
return lockCount != 0;
}
Reference (StoreEntry entry, String identifier, Hashtable defs) {
this.entry = entry;
this.identifier = identifier;
this.defs = defs;
}
}
class StoreEntry implements LRUAble, Serializable {
boolean istransient = false;
// our key in the cache.
Integer key = null;
// LRU management infos:
transient LRUAble next = null ;
transient LRUAble prev = null ;
// ResourceStore infos:
transient ResourceStore store = null ;
// References
transient Hashtable references = null;
//The manager
transient ResourceStoreManager manager = null;
String repository = null ;
transient File rep = null;
public File getRepository() {
if (rep == null)
rep = new File(manager.storedir, repository);
return rep;
}
public LRUAble getNext() {
return next;
}
public LRUAble getPrev() {
return prev;
}
public void setNext(LRUAble next) {
this.next = next;
}
public void setPrev(LRUAble prev) {
this.prev = prev;
}
public void setTransient(boolean onoff) {
istransient = onoff;
}
public boolean isTransient() {
return istransient;
}
/**
* Load the store of this entry.
*/
synchronized ResourceStore getStore() {
if ( store == null ) {
store = new ResourceStoreImpl() ;
store.initialize(manager, this, getRepository(),
manager.serializer) ;
manager.incrLoadedStore();
}
return store;
}
/**
* Delete the store of this entry
*/
synchronized void deleteStore() {
Enumeration e = references.elements();
Reference rr = null;
while (e.hasMoreElements()) {
rr = (Reference) e.nextElement();
rr.invalidate();
}
getRepository().delete();
if (store != null) {
store = null;
manager.decrLoadedStore();
}
references = null;
}
/**
* Lookup this resource.
* @param identifier The resource identifier.
* @return A Resource instance, or <strong>null</strong> if either the
* resource doesn't exist, or it isn't loaded yet.
*/
public ResourceReference lookupResource (String identifier) {
return (ResourceReference)references.get(identifier);
}
/**
* Load a resource, or get one from the cache.
* @param identifier The resource identifier.
* @return A Resource instance, or <strong>null</strong> if the resource
* doesn't exist in that storeEntry.
* @exception InvalidResourceException If the resource couldn't be
* restored from its pickled format.
*/
synchronized ResourceReference loadResource(String name, Hashtable defs) {
ResourceReference rr = lookupResource(name);
if (rr != null)
return rr;
rr = new Reference(this, name, defs);
try {
Resource res = rr.lock();
if (res == null)
return null;
} catch (InvalidResourceException ex) {
return null;
} finally {
rr.unlock();
}
references.put(name, rr);
return rr;
}
/**
* Save a given resource.
* @param resource The resource to be save right now.
*/
synchronized void saveResource(Resource resource) {
getStore();
store.saveResource(resource);
}
/**
* Add a new resource to the resource store.
* @param resource The resource to add.
*/
synchronized ResourceReference addResource(Resource resource,
Hashtable defs) {
getStore();
store.addResource(resource);
String name = resource.getIdentifier();
ResourceReference rr = new Reference(this, name , defs);
references.put(name, rr);
return rr;
}
/**
* FIXME doc
*/
public synchronized void markModified(Resource resource) {
getStore();
store.markModified(resource);
}
/**
* Rename a resource in this resource store.
* @param identifier The identifier of the resource to be renamed.
*/
public synchronized void renameResource(String oldid, String newid) {
getStore();
store.renameResource(oldid, newid);
Reference rr = (Reference)lookupResource(oldid);
if (rr != null) {
rr.identifier = newid;
references.remove(oldid);
references.put(newid, rr);
}
}
public synchronized Enumeration enumerateResourceIdentifiers() {
getStore();
return store.enumerateResourceIdentifiers();
}
/**
* Remove a resource from this resource store.
* @param identifier The identifier of the resource to be removed.
*/
public synchronized void removeResource(String identifier) {
getStore();
Reference rr = (Reference)references.get(identifier);
if (rr != null) {
references.remove(identifier);
manager.getEventQueue().removeSourceEvents(rr);
rr.invalidate();
}
store.removeResource(identifier);
}
/**
* Try unloading the space for this entry.
*/
synchronized boolean unloadStore() {
if ( store != null ) {
Enumeration e = references.elements();
ResourceReference rr = null;
while (e.hasMoreElements()) {
rr = (ResourceReference) e.nextElement();
if (rr.isLocked())
return false;
}
// Will the store unload itself ?
if ( ! store.acceptUnload())
return false;
// Great, proceed:
shutdownStore();
}
return true;
}
/**
* Shutdown the store.
*/
synchronized void shutdownStore() {
if ( store != null ) {
store.shutdown() ;
store = null ;
references = new Hashtable(); // clear also the references
manager.decrLoadedStore();
}
}
/**
* Try stabilizing the store.
*/
synchronized void saveStore() {
if ( store != null ) {
// Save the resource store:
store.save();
}
}
void initialize(ResourceStoreManager manager) {
this.manager = manager;
this.references = new Hashtable();
}
StoreEntry(ResourceStoreManager manager, String repository, Integer key) {
this.manager = manager;
this.store = null ;
this.repository = repository ;
this.key = key;
this.references = new Hashtable();
}
StoreEntry(ResourceStoreManager manager, File repository, Integer key) {
this.manager = manager;
this.store = null ;
this.rep = repository ;
this.key = key;
this.references = new Hashtable();
}
}
class StoreManagerSweeper extends Thread {
ResourceStoreManager manager = null ;
boolean killed = false ;
protected synchronized void waitEvent() {
boolean done = false ;
// Wait for an event to come by:
while ( ! done ) {
try {
wait() ;
done = true ;
} catch (InterruptedException ex) {
}
}
}
protected synchronized void sweep() {
notifyAll() ;
}
protected synchronized void shutdown() {
killed = true ;
notifyAll() ;
}
public void run() {
while ( true ) {
waitEvent() ;
// The whole trick is to run the collect method of the store
// manager, without having the lock on the sweeper itself, so
// that clients can still trigger it later
if ( killed ) {
break ;
} else {
try {
manager.collect() ;
} catch (Exception ex) {
// We really don't want this thread to die
ex.printStackTrace() ;
}
}
}
}
StoreManagerSweeper(ResourceStoreManager manager) {
this.manager = manager ;
this.setName("StoreSweeper") ;
this.start() ;
}
}
public class ResourceStoreManager implements ResourceSpace {
public static boolean debug = false;
/**
* Number of sub-levels file system directories in the store directory.
*/
public final static int SUBDIRS = 128;
public static final String ROOT_REP = "root.xml";
public static final String STATE_F = "state.xml";
public boolean debugMemory = false;
protected Serializer serializer = null;
/**
* The loaded resource stores entries.
*/
Hashtable entries = new Hashtable(256); // <sentryKey - StoreEntry>
/**
* The limit of "BIG" stores, over this limits stores will be kept
* in memory for performance purposes
* If set to -1, this optimization won't happen
*/
private int storeSizeLimit = -1;
/**
* The max number of loaded stores
*/
private int maxLoadedStore = -1;
/**
* The number of loaded stores
*/
private int loadedStore = 0;
/**
* Is this store shutdown ?
*/
protected boolean closed = false ;
/**
* Our store directory.
*/
protected File storedir = null;
/**
* Our index file.
*/
protected File index = null;
/**
* server name;
*/
protected String server_name = null;
/**
* Our root repository.
*/
protected File root_repository = null;
/**
* The store entries least recetenly used list.
*/
protected LRUList lru = null;
// FIXME doc
protected ResourceStoreState state = null;
/**
* Our sweeper thread:
*/
protected StoreManagerSweeper sweeper = null ;
protected ResourceEventQueue eventQueue = null;
public ResourceEventQueue getEventQueue() {
if (eventQueue == null)
eventQueue = new ResourceEventQueue();
return eventQueue;
}
protected final int getMaxLoadedStore() {
return maxLoadedStore ;
}
protected final int getStoreSizeLimit() {
return storeSizeLimit;
}
protected synchronized void incrLoadedStore() {
loadedStore++;
checkMaxLoadedStore();
}
protected synchronized void decrLoadedStore() {
loadedStore--;
}
/**
* Check that this resource store manager isn't closed.
* @exception RuntimeException If the store manager was closed.
*/
protected final synchronized void checkClosed() {
if ( closed )
throw new RuntimeException("Invalid store manager access.") ;
}
/**
* Pick the least recently used entry, and remove all links to it.
* After this method as run, the least recently used entry for some store
* will be returned. The store manager will have discarded all its link to
* it, and the entry shutdown will have to be performed by the caller.
* @return An StoreEntry instance, to be cleaned up.
*/
protected NewStoreEntry pickLRUEntry() {
NewStoreEntry entry;
synchronized (lru) {
entry = (NewStoreEntry) lru.removeTail();
}
return entry;
}
/**
* Collect enough entries to go back into fixed limits.
*/
public void collect() {
if (debugMemory)
System.out.println("[MEMORY] start sweeper <- "+
loadedStore+" stores loaded.");
NewStoreEntry watchdog = null;
NewStoreEntry entry = null;
int maxload = getMaxLoadedStore();
while ( loadedStore > maxload ) {
entry = pickLRUEntry();
if (( entry != null ) && (entry != watchdog)) {
synchronized(entry) {
if (! entry.unloadStore()) {
synchronized (lru) {
lru.toHead(entry);
if (watchdog == null)
watchdog = entry;
}
}
}
} else {
break;
}
}
if (debugMemory)
System.out.println("[MEMORY] sweeper done -> "+
loadedStore+" stores still loaded.");
}
/**
* Create a resource store repository name.
* This method will return a new resource store repository key. When
* used in conjunction with the <code>loadResourceStore</code> method
* that takes a key as a parameter, this allows to caller to abstract
* itself from the physical location of the repository.
* @return A fresh resource store key, guaranteed to be uniq.
*/
public synchronized String createResourceStoreRepository() {
int key = state.getNextKey();
return (key%SUBDIRS)+"/st-" + key;
}
public int getCurrentStoreIdentifier() {
return state.getCurrentKey();
}
protected boolean checkSubDirs() {
for (int i = 0 ; i < SUBDIRS ; i++) {
File subd = new File(storedir, Integer.toString(i));
if ((! subd.exists()) && ! subd.mkdirs())
return false;
}
return true;
}
/**
* Get the index file.
* @return A File instance.
*/
protected File getIndexFile() {
if (index == null)
index = new File(storedir,server_name+"-index.xml");
return index;
}
/**
* Get the index file.
* @return A File instance.
*/
protected File getOldIndexFile() {
if (index == null)
index = new File(storedir,server_name+"-index");
return index;
}
/**
* Get the root repository.
* @return A File instance.
*/
protected File getRootRepository() {
if (root_repository == null)
root_repository = new File(storedir,ROOT_REP);
return root_repository;
}
/**
* Get The root key.
* @return A String instance
*/
protected Integer getRootKey() {
return new Integer((new String("root")).hashCode());
}
/**
* Emit the given string as a warning, to whoever it is appropriate.
* @param msg The warning message.
*/
protected void warning(String msg) {
System.out.println("********* WARNING *********");
System.out.println("[" + getClass().getName()+"]:\n" + msg) ;
System.out.println("***************************");
}
protected synchronized void saveNewEntriesIndex() {
File indexFile = getIndexFile();
String dir = indexFile.getParent();
String name = indexFile.getName();
File tmp = new File(dir, name+".tmp");
File bak = new File(dir, name+".bak");
File tild = new File(dir, name+".bak~");
Enumeration elements = entries.elements();
Vector v = new Vector(10);
while (elements.hasMoreElements()) {
NewStoreEntry entry = (NewStoreEntry) elements.nextElement();
if (! entry.isTransient())
v.addElement(entry);
}
NewStoreEntry se[] = new NewStoreEntry[v.size()];
v.copyInto(se);
try {
FileOutputStream fos = new FileOutputStream(tmp);
Writer writer = new BufferedWriter(
new OutputStreamWriter(fos,"UTF-8"));
serializer.writeResources(se, writer);
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("Unable to save entries index");
}
// 1st move: delete the ~ file if any:
if ( tild.exists() )
tild.delete() ;
// 2nd move rename bak to ~ (bak no longer exists)
if ( bak.exists() ) {
bak.renameTo(tild);
bak.delete();
}
// 3nd move: rename the current index to bak
if ( indexFile.exists() ) {
if ( !indexFile.renameTo(bak) ) {
warning("unable to rename "+indexFile+" to "+bak);
tild.renameTo(bak);
}
indexFile.delete();
}
// 4th move: rename the tmp file to index
if ( !tmp.renameTo(indexFile) ) {
bak.renameTo(indexFile) ;
tild.renameTo(bak);
warning("unable to rename "+tmp+" to "+indexFile);
}
// cleanup (erase the ~ file)
tild.delete() ;
}
protected void loadNewEntriesIndex() {
File indexFile = getIndexFile();
if (! indexFile.exists()) {
File bak = new File(indexFile.getParent(),
indexFile.getName()+".bak");
if (bak.exists()) {
warning(indexFile+" not found! using bak file :"+bak);
if ( !bak.renameTo(indexFile) ) {
warning("unable to rename "+bak+" to "+indexFile+
"\n Try by yourself or all your resources will be lost!");
System.exit(-1);
}
} else {
entries = new Hashtable(256);
return;
}
}
entries = new Hashtable(256);
try {
Reader reader = new BufferedReader(new FileReader(indexFile));
AttributeHolder holders[] =
serializer.readAttributeHolders(reader);
for (int i = 0 ; i < holders.length ; i++) {
NewStoreEntry entry = (NewStoreEntry) holders[i];
entries.put(entry.getKey(), entry);
synchronized (lru) {
lru.toHead((LRUAble) entry);
}
entry.initialize(this);
}
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("Unable to load entries index");
}
}
/**
* update the old index file. Load it and save it with the serializer.
* @param the index file (serialized hashtable of StoreEntry)
*/
public static synchronized
void updateEntriesIndex(File oldIndexFile,
File newIndexFile,
Serializer serializer)
throws IOException
{
Hashtable entries = new Hashtable();
if (! oldIndexFile.exists()) {
File bak = new File(oldIndexFile.getParent(),
oldIndexFile.getName()+".bak");
if (bak.exists()) {
System.out.println(oldIndexFile.getAbsolutePath()+
" not found! using bak file :"+bak);
if ( !bak.renameTo(oldIndexFile) ) {
System.out.println("unable to rename "+bak+" to "+
oldIndexFile+
"\n Try by yourself or all your resources will be lost!");
System.exit(-1);
}
} else {
return;
}
}
ObjectInputStream oi = null;
try {
oi = new ObjectInputStream(new BufferedInputStream
(new FileInputStream
(oldIndexFile)));
entries = (Hashtable) oi.readObject();
} catch (ClassNotFoundException ex) {
ex.printStackTrace();
throw new RuntimeException("Unable to load entries index");
} catch (IOException ex) {
ex.printStackTrace();
throw new RuntimeException("Unable to load entries index");
} finally {
if ( oi != null ) {
try { oi.close() ; } catch (Exception ex) {}
}
}
Enumeration e = entries.elements();
int len = 0;
while (e.hasMoreElements()) {
StoreEntry entry = (StoreEntry)e.nextElement();
if (entry.isTransient())
entries.remove(entry.key);
else {
len++;
String rep = entry.repository;
if (rep != null) {
if (rep.equals("root.idx")) {
entry.repository = "root.xml";
} else {
// st-xxx
int number = Integer.parseInt(rep.substring(3));
entry.repository = (number%SUBDIRS)+"/"+rep;
}
}
}
}
//StoreEntry => NewStoreEntry
e = entries.elements();
NewStoreEntry nentries[] = new NewStoreEntry[len];
int i = 0;
while (e.hasMoreElements()) {
StoreEntry entry = (StoreEntry)e.nextElement();
NewStoreEntry nentry = new NewStoreEntry(null,
entry.repository,
entry.key);
nentries[i++] = nentry;
}
FileOutputStream fos = new FileOutputStream(newIndexFile);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
Writer writer = new BufferedWriter(osw);
serializer.writeResources(nentries, writer);
}
/**
* Shutdown this resource store manager.
* Go through all entries, and shut them down.
*/
public synchronized void shutdown() {
saveNewEntriesIndex();
// Kill the sweeper thread:
sweeper.shutdown() ;
// Clenup all pending resource stores:
Enumeration e = entries.elements() ;
while ( e.hasMoreElements() ) {
NewStoreEntry entry = (NewStoreEntry) e.nextElement() ;
entry.shutdownStore() ;
entries.remove(entry.getKey()) ;
}
closed = true ;
// Now save our state:
File rmstate = new File(storedir, STATE_F);
try {
FileOutputStream fos = new FileOutputStream(rmstate);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
Writer writer = new BufferedWriter ( osw );
AttributeHolder statearray[] = { state };
serializer.writeResources(statearray, writer);
} catch (Exception ex) {
// FIXME !!
System.out.println("ResourceStoreManager: unable to save state !");
ex.printStackTrace();
}
}
/**
* Checkpoint all modified resource stores, by saving them to disk.
*/
public void checkpoint() {
try {
saveNewEntriesIndex();
} catch (Exception ex) {
System.out.println("*** Error during checkpoint");
}
// Checkpoint all loaded resource stores:
Enumeration e = entries.elements();
while ( e.hasMoreElements() ) {
NewStoreEntry entry = (NewStoreEntry) e.nextElement();
try {
entry.saveStore();
} catch (Exception ex) {
if (entry == null) {
System.out.println("*** Error, saving null entry!");
} else {
System.out.println("*** Error while saving store "
+ entry.getKey());
}
}
}
// Then save our state:
File rmstate = new File(storedir, STATE_F);
try {
FileOutputStream fos = new FileOutputStream(rmstate);
OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
Writer writer = new BufferedWriter ( osw );
AttributeHolder statearray[] = { state };
serializer.writeResources(statearray, writer);
} catch (Exception ex) {
// FIXME !!
System.out.println("ResourceStoreManager: unable to save state !");
ex.printStackTrace();
}
}
/**
* Mark the given store as having been used recently.
* @param token The token the resource store manager provided you when
* it initialized the store.
*/
public void markUsed(Object token) {
NewStoreEntry entry = (NewStoreEntry) token;
if ( entry != null ) {
synchronized (lru) {
lru.toHead(entry);
}
}
}
protected boolean used(String rep) {
Enumeration e = entries.elements();
NewStoreEntry entry = null;
while (e.hasMoreElements()) {
entry = (NewStoreEntry)e.nextElement();
System.out.println(storedir+":"+entry.getRepository());
try {
if (entry.getRepository().getName().equals(rep))
return true;
} catch (Exception ex) {
//continue
}
}
return false;
}
public void salvage() {
String stores[] = storedir.list();
if ( stores != null ) {
for (int i = 0 ; i < stores.length; i++) {
if ((stores[i].length() <= 3) || ! stores[i].startsWith("st-"))
continue;
if ( stores[i].endsWith(".bak") )
continue;
//Is there a storeEntry using this repository?
if (! used(stores[i])) {
// For testing now
System.err.println("*** "+stores[i]+" not used! deleted");
File notUsed = new File(storedir, stores[i]);
notUsed.delete();
}
}
}
}
public void displayIndex() {
Enumeration e = entries.elements();
NewStoreEntry entry = null;
System.out.println("Index : ");
while (e.hasMoreElements()) {
entry = (NewStoreEntry)e.nextElement();
System.out.println(entry.getKey()+" : "+entry.getRepository());
}
}
/**
* Try to salvage the resource store manager state.
* That's pretty easy and robust, it should really work well if no
* one hacked the store directory.
* @return A ResourceStoreState instance.
*/
public ResourceStoreState salvageState() {
System.err.println("*** salvaging resource manager state...");
File state = new File(storedir, STATE_F);
int maxid = -1;
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return (name.startsWith("st-") && (! name.endsWith(".bak")));
}
};
for (int i = 0 ; i < SUBDIRS ; i++) {
File subdir = new File(storedir, Integer.toString(i));
if (subdir.exists()) {
String stores[] = subdir.list(filter);
for (int j = 0 ; j < stores.length; j++) {
int id = Integer.parseInt(stores[j].substring(3));
maxid = Math.max(maxid, id);
}
}
}
++maxid;
System.err.println("*** resource store state salvaged, using: "+maxid);
return new ResourceStoreState(maxid);
}
/**
* Restore the resource whose name is given from the root NewStoreEntry.
* @param identifier The identifier of the resource to restore.
* @param defs Default attribute values.
*/
public ResourceReference loadRootResource(String identifier,
Hashtable defs)
{
NewStoreEntry entry = (NewStoreEntry) entries.get(getRootKey());
if (entry == null) {
synchronized (this) {
entry = (NewStoreEntry) entries.get(getRootKey());
if (entry == null) {
entry = new NewStoreEntry(this, ROOT_REP, getRootKey());
entries.put(getRootKey(), entry);
}
}
}
return entry.loadResource(identifier, defs);
}
/**
* Check that the key is not already in the list of references
* @return a boolean, true if the key is not there, false if it is
*/
public boolean checkKey(Integer key) {
if (entries != null) {
return !entries.containsKey(key);
}
return true;
}
/**
* Lookup this resource.
* @param sentry The resource space entry.
* @param identifier The resource identifier.
* @return A Resource instance, or <strong>null</strong> if either the
* resource doesn't exist, or it isn't loaded yet.
*/
public ResourceReference lookupResource(SpaceEntry sentry,
String identifier)
{
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException("Unable to lookup resource ("+
identifier+
"), no StoreEntry for its space entry");
return entry.lookupResource(identifier);
}
/**
* Restore the resource whose name is given.
* @param sentry The resource space entry.
* @param identifier The identifier of the resource to restore.
* @param defs Default attribute values.
*/
public ResourceReference loadResource(SpaceEntry sentry,
String identifier,
Hashtable defs)
{
if (debug) {
System.out.println("[RSM] loading ["+identifier+"] from ["+
sentry+']');
}
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException(identifier+": Space Entry not valid");
return entry.loadResource(identifier, defs);
}
/**
* Add this resource to the StoreEntry of the space entry.
* @param sentry The resource space entry.
* @param resource The resource to add.
* @param defs Default attribute values.
*/
public ResourceReference addResource(SpaceEntry sentry,
Resource resource,
Hashtable defs)
{
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException("Unable to add resource ("+
resource.getIdentifier()+
"), no StoreEntry for its space entry");
return entry.addResource(resource, defs);
}
/**
* Save this resource to the StoreEntry of the space entry.
* @param sentry The resource space entry
* @param resource The resource to save.
*/
public void saveResource(SpaceEntry sentry,
Resource resource)
{
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException("Unable to save resource ("+
resource.getIdentifier()+
"), no StoreEntry for its space entry");
entry.saveResource(resource);
}
/**
* Mark the given resource as being modified.
* @param sentry The resource space entry.
* @param resource The resource to mark as modified.
*/
public void markModified(SpaceEntry sentry,
Resource resource)
{
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException("Unable to mark resource ("+
resource.getIdentifier()+
"), no StoreEntry for its space entry");
synchronized (resource) {
entry.markModified(resource);
}
}
/**
* Rename a resource in this resource space.
* @param sentry The resource space entry.
* @param oldid The old resorce identifier.
* @param newid The new resorce identifier.
*/
public void renameResource(SpaceEntry sentry,
String oldid,
String newid)
{
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException("Unable to rename resource ("+
oldid+" to "+newid+
"), no StoreEntry for its space entry");
entry.renameResource(oldid, newid);
}
/**
* delete this resource from the StoreEntry (and the repository).
* @param sentry The resource space entry
* @param resource The resource to delete.
*/
public void deleteResource(SpaceEntry sentry,
Resource resource)
{
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException("Unable to delete resource ("+
resource.getIdentifier()+
"), no StoreEntry for its space entry");
entry.removeResource(resource.getIdentifier());
}
/**
* Delete all the children of resource indentified by its
* space entry.
* @param sentry The resource space entry
*/
public void deleteChildren(SpaceEntry sentry) {
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException(
"Unable to delete children, no StoreEntry"+
" for its space entry");
entry.deleteStore();
entries.remove(sentry.getEntryKey());
}
/**
* Save all the children of the resource indentified by its
* spaec entry.
* @param sentry The resource space entry
*/
public void saveChildren(SpaceEntry sentry) {
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException(
"Unable to save children, no StoreEntry"+
" for its space entry");
entry.saveStore();
}
protected void checkMaxLoadedStore() {
// Check to see if we have exceeded our quota:
if (loadedStore > getMaxLoadedStore()) {
sweeper.sweep() ;
}
}
/**
* acquire children from an external file.
* @param sentry The resource space entry.
* @param repository The file used to store children.
*/
public void acquireChildren(SpaceEntry sentry,
File repository,
boolean transientFlag)
{
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null) {
//create it.
checkMaxLoadedStore();
entry = new NewStoreEntry(this, repository, sentry.getEntryKey());
entry.setTransient(transientFlag);
entries.put(sentry.getEntryKey(), entry);
// FIXME : unload index hashtable ?
} else {
entry.rep = repository;
}
}
/**
* Acquire the StoreEntry of the space entry.
* @param sentry The resource space entry.
*/
public void acquireChildren(SpaceEntry sentry) {
//
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null) {
//create it.
checkMaxLoadedStore();
String rpath = createResourceStoreRepository();
File repository = new File(storedir, rpath);
if (repository.exists()) {
String fpath = new String(rpath);
String lpath = null;
do {
lpath = new String(rpath);
rpath = createResourceStoreRepository();
repository = new File(storedir, rpath);
} while (repository.exists());
if (fpath.equals(lpath))
warning("repository "+fpath+" exists! using "+rpath);
else
warning("repositories "+fpath+" to "+
lpath+" exists! using "+rpath);
}
entry = new NewStoreEntry(this, rpath, sentry.getEntryKey());
entries.put(sentry.getEntryKey(), entry);
// FIXME : unload index hashtable ?
}
}
/**
* Implementation of Status interface
* Display statistics about usage of the resource store manager
* @returns a String containing a fragment of HTML
*/
public String getHTMLStatus() {
Enumeration elements = entries.elements();
int nb_entries = 0;
int entries_nn = 0;
int nb_entries_store = 0;
int stores_nn = 0;
int oversized = 0;
while (elements.hasMoreElements()) {
int nb;
NewStoreEntry entry = (NewStoreEntry) elements.nextElement();
if (entry.references != null) {
nb = entry.references.size();
if (nb > 0) {
nb_entries += nb;
entries_nn++;
}
}
if (entry.store != null) {
ResourceStoreImpl st = (ResourceStoreImpl) entry.store;
if (st.resources != null) {
nb = st.resources.size();
if (nb > 0) {
int ssize = st.resources.size();
nb_entries_store += ssize;
stores_nn++;
if ((storeSizeLimit > 0) && (ssize > storeSizeLimit)) {
oversized++;
}
}
}
}
}
StringBuffer sb = new StringBuffer();
sb.append("<table border=\"1\" class=\"store\">\n"
+ "<caption>Resource Store usage</caption>\n"
+ "<tr><th>Loaded Stores</th>"
+ "<th>Loaded Entries</th></tr>\n");
sb.append("<tr><td>");
sb.append(loadedStore);
sb.append(" (");
sb.append(entries.size());
sb.append(") / ");
sb.append(maxLoadedStore);
if (storeSizeLimit >= 0) {
sb.append(" [");
sb.append(oversized);
sb.append(" > ");
sb.append(storeSizeLimit);
sb.append(" ]");
}
sb.append("</td><td>");
sb.append(nb_entries);
sb.append('(');
sb.append(entries_nn);
sb.append(')');
sb.append(" / ");
sb.append(nb_entries_store);
sb.append('(');
sb.append(stores_nn);
sb.append(")</td>\n</table>");
return sb.toString();
}
/**
* Enumerate the name (ie identifiers) of the space entry children.
* @param sentry The space entry.
* @param all Should all resources be listed.
* @return An enumeration, providing one element per child, which is
* the name of the child, as a String.
*/
public Enumeration enumerateResourceIdentifiers(SpaceEntry sentry) {
NewStoreEntry entry =
(NewStoreEntry) entries.get(sentry.getEntryKey());
if (entry == null)
throw new RuntimeException(
"Unable to list children, no StoreEntry"+
" for its space entry");
return entry.enumerateResourceIdentifiers();
}
/**
* Create a new resource store manager for given store directory.
* The resource store manager will manage the collection of stores
* contained in the directory, and keep track of the stores state.
* @param storedir The store directory to manage.
*/
public ResourceStoreManager (String server_name,
File storedir,
String default_root_class,
String default_root_name,
String serializer_class,
int max_loaded_store,
Hashtable defs)
{
this(server_name, storedir, default_root_class, default_root_name,
serializer_class, max_loaded_store, -1, defs);
}
/**
* Create a new resource store manager for given store directory.
* The resource store manager will manage the collection of stores
* contained in the directory, and keep track of the stores state.
* @param storedir The store directory to manage.
*/
public ResourceStoreManager (String server_name,
File storedir,
String default_root_class,
String default_root_name,
String serializer_class,
int max_loaded_store,
int store_size_limit,
Hashtable defs)
{
// Initialize the instance variables:
this.server_name = server_name;
this.storedir = storedir;
this.entries = new Hashtable() ;
this.sweeper = new StoreManagerSweeper(this) ;
this.lru = new AsyncLRUList();
this.maxLoadedStore = (max_loaded_store < 10) ? 10 : max_loaded_store;
this.storeSizeLimit = (store_size_limit < 100) ? -1 : store_size_limit;
this.loadedStore = 0;
try {
Class ser_class = Class.forName(serializer_class);
this.serializer = (Serializer) ser_class.newInstance();
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException("Invalid serializer class : "+
serializer_class);
}
loadNewEntriesIndex();
if (! checkSubDirs())
throw new RuntimeException("Unable to create store "+
"subdirectories!");
// If not already available, create the root resource, and its
// repository.
getRootRepository();
if (! root_repository.exists()) {
try {
Class root_class = Class.forName(default_root_class);
Resource root = (Resource) root_class.newInstance();
if (defs == null)
defs = new Hashtable(4);
defs.put("identifier".intern(), default_root_name);
defs.put("key".intern(), getRootKey());
root.initialize(defs);
NewStoreEntry entry = new NewStoreEntry(this,
ROOT_REP,
getRootKey());
ResourceReference rr = entry.addResource(root, defs);
ResourceContext context = (ResourceContext)
defs.get("context");
context.setResourceReference(rr);
entry.saveResource(root);
entries.put(getRootKey(), entry);
saveNewEntriesIndex();
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (ClassNotFoundException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
}
// If not already available, create the resource store state object:
File rsmstate = new File(storedir, STATE_F);
// Restore it:
Reader reader = null;
try {
reader = new BufferedReader( new FileReader(rsmstate));
AttributeHolder states[] = serializer.readAttributeHolders(reader);
this.state = (ResourceStoreState) states[0];
} catch (Exception ex) {
// Let's try to fix this:
this.state = salvageState();
}
if ( reader != null ) {
try { reader.close(); } catch (IOException ex) {}
}
// salvage();
// displayIndex();
}
}