0001: // Resourcestoremanager.java
0002: // $Id: ResourceStoreManager.java,v 1.33 2007/02/09 22:27:25 ylafon Exp $
0003: // (c) COPYRIGHT MIT and INRIA, 1996.
0004: // Please first read the full copyright statement in file COPYRIGHT.html
0005:
0006: package org.w3c.tools.resources.store;
0007:
0008: import org.w3c.tools.resources.AttributeHolder;
0009: import org.w3c.tools.resources.InvalidResourceException;
0010: import org.w3c.tools.resources.Resource;
0011: import org.w3c.tools.resources.ResourceContext;
0012: import org.w3c.tools.resources.ResourceReference;
0013: import org.w3c.tools.resources.ResourceSpace;
0014: import org.w3c.tools.resources.ServerInterface;
0015: import org.w3c.tools.resources.SpaceEntry;
0016:
0017: import org.w3c.tools.resources.serialization.Serializer;
0018:
0019: import org.w3c.tools.resources.event.ResourceEventQueue;
0020:
0021: import java.io.BufferedInputStream;
0022: import java.io.BufferedReader;
0023: import java.io.BufferedWriter;
0024: import java.io.File;
0025: import java.io.FileInputStream;
0026: import java.io.FileReader;
0027: import java.io.FilenameFilter;
0028: import java.io.FileOutputStream;
0029: import java.io.OutputStreamWriter;
0030: import java.io.IOException;
0031: import java.io.InputStream;
0032: import java.io.ObjectInputStream;
0033: import java.io.PrintStream;
0034: import java.io.Reader;
0035: import java.io.Serializable;
0036: import java.io.Writer;
0037:
0038: import java.util.Enumeration;
0039: import java.util.Hashtable;
0040: import java.util.Vector;
0041:
0042: import org.w3c.util.AsyncLRUList;
0043: import org.w3c.util.LRUAble;
0044: import org.w3c.util.LRUList;
0045: import org.w3c.util.Status;
0046:
0047: class Reference implements ResourceReference {
0048:
0049: public static boolean debug = false;
0050:
0051: // Our entry
0052: StoreEntry entry = null;
0053:
0054: // The resource identifier
0055: String identifier = null;
0056:
0057: // The default attributs
0058: Hashtable defs = null;
0059:
0060: public void updateContext(ResourceContext ctxt) {
0061: if (defs != null)
0062: defs.put("context", ctxt);
0063: }
0064:
0065: /**
0066: * The lock count associated to the reference.
0067: */
0068: protected int lockCount = 0;
0069:
0070: public int nbLock() {
0071: return lockCount;
0072: }
0073:
0074: protected void invalidate() {
0075: entry = null;
0076: }
0077:
0078: protected boolean isValid() {
0079: return (entry != null);
0080: }
0081:
0082: public Resource unsafeLock() throws InvalidResourceException {
0083: return lock();
0084: }
0085:
0086: /**
0087: * Lock that reference in memory.
0088: * @return A pointer to the underlying resource, after lock succeed.
0089: */
0090: public synchronized Resource lock() throws InvalidResourceException {
0091: lockCount++;
0092: if (entry == null)
0093: throw new InvalidResourceException(identifier,
0094: "This reference was invalidate");
0095: ResourceStore store = entry.getStore();
0096: Resource resource = store.lookupResource(identifier);
0097: if (debug) {
0098: if (defs.get("context") == null)
0099: System.out.println("**** Context null for : "
0100: + identifier);
0101: else if (((ResourceContext) (defs.get("context")))
0102: .getServer() == null)
0103: System.out.println("**** Server null for " + identifier
0104: + "'s context");
0105: }
0106: if (resource == null) {
0107: resource = store.loadResource(identifier, defs);
0108: }
0109: if (debug)
0110: System.out.println("[LOCK] locking [" + lockCount + "]: "
0111: + identifier);
0112: return resource;
0113: }
0114:
0115: /**
0116: * Unlock that resource reference.
0117: */
0118: public void unlock() {
0119: lockCount--;
0120: if (debug)
0121: System.out.println("[LOCK] unlocking [" + lockCount + "]: "
0122: + identifier);
0123: }
0124:
0125: /**
0126: * Is that resource locked ?
0127: * @return A boolean, <strong>true</strong> if the resource is locked.
0128: */
0129: public boolean isLocked() {
0130: return lockCount != 0;
0131: }
0132:
0133: Reference(StoreEntry entry, String identifier, Hashtable defs) {
0134: this .entry = entry;
0135: this .identifier = identifier;
0136: this .defs = defs;
0137: }
0138:
0139: }
0140:
0141: class StoreEntry implements LRUAble, Serializable {
0142:
0143: boolean istransient = false;
0144:
0145: // our key in the cache.
0146: Integer key = null;
0147:
0148: // LRU management infos:
0149: transient LRUAble next = null;
0150: transient LRUAble prev = null;
0151:
0152: // ResourceStore infos:
0153: transient ResourceStore store = null;
0154:
0155: // References
0156: transient Hashtable references = null;
0157:
0158: //The manager
0159: transient ResourceStoreManager manager = null;
0160:
0161: String repository = null;
0162:
0163: transient File rep = null;
0164:
0165: public File getRepository() {
0166: if (rep == null)
0167: rep = new File(manager.storedir, repository);
0168: return rep;
0169: }
0170:
0171: public LRUAble getNext() {
0172: return next;
0173: }
0174:
0175: public LRUAble getPrev() {
0176: return prev;
0177: }
0178:
0179: public void setNext(LRUAble next) {
0180: this .next = next;
0181: }
0182:
0183: public void setPrev(LRUAble prev) {
0184: this .prev = prev;
0185: }
0186:
0187: public void setTransient(boolean onoff) {
0188: istransient = onoff;
0189: }
0190:
0191: public boolean isTransient() {
0192: return istransient;
0193: }
0194:
0195: /**
0196: * Load the store of this entry.
0197: */
0198: synchronized ResourceStore getStore() {
0199: if (store == null) {
0200: store = new ResourceStoreImpl();
0201: store.initialize(manager, this , getRepository(),
0202: manager.serializer);
0203: manager.incrLoadedStore();
0204: }
0205: return store;
0206: }
0207:
0208: /**
0209: * Delete the store of this entry
0210: */
0211: synchronized void deleteStore() {
0212: Enumeration e = references.elements();
0213: Reference rr = null;
0214: while (e.hasMoreElements()) {
0215: rr = (Reference) e.nextElement();
0216: rr.invalidate();
0217: }
0218: getRepository().delete();
0219: if (store != null) {
0220: store = null;
0221: manager.decrLoadedStore();
0222: }
0223: references = null;
0224: }
0225:
0226: /**
0227: * Lookup this resource.
0228: * @param identifier The resource identifier.
0229: * @return A Resource instance, or <strong>null</strong> if either the
0230: * resource doesn't exist, or it isn't loaded yet.
0231: */
0232: public ResourceReference lookupResource(String identifier) {
0233: return (ResourceReference) references.get(identifier);
0234: }
0235:
0236: /**
0237: * Load a resource, or get one from the cache.
0238: * @param identifier The resource identifier.
0239: * @return A Resource instance, or <strong>null</strong> if the resource
0240: * doesn't exist in that storeEntry.
0241: * @exception InvalidResourceException If the resource couldn't be
0242: * restored from its pickled format.
0243: */
0244: synchronized ResourceReference loadResource(String name,
0245: Hashtable defs) {
0246: ResourceReference rr = lookupResource(name);
0247: if (rr != null)
0248: return rr;
0249: rr = new Reference(this , name, defs);
0250: try {
0251: Resource res = rr.lock();
0252: if (res == null)
0253: return null;
0254: } catch (InvalidResourceException ex) {
0255: return null;
0256: } finally {
0257: rr.unlock();
0258: }
0259: references.put(name, rr);
0260: return rr;
0261: }
0262:
0263: /**
0264: * Save a given resource.
0265: * @param resource The resource to be save right now.
0266: */
0267: synchronized void saveResource(Resource resource) {
0268: getStore();
0269: store.saveResource(resource);
0270: }
0271:
0272: /**
0273: * Add a new resource to the resource store.
0274: * @param resource The resource to add.
0275: */
0276: synchronized ResourceReference addResource(Resource resource,
0277: Hashtable defs) {
0278: getStore();
0279: store.addResource(resource);
0280: String name = resource.getIdentifier();
0281: ResourceReference rr = new Reference(this , name, defs);
0282: references.put(name, rr);
0283: return rr;
0284: }
0285:
0286: /**
0287: * FIXME doc
0288: */
0289: public synchronized void markModified(Resource resource) {
0290: getStore();
0291: store.markModified(resource);
0292: }
0293:
0294: /**
0295: * Rename a resource in this resource store.
0296: * @param identifier The identifier of the resource to be renamed.
0297: */
0298: public synchronized void renameResource(String oldid, String newid) {
0299: getStore();
0300: store.renameResource(oldid, newid);
0301: Reference rr = (Reference) lookupResource(oldid);
0302: if (rr != null) {
0303: rr.identifier = newid;
0304: references.remove(oldid);
0305: references.put(newid, rr);
0306: }
0307: }
0308:
0309: public synchronized Enumeration enumerateResourceIdentifiers() {
0310: getStore();
0311: return store.enumerateResourceIdentifiers();
0312: }
0313:
0314: /**
0315: * Remove a resource from this resource store.
0316: * @param identifier The identifier of the resource to be removed.
0317: */
0318: public synchronized void removeResource(String identifier) {
0319: getStore();
0320: Reference rr = (Reference) references.get(identifier);
0321: if (rr != null) {
0322: references.remove(identifier);
0323: manager.getEventQueue().removeSourceEvents(rr);
0324: rr.invalidate();
0325: }
0326: store.removeResource(identifier);
0327: }
0328:
0329: /**
0330: * Try unloading the space for this entry.
0331: */
0332: synchronized boolean unloadStore() {
0333: if (store != null) {
0334: Enumeration e = references.elements();
0335: ResourceReference rr = null;
0336: while (e.hasMoreElements()) {
0337: rr = (ResourceReference) e.nextElement();
0338: if (rr.isLocked())
0339: return false;
0340: }
0341: // Will the store unload itself ?
0342: if (!store.acceptUnload())
0343: return false;
0344: // Great, proceed:
0345: shutdownStore();
0346: }
0347: return true;
0348: }
0349:
0350: /**
0351: * Shutdown the store.
0352: */
0353: synchronized void shutdownStore() {
0354: if (store != null) {
0355: store.shutdown();
0356: store = null;
0357: references = new Hashtable(); // clear also the references
0358: manager.decrLoadedStore();
0359: }
0360: }
0361:
0362: /**
0363: * Try stabilizing the store.
0364: */
0365: synchronized void saveStore() {
0366: if (store != null) {
0367: // Save the resource store:
0368: store.save();
0369: }
0370: }
0371:
0372: void initialize(ResourceStoreManager manager) {
0373: this .manager = manager;
0374: this .references = new Hashtable();
0375: }
0376:
0377: StoreEntry(ResourceStoreManager manager, String repository,
0378: Integer key) {
0379: this .manager = manager;
0380: this .store = null;
0381: this .repository = repository;
0382: this .key = key;
0383: this .references = new Hashtable();
0384: }
0385:
0386: StoreEntry(ResourceStoreManager manager, File repository,
0387: Integer key) {
0388: this .manager = manager;
0389: this .store = null;
0390: this .rep = repository;
0391: this .key = key;
0392: this .references = new Hashtable();
0393: }
0394:
0395: }
0396:
0397: class StoreManagerSweeper extends Thread {
0398: ResourceStoreManager manager = null;
0399: boolean killed = false;
0400:
0401: protected synchronized void waitEvent() {
0402: boolean done = false;
0403:
0404: // Wait for an event to come by:
0405: while (!done) {
0406: try {
0407: wait();
0408: done = true;
0409: } catch (InterruptedException ex) {
0410: }
0411: }
0412: }
0413:
0414: protected synchronized void sweep() {
0415: notifyAll();
0416: }
0417:
0418: protected synchronized void shutdown() {
0419: killed = true;
0420: notifyAll();
0421: }
0422:
0423: public void run() {
0424: while (true) {
0425: waitEvent();
0426: // The whole trick is to run the collect method of the store
0427: // manager, without having the lock on the sweeper itself, so
0428: // that clients can still trigger it later
0429: if (killed) {
0430: break;
0431: } else {
0432: try {
0433: manager.collect();
0434: } catch (Exception ex) {
0435: // We really don't want this thread to die
0436: ex.printStackTrace();
0437: }
0438: }
0439: }
0440: }
0441:
0442: StoreManagerSweeper(ResourceStoreManager manager) {
0443: this .manager = manager;
0444: this .setName("StoreSweeper");
0445: this .start();
0446: }
0447:
0448: }
0449:
0450: public class ResourceStoreManager implements ResourceSpace {
0451:
0452: public static boolean debug = false;
0453:
0454: /**
0455: * Number of sub-levels file system directories in the store directory.
0456: */
0457: public final static int SUBDIRS = 128;
0458:
0459: public static final String ROOT_REP = "root.xml";
0460:
0461: public static final String STATE_F = "state.xml";
0462:
0463: public boolean debugMemory = false;
0464:
0465: protected Serializer serializer = null;
0466:
0467: /**
0468: * The loaded resource stores entries.
0469: */
0470: Hashtable entries = new Hashtable(256); // <sentryKey - StoreEntry>
0471: /**
0472: * The limit of "BIG" stores, over this limits stores will be kept
0473: * in memory for performance purposes
0474: * If set to -1, this optimization won't happen
0475: */
0476: private int storeSizeLimit = -1;
0477: /**
0478: * The max number of loaded stores
0479: */
0480: private int maxLoadedStore = -1;
0481: /**
0482: * The number of loaded stores
0483: */
0484: private int loadedStore = 0;
0485: /**
0486: * Is this store shutdown ?
0487: */
0488: protected boolean closed = false;
0489: /**
0490: * Our store directory.
0491: */
0492: protected File storedir = null;
0493: /**
0494: * Our index file.
0495: */
0496: protected File index = null;
0497: /**
0498: * server name;
0499: */
0500: protected String server_name = null;
0501:
0502: /**
0503: * Our root repository.
0504: */
0505: protected File root_repository = null;
0506: /**
0507: * The store entries least recetenly used list.
0508: */
0509: protected LRUList lru = null;
0510: // FIXME doc
0511: protected ResourceStoreState state = null;
0512:
0513: /**
0514: * Our sweeper thread:
0515: */
0516: protected StoreManagerSweeper sweeper = null;
0517:
0518: protected ResourceEventQueue eventQueue = null;
0519:
0520: public ResourceEventQueue getEventQueue() {
0521: if (eventQueue == null)
0522: eventQueue = new ResourceEventQueue();
0523: return eventQueue;
0524: }
0525:
0526: protected final int getMaxLoadedStore() {
0527: return maxLoadedStore;
0528: }
0529:
0530: protected final int getStoreSizeLimit() {
0531: return storeSizeLimit;
0532: }
0533:
0534: protected synchronized void incrLoadedStore() {
0535: loadedStore++;
0536: checkMaxLoadedStore();
0537: }
0538:
0539: protected synchronized void decrLoadedStore() {
0540: loadedStore--;
0541: }
0542:
0543: /**
0544: * Check that this resource store manager isn't closed.
0545: * @exception RuntimeException If the store manager was closed.
0546: */
0547: protected final synchronized void checkClosed() {
0548: if (closed)
0549: throw new RuntimeException("Invalid store manager access.");
0550: }
0551:
0552: /**
0553: * Pick the least recently used entry, and remove all links to it.
0554: * After this method as run, the least recently used entry for some store
0555: * will be returned. The store manager will have discarded all its link to
0556: * it, and the entry shutdown will have to be performed by the caller.
0557: * @return An StoreEntry instance, to be cleaned up.
0558: */
0559: protected NewStoreEntry pickLRUEntry() {
0560: NewStoreEntry entry;
0561: synchronized (lru) {
0562: entry = (NewStoreEntry) lru.removeTail();
0563: }
0564: return entry;
0565: }
0566:
0567: /**
0568: * Collect enough entries to go back into fixed limits.
0569: */
0570: public void collect() {
0571: if (debugMemory)
0572: System.out.println("[MEMORY] start sweeper <- "
0573: + loadedStore + " stores loaded.");
0574: NewStoreEntry watchdog = null;
0575: NewStoreEntry entry = null;
0576: int maxload = getMaxLoadedStore();
0577: while (loadedStore > maxload) {
0578: entry = pickLRUEntry();
0579: if ((entry != null) && (entry != watchdog)) {
0580: synchronized (entry) {
0581: if (!entry.unloadStore()) {
0582: synchronized (lru) {
0583: lru.toHead(entry);
0584: if (watchdog == null)
0585: watchdog = entry;
0586: }
0587: }
0588: }
0589: } else {
0590: break;
0591: }
0592: }
0593: if (debugMemory)
0594: System.out.println("[MEMORY] sweeper done -> "
0595: + loadedStore + " stores still loaded.");
0596: }
0597:
0598: /**
0599: * Create a resource store repository name.
0600: * This method will return a new resource store repository key. When
0601: * used in conjunction with the <code>loadResourceStore</code> method
0602: * that takes a key as a parameter, this allows to caller to abstract
0603: * itself from the physical location of the repository.
0604: * @return A fresh resource store key, guaranteed to be uniq.
0605: */
0606: public synchronized String createResourceStoreRepository() {
0607: int key = state.getNextKey();
0608: return (key % SUBDIRS) + "/st-" + key;
0609: }
0610:
0611: public int getCurrentStoreIdentifier() {
0612: return state.getCurrentKey();
0613: }
0614:
0615: protected boolean checkSubDirs() {
0616: for (int i = 0; i < SUBDIRS; i++) {
0617: File subd = new File(storedir, Integer.toString(i));
0618: if ((!subd.exists()) && !subd.mkdirs())
0619: return false;
0620: }
0621: return true;
0622: }
0623:
0624: /**
0625: * Get the index file.
0626: * @return A File instance.
0627: */
0628: protected File getIndexFile() {
0629: if (index == null)
0630: index = new File(storedir, server_name + "-index.xml");
0631: return index;
0632: }
0633:
0634: /**
0635: * Get the index file.
0636: * @return A File instance.
0637: */
0638: protected File getOldIndexFile() {
0639: if (index == null)
0640: index = new File(storedir, server_name + "-index");
0641: return index;
0642: }
0643:
0644: /**
0645: * Get the root repository.
0646: * @return A File instance.
0647: */
0648: protected File getRootRepository() {
0649: if (root_repository == null)
0650: root_repository = new File(storedir, ROOT_REP);
0651: return root_repository;
0652: }
0653:
0654: /**
0655: * Get The root key.
0656: * @return A String instance
0657: */
0658: protected Integer getRootKey() {
0659: return new Integer((new String("root")).hashCode());
0660: }
0661:
0662: /**
0663: * Emit the given string as a warning, to whoever it is appropriate.
0664: * @param msg The warning message.
0665: */
0666: protected void warning(String msg) {
0667: System.out.println("********* WARNING *********");
0668: System.out.println("[" + getClass().getName() + "]:\n" + msg);
0669: System.out.println("***************************");
0670: }
0671:
0672: protected synchronized void saveNewEntriesIndex() {
0673: File indexFile = getIndexFile();
0674: String dir = indexFile.getParent();
0675: String name = indexFile.getName();
0676: File tmp = new File(dir, name + ".tmp");
0677: File bak = new File(dir, name + ".bak");
0678: File tild = new File(dir, name + ".bak~");
0679:
0680: Enumeration elements = entries.elements();
0681: Vector v = new Vector(10);
0682: while (elements.hasMoreElements()) {
0683: NewStoreEntry entry = (NewStoreEntry) elements
0684: .nextElement();
0685: if (!entry.isTransient())
0686: v.addElement(entry);
0687: }
0688: NewStoreEntry se[] = new NewStoreEntry[v.size()];
0689: v.copyInto(se);
0690:
0691: try {
0692: FileOutputStream fos = new FileOutputStream(tmp);
0693:
0694: Writer writer = new BufferedWriter(new OutputStreamWriter(
0695: fos, "UTF-8"));
0696: serializer.writeResources(se, writer);
0697: } catch (Exception ex) {
0698: ex.printStackTrace();
0699: throw new RuntimeException("Unable to save entries index");
0700: }
0701: // 1st move: delete the ~ file if any:
0702: if (tild.exists())
0703: tild.delete();
0704: // 2nd move rename bak to ~ (bak no longer exists)
0705: if (bak.exists()) {
0706: bak.renameTo(tild);
0707: bak.delete();
0708: }
0709: // 3nd move: rename the current index to bak
0710: if (indexFile.exists()) {
0711: if (!indexFile.renameTo(bak)) {
0712: warning("unable to rename " + indexFile + " to " + bak);
0713: tild.renameTo(bak);
0714: }
0715: indexFile.delete();
0716: }
0717: // 4th move: rename the tmp file to index
0718: if (!tmp.renameTo(indexFile)) {
0719: bak.renameTo(indexFile);
0720: tild.renameTo(bak);
0721: warning("unable to rename " + tmp + " to " + indexFile);
0722: }
0723: // cleanup (erase the ~ file)
0724: tild.delete();
0725: }
0726:
0727: protected void loadNewEntriesIndex() {
0728: File indexFile = getIndexFile();
0729: if (!indexFile.exists()) {
0730: File bak = new File(indexFile.getParent(), indexFile
0731: .getName()
0732: + ".bak");
0733: if (bak.exists()) {
0734: warning(indexFile + " not found! using bak file :"
0735: + bak);
0736: if (!bak.renameTo(indexFile)) {
0737: warning("unable to rename "
0738: + bak
0739: + " to "
0740: + indexFile
0741: + "\n Try by yourself or all your resources will be lost!");
0742: System.exit(-1);
0743: }
0744: } else {
0745: entries = new Hashtable(256);
0746: return;
0747: }
0748: }
0749: entries = new Hashtable(256);
0750: try {
0751: Reader reader = new BufferedReader(
0752: new FileReader(indexFile));
0753: AttributeHolder holders[] = serializer
0754: .readAttributeHolders(reader);
0755: for (int i = 0; i < holders.length; i++) {
0756: NewStoreEntry entry = (NewStoreEntry) holders[i];
0757: entries.put(entry.getKey(), entry);
0758: synchronized (lru) {
0759: lru.toHead((LRUAble) entry);
0760: }
0761: entry.initialize(this );
0762: }
0763: } catch (Exception ex) {
0764: ex.printStackTrace();
0765: throw new RuntimeException("Unable to load entries index");
0766: }
0767: }
0768:
0769: /**
0770: * update the old index file. Load it and save it with the serializer.
0771: * @param the index file (serialized hashtable of StoreEntry)
0772: */
0773: public static synchronized void updateEntriesIndex(
0774: File oldIndexFile, File newIndexFile, Serializer serializer)
0775: throws IOException {
0776: Hashtable entries = new Hashtable();
0777: if (!oldIndexFile.exists()) {
0778: File bak = new File(oldIndexFile.getParent(), oldIndexFile
0779: .getName()
0780: + ".bak");
0781: if (bak.exists()) {
0782: System.out.println(oldIndexFile.getAbsolutePath()
0783: + " not found! using bak file :" + bak);
0784: if (!bak.renameTo(oldIndexFile)) {
0785: System.out
0786: .println("unable to rename "
0787: + bak
0788: + " to "
0789: + oldIndexFile
0790: + "\n Try by yourself or all your resources will be lost!");
0791: System.exit(-1);
0792: }
0793: } else {
0794: return;
0795: }
0796: }
0797:
0798: ObjectInputStream oi = null;
0799: try {
0800: oi = new ObjectInputStream(new BufferedInputStream(
0801: new FileInputStream(oldIndexFile)));
0802: entries = (Hashtable) oi.readObject();
0803: } catch (ClassNotFoundException ex) {
0804: ex.printStackTrace();
0805: throw new RuntimeException("Unable to load entries index");
0806: } catch (IOException ex) {
0807: ex.printStackTrace();
0808: throw new RuntimeException("Unable to load entries index");
0809: } finally {
0810: if (oi != null) {
0811: try {
0812: oi.close();
0813: } catch (Exception ex) {
0814: }
0815: }
0816: }
0817:
0818: Enumeration e = entries.elements();
0819: int len = 0;
0820: while (e.hasMoreElements()) {
0821: StoreEntry entry = (StoreEntry) e.nextElement();
0822: if (entry.isTransient())
0823: entries.remove(entry.key);
0824: else {
0825: len++;
0826: String rep = entry.repository;
0827: if (rep != null) {
0828: if (rep.equals("root.idx")) {
0829: entry.repository = "root.xml";
0830: } else {
0831: // st-xxx
0832: int number = Integer.parseInt(rep.substring(3));
0833: entry.repository = (number % SUBDIRS) + "/"
0834: + rep;
0835: }
0836: }
0837: }
0838: }
0839: //StoreEntry => NewStoreEntry
0840: e = entries.elements();
0841: NewStoreEntry nentries[] = new NewStoreEntry[len];
0842: int i = 0;
0843: while (e.hasMoreElements()) {
0844: StoreEntry entry = (StoreEntry) e.nextElement();
0845: NewStoreEntry nentry = new NewStoreEntry(null,
0846: entry.repository, entry.key);
0847: nentries[i++] = nentry;
0848: }
0849: FileOutputStream fos = new FileOutputStream(newIndexFile);
0850: OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");
0851: Writer writer = new BufferedWriter(osw);
0852: serializer.writeResources(nentries, writer);
0853: }
0854:
0855: /**
0856: * Shutdown this resource store manager.
0857: * Go through all entries, and shut them down.
0858: */
0859: public synchronized void shutdown() {
0860: saveNewEntriesIndex();
0861: // Kill the sweeper thread:
0862: sweeper.shutdown();
0863: // Clenup all pending resource stores:
0864: Enumeration e = entries.elements();
0865: while (e.hasMoreElements()) {
0866: NewStoreEntry entry = (NewStoreEntry) e.nextElement();
0867: entry.shutdownStore();
0868: entries.remove(entry.getKey());
0869: }
0870: closed = true;
0871: // Now save our state:
0872: File rmstate = new File(storedir, STATE_F);
0873: try {
0874: FileOutputStream fos = new FileOutputStream(rmstate);
0875: OutputStreamWriter osw = new OutputStreamWriter(fos,
0876: "UTF-8");
0877: Writer writer = new BufferedWriter(osw);
0878: AttributeHolder statearray[] = { state };
0879: serializer.writeResources(statearray, writer);
0880: } catch (Exception ex) {
0881: // FIXME !!
0882: System.out
0883: .println("ResourceStoreManager: unable to save state !");
0884: ex.printStackTrace();
0885: }
0886: }
0887:
0888: /**
0889: * Checkpoint all modified resource stores, by saving them to disk.
0890: */
0891: public void checkpoint() {
0892: try {
0893: saveNewEntriesIndex();
0894: } catch (Exception ex) {
0895: System.out.println("*** Error during checkpoint");
0896: }
0897: // Checkpoint all loaded resource stores:
0898: Enumeration e = entries.elements();
0899: while (e.hasMoreElements()) {
0900: NewStoreEntry entry = (NewStoreEntry) e.nextElement();
0901: try {
0902: entry.saveStore();
0903: } catch (Exception ex) {
0904: if (entry == null) {
0905: System.out.println("*** Error, saving null entry!");
0906: } else {
0907: System.out.println("*** Error while saving store "
0908: + entry.getKey());
0909: }
0910: }
0911: }
0912: // Then save our state:
0913: File rmstate = new File(storedir, STATE_F);
0914: try {
0915: FileOutputStream fos = new FileOutputStream(rmstate);
0916: OutputStreamWriter osw = new OutputStreamWriter(fos,
0917: "UTF-8");
0918: Writer writer = new BufferedWriter(osw);
0919: AttributeHolder statearray[] = { state };
0920: serializer.writeResources(statearray, writer);
0921: } catch (Exception ex) {
0922: // FIXME !!
0923: System.out
0924: .println("ResourceStoreManager: unable to save state !");
0925: ex.printStackTrace();
0926: }
0927: }
0928:
0929: /**
0930: * Mark the given store as having been used recently.
0931: * @param token The token the resource store manager provided you when
0932: * it initialized the store.
0933: */
0934: public void markUsed(Object token) {
0935: NewStoreEntry entry = (NewStoreEntry) token;
0936: if (entry != null) {
0937: synchronized (lru) {
0938: lru.toHead(entry);
0939: }
0940: }
0941: }
0942:
0943: protected boolean used(String rep) {
0944: Enumeration e = entries.elements();
0945: NewStoreEntry entry = null;
0946: while (e.hasMoreElements()) {
0947: entry = (NewStoreEntry) e.nextElement();
0948: System.out.println(storedir + ":" + entry.getRepository());
0949: try {
0950: if (entry.getRepository().getName().equals(rep))
0951: return true;
0952: } catch (Exception ex) {
0953: //continue
0954: }
0955: }
0956: return false;
0957: }
0958:
0959: public void salvage() {
0960: String stores[] = storedir.list();
0961: if (stores != null) {
0962: for (int i = 0; i < stores.length; i++) {
0963: if ((stores[i].length() <= 3)
0964: || !stores[i].startsWith("st-"))
0965: continue;
0966: if (stores[i].endsWith(".bak"))
0967: continue;
0968: //Is there a storeEntry using this repository?
0969: if (!used(stores[i])) {
0970: // For testing now
0971: System.err.println("*** " + stores[i]
0972: + " not used! deleted");
0973: File notUsed = new File(storedir, stores[i]);
0974: notUsed.delete();
0975: }
0976: }
0977: }
0978: }
0979:
0980: public void displayIndex() {
0981: Enumeration e = entries.elements();
0982: NewStoreEntry entry = null;
0983: System.out.println("Index : ");
0984: while (e.hasMoreElements()) {
0985: entry = (NewStoreEntry) e.nextElement();
0986: System.out.println(entry.getKey() + " : "
0987: + entry.getRepository());
0988: }
0989: }
0990:
0991: /**
0992: * Try to salvage the resource store manager state.
0993: * That's pretty easy and robust, it should really work well if no
0994: * one hacked the store directory.
0995: * @return A ResourceStoreState instance.
0996: */
0997: public ResourceStoreState salvageState() {
0998: System.err.println("*** salvaging resource manager state...");
0999: File state = new File(storedir, STATE_F);
1000: int maxid = -1;
1001: FilenameFilter filter = new FilenameFilter() {
1002: public boolean accept(File dir, String name) {
1003: return (name.startsWith("st-") && (!name
1004: .endsWith(".bak")));
1005: }
1006: };
1007: for (int i = 0; i < SUBDIRS; i++) {
1008: File subdir = new File(storedir, Integer.toString(i));
1009: if (subdir.exists()) {
1010: String stores[] = subdir.list(filter);
1011: for (int j = 0; j < stores.length; j++) {
1012: int id = Integer.parseInt(stores[j].substring(3));
1013: maxid = Math.max(maxid, id);
1014: }
1015: }
1016: }
1017: ++maxid;
1018: System.err.println("*** resource store state salvaged, using: "
1019: + maxid);
1020: return new ResourceStoreState(maxid);
1021: }
1022:
1023: /**
1024: * Restore the resource whose name is given from the root NewStoreEntry.
1025: * @param identifier The identifier of the resource to restore.
1026: * @param defs Default attribute values.
1027: */
1028: public ResourceReference loadRootResource(String identifier,
1029: Hashtable defs) {
1030: NewStoreEntry entry = (NewStoreEntry) entries.get(getRootKey());
1031: if (entry == null) {
1032: synchronized (this ) {
1033: entry = (NewStoreEntry) entries.get(getRootKey());
1034: if (entry == null) {
1035: entry = new NewStoreEntry(this , ROOT_REP,
1036: getRootKey());
1037: entries.put(getRootKey(), entry);
1038: }
1039: }
1040: }
1041: return entry.loadResource(identifier, defs);
1042: }
1043:
1044: /**
1045: * Check that the key is not already in the list of references
1046: * @return a boolean, true if the key is not there, false if it is
1047: */
1048: public boolean checkKey(Integer key) {
1049: if (entries != null) {
1050: return !entries.containsKey(key);
1051: }
1052: return true;
1053: }
1054:
1055: /**
1056: * Lookup this resource.
1057: * @param sentry The resource space entry.
1058: * @param identifier The resource identifier.
1059: * @return A Resource instance, or <strong>null</strong> if either the
1060: * resource doesn't exist, or it isn't loaded yet.
1061: */
1062: public ResourceReference lookupResource(SpaceEntry sentry,
1063: String identifier) {
1064: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1065: .getEntryKey());
1066: if (entry == null)
1067: throw new RuntimeException("Unable to lookup resource ("
1068: + identifier
1069: + "), no StoreEntry for its space entry");
1070: return entry.lookupResource(identifier);
1071: }
1072:
1073: /**
1074: * Restore the resource whose name is given.
1075: * @param sentry The resource space entry.
1076: * @param identifier The identifier of the resource to restore.
1077: * @param defs Default attribute values.
1078: */
1079: public ResourceReference loadResource(SpaceEntry sentry,
1080: String identifier, Hashtable defs) {
1081: if (debug) {
1082: System.out.println("[RSM] loading [" + identifier
1083: + "] from [" + sentry + ']');
1084: }
1085: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1086: .getEntryKey());
1087: if (entry == null)
1088: throw new RuntimeException(identifier
1089: + ": Space Entry not valid");
1090: return entry.loadResource(identifier, defs);
1091: }
1092:
1093: /**
1094: * Add this resource to the StoreEntry of the space entry.
1095: * @param sentry The resource space entry.
1096: * @param resource The resource to add.
1097: * @param defs Default attribute values.
1098: */
1099: public ResourceReference addResource(SpaceEntry sentry,
1100: Resource resource, Hashtable defs) {
1101: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1102: .getEntryKey());
1103: if (entry == null)
1104: throw new RuntimeException("Unable to add resource ("
1105: + resource.getIdentifier()
1106: + "), no StoreEntry for its space entry");
1107: return entry.addResource(resource, defs);
1108: }
1109:
1110: /**
1111: * Save this resource to the StoreEntry of the space entry.
1112: * @param sentry The resource space entry
1113: * @param resource The resource to save.
1114: */
1115: public void saveResource(SpaceEntry sentry, Resource resource) {
1116: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1117: .getEntryKey());
1118: if (entry == null)
1119: throw new RuntimeException("Unable to save resource ("
1120: + resource.getIdentifier()
1121: + "), no StoreEntry for its space entry");
1122: entry.saveResource(resource);
1123: }
1124:
1125: /**
1126: * Mark the given resource as being modified.
1127: * @param sentry The resource space entry.
1128: * @param resource The resource to mark as modified.
1129: */
1130: public void markModified(SpaceEntry sentry, Resource resource) {
1131: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1132: .getEntryKey());
1133: if (entry == null)
1134: throw new RuntimeException("Unable to mark resource ("
1135: + resource.getIdentifier()
1136: + "), no StoreEntry for its space entry");
1137: synchronized (resource) {
1138: entry.markModified(resource);
1139: }
1140: }
1141:
1142: /**
1143: * Rename a resource in this resource space.
1144: * @param sentry The resource space entry.
1145: * @param oldid The old resorce identifier.
1146: * @param newid The new resorce identifier.
1147: */
1148: public void renameResource(SpaceEntry sentry, String oldid,
1149: String newid) {
1150: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1151: .getEntryKey());
1152: if (entry == null)
1153: throw new RuntimeException("Unable to rename resource ("
1154: + oldid + " to " + newid
1155: + "), no StoreEntry for its space entry");
1156: entry.renameResource(oldid, newid);
1157: }
1158:
1159: /**
1160: * delete this resource from the StoreEntry (and the repository).
1161: * @param sentry The resource space entry
1162: * @param resource The resource to delete.
1163: */
1164: public void deleteResource(SpaceEntry sentry, Resource resource) {
1165: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1166: .getEntryKey());
1167: if (entry == null)
1168: throw new RuntimeException("Unable to delete resource ("
1169: + resource.getIdentifier()
1170: + "), no StoreEntry for its space entry");
1171: entry.removeResource(resource.getIdentifier());
1172: }
1173:
1174: /**
1175: * Delete all the children of resource indentified by its
1176: * space entry.
1177: * @param sentry The resource space entry
1178: */
1179: public void deleteChildren(SpaceEntry sentry) {
1180: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1181: .getEntryKey());
1182: if (entry == null)
1183: throw new RuntimeException(
1184: "Unable to delete children, no StoreEntry"
1185: + " for its space entry");
1186: entry.deleteStore();
1187: entries.remove(sentry.getEntryKey());
1188: }
1189:
1190: /**
1191: * Save all the children of the resource indentified by its
1192: * spaec entry.
1193: * @param sentry The resource space entry
1194: */
1195: public void saveChildren(SpaceEntry sentry) {
1196: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1197: .getEntryKey());
1198: if (entry == null)
1199: throw new RuntimeException(
1200: "Unable to save children, no StoreEntry"
1201: + " for its space entry");
1202: entry.saveStore();
1203: }
1204:
1205: protected void checkMaxLoadedStore() {
1206: // Check to see if we have exceeded our quota:
1207: if (loadedStore > getMaxLoadedStore()) {
1208: sweeper.sweep();
1209: }
1210: }
1211:
1212: /**
1213: * acquire children from an external file.
1214: * @param sentry The resource space entry.
1215: * @param repository The file used to store children.
1216: */
1217: public void acquireChildren(SpaceEntry sentry, File repository,
1218: boolean transientFlag) {
1219: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1220: .getEntryKey());
1221: if (entry == null) {
1222: //create it.
1223: checkMaxLoadedStore();
1224: entry = new NewStoreEntry(this , repository, sentry
1225: .getEntryKey());
1226: entry.setTransient(transientFlag);
1227: entries.put(sentry.getEntryKey(), entry);
1228: // FIXME : unload index hashtable ?
1229: } else {
1230: entry.rep = repository;
1231: }
1232: }
1233:
1234: /**
1235: * Acquire the StoreEntry of the space entry.
1236: * @param sentry The resource space entry.
1237: */
1238: public void acquireChildren(SpaceEntry sentry) {
1239: //
1240: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1241: .getEntryKey());
1242: if (entry == null) {
1243: //create it.
1244: checkMaxLoadedStore();
1245: String rpath = createResourceStoreRepository();
1246: File repository = new File(storedir, rpath);
1247: if (repository.exists()) {
1248: String fpath = new String(rpath);
1249: String lpath = null;
1250: do {
1251: lpath = new String(rpath);
1252: rpath = createResourceStoreRepository();
1253: repository = new File(storedir, rpath);
1254: } while (repository.exists());
1255: if (fpath.equals(lpath))
1256: warning("repository " + fpath + " exists! using "
1257: + rpath);
1258: else
1259: warning("repositories " + fpath + " to " + lpath
1260: + " exists! using " + rpath);
1261: }
1262: entry = new NewStoreEntry(this , rpath, sentry.getEntryKey());
1263: entries.put(sentry.getEntryKey(), entry);
1264: // FIXME : unload index hashtable ?
1265: }
1266: }
1267:
1268: /**
1269: * Implementation of Status interface
1270: * Display statistics about usage of the resource store manager
1271: * @returns a String containing a fragment of HTML
1272: */
1273: public String getHTMLStatus() {
1274: Enumeration elements = entries.elements();
1275: int nb_entries = 0;
1276: int entries_nn = 0;
1277: int nb_entries_store = 0;
1278: int stores_nn = 0;
1279: int oversized = 0;
1280: while (elements.hasMoreElements()) {
1281: int nb;
1282: NewStoreEntry entry = (NewStoreEntry) elements
1283: .nextElement();
1284: if (entry.references != null) {
1285: nb = entry.references.size();
1286: if (nb > 0) {
1287: nb_entries += nb;
1288: entries_nn++;
1289: }
1290: }
1291: if (entry.store != null) {
1292: ResourceStoreImpl st = (ResourceStoreImpl) entry.store;
1293: if (st.resources != null) {
1294: nb = st.resources.size();
1295: if (nb > 0) {
1296: int ssize = st.resources.size();
1297: nb_entries_store += ssize;
1298: stores_nn++;
1299: if ((storeSizeLimit > 0)
1300: && (ssize > storeSizeLimit)) {
1301: oversized++;
1302: }
1303: }
1304: }
1305: }
1306: }
1307: StringBuffer sb = new StringBuffer();
1308: sb.append("<table border=\"1\" class=\"store\">\n"
1309: + "<caption>Resource Store usage</caption>\n"
1310: + "<tr><th>Loaded Stores</th>"
1311: + "<th>Loaded Entries</th></tr>\n");
1312: sb.append("<tr><td>");
1313: sb.append(loadedStore);
1314: sb.append(" (");
1315: sb.append(entries.size());
1316: sb.append(") / ");
1317: sb.append(maxLoadedStore);
1318: if (storeSizeLimit >= 0) {
1319: sb.append(" [");
1320: sb.append(oversized);
1321: sb.append(" > ");
1322: sb.append(storeSizeLimit);
1323: sb.append(" ]");
1324: }
1325: sb.append("</td><td>");
1326: sb.append(nb_entries);
1327: sb.append('(');
1328: sb.append(entries_nn);
1329: sb.append(')');
1330: sb.append(" / ");
1331: sb.append(nb_entries_store);
1332: sb.append('(');
1333: sb.append(stores_nn);
1334: sb.append(")</td>\n</table>");
1335: return sb.toString();
1336: }
1337:
1338: /**
1339: * Enumerate the name (ie identifiers) of the space entry children.
1340: * @param sentry The space entry.
1341: * @param all Should all resources be listed.
1342: * @return An enumeration, providing one element per child, which is
1343: * the name of the child, as a String.
1344: */
1345: public Enumeration enumerateResourceIdentifiers(SpaceEntry sentry) {
1346: NewStoreEntry entry = (NewStoreEntry) entries.get(sentry
1347: .getEntryKey());
1348: if (entry == null)
1349: throw new RuntimeException(
1350: "Unable to list children, no StoreEntry"
1351: + " for its space entry");
1352: return entry.enumerateResourceIdentifiers();
1353: }
1354:
1355: /**
1356: * Create a new resource store manager for given store directory.
1357: * The resource store manager will manage the collection of stores
1358: * contained in the directory, and keep track of the stores state.
1359: * @param storedir The store directory to manage.
1360: */
1361: public ResourceStoreManager(String server_name, File storedir,
1362: String default_root_class, String default_root_name,
1363: String serializer_class, int max_loaded_store,
1364: Hashtable defs) {
1365: this (server_name, storedir, default_root_class,
1366: default_root_name, serializer_class, max_loaded_store,
1367: -1, defs);
1368: }
1369:
1370: /**
1371: * Create a new resource store manager for given store directory.
1372: * The resource store manager will manage the collection of stores
1373: * contained in the directory, and keep track of the stores state.
1374: * @param storedir The store directory to manage.
1375: */
1376: public ResourceStoreManager(String server_name, File storedir,
1377: String default_root_class, String default_root_name,
1378: String serializer_class, int max_loaded_store,
1379: int store_size_limit, Hashtable defs) {
1380: // Initialize the instance variables:
1381: this .server_name = server_name;
1382: this .storedir = storedir;
1383: this .entries = new Hashtable();
1384: this .sweeper = new StoreManagerSweeper(this );
1385: this .lru = new AsyncLRUList();
1386: this .maxLoadedStore = (max_loaded_store < 10) ? 10
1387: : max_loaded_store;
1388: this .storeSizeLimit = (store_size_limit < 100) ? -1
1389: : store_size_limit;
1390: this .loadedStore = 0;
1391:
1392: try {
1393: Class ser_class = Class.forName(serializer_class);
1394: this .serializer = (Serializer) ser_class.newInstance();
1395: } catch (Exception ex) {
1396: ex.printStackTrace();
1397: throw new RuntimeException("Invalid serializer class : "
1398: + serializer_class);
1399: }
1400:
1401: loadNewEntriesIndex();
1402:
1403: if (!checkSubDirs())
1404: throw new RuntimeException("Unable to create store "
1405: + "subdirectories!");
1406:
1407: // If not already available, create the root resource, and its
1408: // repository.
1409: getRootRepository();
1410: if (!root_repository.exists()) {
1411: try {
1412: Class root_class = Class.forName(default_root_class);
1413: Resource root = (Resource) root_class.newInstance();
1414: if (defs == null)
1415: defs = new Hashtable(4);
1416: defs.put("identifier".intern(), default_root_name);
1417: defs.put("key".intern(), getRootKey());
1418: root.initialize(defs);
1419:
1420: NewStoreEntry entry = new NewStoreEntry(this , ROOT_REP,
1421: getRootKey());
1422: ResourceReference rr = entry.addResource(root, defs);
1423: ResourceContext context = (ResourceContext) defs
1424: .get("context");
1425: context.setResourceReference(rr);
1426: entry.saveResource(root);
1427: entries.put(getRootKey(), entry);
1428: saveNewEntriesIndex();
1429: } catch (InstantiationException ex) {
1430: } catch (IllegalAccessException ex) {
1431: } catch (ClassNotFoundException ex) {
1432: System.out.println(ex.getMessage());
1433: ex.printStackTrace();
1434: }
1435: }
1436:
1437: // If not already available, create the resource store state object:
1438: File rsmstate = new File(storedir, STATE_F);
1439: // Restore it:
1440: Reader reader = null;
1441: try {
1442: reader = new BufferedReader(new FileReader(rsmstate));
1443: AttributeHolder states[] = serializer
1444: .readAttributeHolders(reader);
1445: this .state = (ResourceStoreState) states[0];
1446: } catch (Exception ex) {
1447: // Let's try to fix this:
1448: this .state = salvageState();
1449: }
1450: if (reader != null) {
1451: try {
1452: reader.close();
1453: } catch (IOException ex) {
1454: }
1455: }
1456: // salvage();
1457: // displayIndex();
1458: }
1459:
1460: }
|