📄 resourcestoreimpl.java
字号:
// ResourceStoreImpl.java// $Id: ResourceStoreImpl.java,v 1.15 2004/01/21 14:01:19 ylafon Exp $// (c) COPYRIGHT MIT and INRIA, 1996.// Please first read the full copyright statement in file COPYRIGHT.htmlpackage 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.serialization.Serializer;import org.w3c.tools.resources.serialization.SerializationException;import org.w3c.util.EmptyEnumeration;import java.util.Enumeration;import java.util.Hashtable;import java.util.Vector;import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import java.io.FileReader;import java.io.FileOutputStream;import java.io.OutputStreamWriter;import java.io.IOException;import java.io.PrintStream;import java.io.Reader;import java.io.Writer;/** * A generic resource store that keeps resource in a file using * the Serializer interface. */public class ResourceStoreImpl implements ResourceStore { static final int writerSize = 65536; class ResourceIndex { boolean modified = false; String identifier = null; Resource resource = null; boolean initialized = false; synchronized void markModified() { modified = true; } synchronized boolean isModified() { return modified; } synchronized Resource loadResource(Hashtable defs) { if (initialized) { return resource; } else { resource.initialize(defs); initialized = true; return resource; } } synchronized Resource getResource() { return resource; } synchronized void unloadResource() { // notify the resource of unload: resource.notifyUnload() ; resource = null; } synchronized String getIdentifier() { return identifier; } synchronized void setIdentifier(String identifier) { this.identifier = identifier; } ResourceIndex(Resource resource, boolean initialized) { this.resource = resource; this.identifier = resource.unsafeGetIdentifier(); this.modified = false; this.initialized = initialized; } } /** * The store format version number. */ private static final int VERSION = 2; /** * Our Resource Serializer. */ protected Serializer serializer = null; /** * Our underlying associated file. */ File repository = null ; /** * Our resource store manager. */ protected ResourceStoreManager manager = null ; /** * Our token within the resource store manager. */ Object token = null; /** * The resources we know about: maps identifier to resource objects. */ Hashtable resources = null ; /** * Has this repository been modified. */ boolean modified = false ; /** * Mark the store as having been used recently. */ protected final void markUsed() { if ( manager != null ) manager.markUsed(token) ; } // be smart here // FIXME removed the synchronized to avoid a deadlock protected void markModified() { if (!modified) { synchronized (this) { modified = true; } } } /** * Get the version of that resource store. * Version numbers are used to distinguish between pickling format. * A resource store implementator has the duty of bumping the returned * number whenever it's archiving format changes. * Resource stores that relies on some external archiving mechanisms * (such as a database), may return a constant. * @return An integer version number. */ public int getVersion() { return VERSION; } /** * Get the identifier for that store. * @return A uniq store identifier, as a String. */ public String getIdentifier() { return repository.getAbsolutePath(); } /** * 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("[" + getClass().getName()+ "@" + repository+ "]: " + msg) ; } /** * Restore the resource whose name is given. * This method doesn't assume that the resource will actually be restored, * it can be kept in a cache by the ResourceStore object, and the cached * version of the resource may be returned. * @param identifier The identifier of the resource to restore. * @param defs Default attribute values. If the resource needs to be * restored from its pickled version, this Hashtable provides * a set of default values for some of the attributes. * @return A Resource instance, or <strong>null</strong>. * @exception InvalidResourceException If the resource could not * be restored from the store. */ public Resource loadResource(String identifier, Hashtable defs) throws InvalidResourceException { loadResources(); markUsed(); ResourceIndex index = (ResourceIndex) resources.get(identifier) ; if ( index == null ) return null; if ( defs == null ) defs = new Hashtable(3); defs.put("store-entry", index); return index.loadResource(defs); } /** * Get this resource, but only if already loaded. * The resource store may (recommended) maintain a cache of the resource * it loads from its store. If the resource having this identifier * has already been loaded, return it, otherwise, return * <strong>null</strong>. * @param identifier The resource identifier. * @return A Resource instance, or <strong>null</strong>. */ public Resource lookupResource(String identifier) { loadResources(); markUsed(); ResourceIndex index = (ResourceIndex) resources.get(identifier); return (((index == null) || (! index.initialized)) ? null : index.getResource() ); } /** * Stabilize the given resource. * @param resource The resource to save. */ public void saveResource(Resource resource) { loadResources(); ResourceIndex index = (ResourceIndex) resource.getStoreEntry(); if ( index == null ) throw new UnknownResourceException(resource); if (index.isModified()) save() ; markUsed() ; } /** * Add this resource to this resource store. * @param resource The resource to be added. */ public synchronized void addResource(Resource resource) { loadResources(); ResourceIndex index = new ResourceIndex(resource, true); index.markModified(); resource.setValue("store-entry", index); resources.put(index.getIdentifier(), index); markModified(); markUsed(); } /** * Remove this resource from the repository. * @param identifier The identifier of the resource to be removed. */ public synchronized void removeResource(String identifier) { ResourceIndex index = (ResourceIndex) resources.get(identifier); if ( index != null ) { index.unloadResource(); resources.remove(identifier); markModified(); markUsed(); } } /** * Rename a given resource. * @param oldid The olde resource identifier. * @param newid The new resource identifier. */ public synchronized void renameResource(String oldid, String newid) { ResourceIndex index = (ResourceIndex) resources.get(oldid); if (index != null) { resources.remove(oldid); index.setIdentifier(newid); resources.put(newid, index); index.markModified(); markModified(); } } /** * Mark this resource as modified. * @param resource The resource that has changed (and will have to be * pickled some time latter). */ public void markModified(Resource resource) { ResourceIndex index = (ResourceIndex) resource.getStoreEntry(); if ( index != null ) { index.markModified(); markModified() ; markUsed(); } } /** * Can this resource store be unloaded now ? * This method gets called by the ResourceStoreManager before calling * the <code>shutdown</code> method, when possible. An implementation * of that method is responsible for checking the <code>acceptUnload * </code> method of all its loaded resource before returning * <strong>true</strong>, meaning that the resource store can be unloaded. * @return A boolean <strong>true</strong> if the resource store can be * unloaded. */ public synchronized boolean acceptUnload() { boolean accept = true; if ((manager != null) && (manager.getStoreSizeLimit() > 0) && resources.size() > manager.getStoreSizeLimit()) { accept = false; } else { Enumeration e = resources.elements(); while ( e.hasMoreElements() ) { ResourceIndex entry = (ResourceIndex) e.nextElement(); Resource resource = entry.getResource(); synchronized (entry) { if (! resource.acceptUnload() ) accept = false; } } } if ( ! accept ) { try { if ( modified ) internalSave(false); } catch (IOException ex) { ex.printStackTrace(); warning("internalSave failed at acceptUnload"); } } return accept; } /** * Internal save: save the repository back to disk. * @param unload Should we unload any existing resources ? */ protected synchronized void internalSave(boolean unload) throws IOException { //nothing to save if (resources == null) return; //1st, build the resource array Enumeration enum = resources.elements(); Vector vres = new Vector(11); while (enum.hasMoreElements()) { Resource res = ((ResourceIndex)enum.nextElement()).getResource(); vres.addElement(res); } Resource resourcearray[] = new Resource[vres.size()]; vres.copyInto(resourcearray); //try to save in a temporary file File tmp = new File(repository.getParent(), repository.getName()+".tmp") ; FileOutputStream fos = new FileOutputStream(tmp); OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8"); Writer writer = new BufferedWriter( osw, writerSize); serializer.writeResources(resourcearray, writer); //success!! String name = repository.getName() ; String dir = repository.getParent() ; File bak = new File(dir, name+".bak"); File tild = new File(dir, name+".bak~"); // 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 repository to bak if ( repository.exists() ) { if ( ! repository.renameTo(bak) ) { warning("unable to rename "+repository+" to "+bak) ; tild.renameTo(bak) ; } repository.delete(); } // 4th move: rename the tmp file to the repository if ( ! tmp.renameTo(repository) ) { bak.renameTo(repository) ; tild.renameTo(bak); warning("unable to rename "+tmp+" to "+repository) ; } // cleanup (erase the ~ file) tild.delete() ; modified = false; //unload if needed if (unload) { for (int i = 0 ; i < resourcearray.length ; i++) resourcearray[i].notifyUnload(); resources = null; } } /** * Shutdown this store. */ public void shutdown() { if (modified) { try { internalSave(true) ; } catch (IOException ex) { ex.printStackTrace(); warning("internalSave failed at shutdown.") ; } } else { // Just notify the unload of loaded resources: Enumeration entries = resources.elements() ; while ( entries.hasMoreElements() ) { ResourceIndex index = (ResourceIndex) entries.nextElement() ; index.unloadResource() ; } } // Clean-up all references we have to external objects: resources = null ; manager = null ; } /** * Save this store. */ public void save() { if ( modified ) { try { internalSave(false) ; } catch (IOException ex) { warning("Save failed ["+ex.getMessage()+"]"); } } } /** * Enumerate all the resources saved in this store. * @return An enumeration of Strings, giving the identifier for all * the resources that this store knows about. */ public Enumeration enumerateResourceIdentifiers() { markUsed(); loadResources(); return resources.keys(); } /** * Check for the existence of a resource in this store. * @param identifier The identifier of the resource to check. * @return A boolean <strong>true</strong> if the resource exists * in this store, <strong>false</strong> otherwise. */ public boolean hasResource(String identifier) { markUsed(); loadResources(); return resources.get(identifier) != null ; } protected void loadResources() { if (resources == null) { synchronized (this) { int i = 0; if (resources == null) { try { resources = new Hashtable(11); if (repository.exists()) { Reader reader = new BufferedReader(new FileReader(repository)); Resource resourceArray[] = serializer.readResources(reader); for (i = 0 ; i < resourceArray.length ; i++) { ResourceIndex entry = new ResourceIndex(resourceArray[i], false); if (entry != null && entry.getIdentifier() != null) { resources.put(entry.getIdentifier(), entry); } } } } catch (IOException ioex) { ioex.printStackTrace(); warning("Unable to load resources"); } catch (SerializationException sex) { warning(sex.getMessage()); sex.printStackTrace(); } catch (Exception ex) { ex.printStackTrace(); String err = "Error in " + repository.getName() + " in dir " + repository.getParent() + ": [" + i + "] " + ex.getMessage(); warning(err); } } } } } /** * This resource store is being built, initialize it with the given arg. * @param manager The ResourceStoreManager instance that asks yourself * to initialize. * @param token The resource store manager key to that resource store, * this token should be used when calling methods from the manager that * are to act on yourself. * @param repository A file, giving the location of the associated * repository. */ public void initialize(ResourceStoreManager manager, Object token, File repository, Serializer serializer) { this.manager = manager; this.token = token; this.repository = repository; this.serializer = serializer; this.resources = null; markUsed(); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -