Package org.w3c.tools.resources.store

Source Code of org.w3c.tools.resources.store.ResourceStoreManager

// 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();
    }

}

TOP

Related Classes of org.w3c.tools.resources.store.ResourceStoreManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.