001: // DirectoryResource.java
002: // $Id: DirectoryResource.java,v 1.24 2003/06/06 14:23:14 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.tools.resources;
007:
008: import java.util.Enumeration;
009: import java.util.Hashtable;
010:
011: import java.io.File;
012: import java.io.PrintStream;
013: import java.io.RandomAccessFile;
014:
015: import org.w3c.tools.resources.indexer.IndexerModule;
016: import org.w3c.tools.resources.indexer.ResourceIndexer;
017:
018: import org.w3c.tools.resources.event.StructureChangedEvent;
019:
020: /**
021: * A simple, and reasonably efficient directory resource.
022: */
023: public class DirectoryResource extends ContainerResource {
024: /**
025: * Attribute index - The index for our directory attribute.
026: */
027: protected static int ATTR_DIRECTORY = -1;
028: /**
029: * Attribute index - The last time we physically visited the directory.
030: */
031: protected static int ATTR_DIRSTAMP = -1;
032: /**
033: * Attribute index - The indexer to use for that directory, if any.
034: */
035: protected static int ATTR_INDEXER = -1;
036: /**
037: * Attribute index - The index of wether we are extensible.
038: */
039: protected static int ATTR_EXTENSIBLE = -1;
040: /**
041: * Attribute index - The index of wether we can be shrinked.
042: */
043: protected static int ATTR_SHRINKABLE = -1;
044:
045: static String di = "directory".intern();
046:
047: static {
048: Attribute a = null;
049: Class cls = null;
050: // Get a pointer to our class.
051: try {
052: cls = Class
053: .forName("org.w3c.tools.resources.DirectoryResource");
054: } catch (Exception ex) {
055: ex.printStackTrace();
056: System.exit(1);
057: }
058: // The directory attribute.
059: a = new FileAttribute("directory", null, Attribute.COMPUTED
060: | Attribute.DONTSAVE);
061: ATTR_DIRECTORY = AttributeRegistry.registerAttribute(cls, a);
062: // The last time we visited the directory
063: a = new DateAttribute("dirstamp", null, Attribute.COMPUTED);
064: ATTR_DIRSTAMP = AttributeRegistry.registerAttribute(cls, a);
065: // Our indexer name (optional).
066: a = new StringAttribute("indexer", null, Attribute.EDITABLE);
067: ATTR_INDEXER = AttributeRegistry.registerAttribute(cls, a);
068: // Are we extensible (can we create resources on the fly):
069: a = new BooleanAttribute("extensible", Boolean.TRUE,
070: Attribute.EDITABLE);
071: ATTR_EXTENSIBLE = AttributeRegistry.registerAttribute(cls, a);
072: // Are we shrinkable (can we delete resources on the fly):
073: a = new BooleanAttribute("shrinkable", Boolean.TRUE,
074: Attribute.EDITABLE);
075: ATTR_SHRINKABLE = AttributeRegistry.registerAttribute(cls, a);
076: }
077:
078: /**
079: * Get the indexer out of the given context.
080: * @return A ResourceIndexer instance, guaranteeed not to be <strong>
081: * null</strong>.
082: */
083: protected ResourceReference getIndexer(ResourceContext c) {
084: IndexerModule m = (IndexerModule) c
085: .getModule(IndexerModule.NAME);
086: ResourceReference rr = m.getIndexer(c);
087: return rr;
088: }
089:
090: public void setValue(int idx, Object value) {
091: super .setValue(idx, value);
092: if (idx == ATTR_INDEXER) {
093: String indexer = getString(ATTR_INDEXER, null);
094: if (indexer != null) {
095: ResourceContext c = null;
096: IndexerModule m = null;
097: c = getContext();
098: m = (IndexerModule) c.getModule(IndexerModule.NAME);
099: m.registerIndexer(c, indexer);
100: }
101: }
102: }
103:
104: /**
105: * Get the physical directory exported by this resource.
106: * @return A non-null File object giving the directory of this resource.
107: */
108:
109: public File getDirectory() {
110: return (File) getValue(ATTR_DIRECTORY, null);
111: }
112:
113: /**
114: * Get the physical directory exported by this resource.
115: * @return A non-null File object giving the directory of this resource.
116: */
117:
118: public File unsafeGetDirectory() {
119: return (File) unsafeGetValue(ATTR_DIRECTORY, null);
120: }
121:
122: /**
123: * Get the absolute time at which we examined the physicall directory.
124: * @return The date (as a long number of ms since Java epoch), or
125: * <strong>-1</strong> if we never examined it before.
126: */
127:
128: public long getDirStamp() {
129: return getLong(ATTR_DIRSTAMP, -1);
130: }
131:
132: /**
133: * Get the extensible flag value.
134: * A DirectoryResource is extensible, if it is allowed to create new
135: * resources out of the file system knowledge on the fly.
136: * <p>Setting this flag might slow down the server. It unfortunatelly
137: * defaults to <strong>true</strong> until I have a decent admin
138: * program.
139: * @return A boolean <strong>true</strong> if the directory is
140: * extensible.
141: */
142:
143: public boolean getExtensibleFlag() {
144: return getBoolean(ATTR_EXTENSIBLE, true);
145: }
146:
147: /**
148: * Get the extensible flag value.
149: * A DirectoryResource is extensible, if it is allowed to create new
150: * resources out of the file system knowledge on the fly.
151: * <p>Setting this flag might slow down the server. It unfortunatelly
152: * defaults to <strong>true</strong> until I have a decent admin
153: * program.
154: * @return A boolean <strong>true</strong> if the directory is
155: * extensible.
156: */
157:
158: public boolean getShrinkableFlag() {
159: return getBoolean(ATTR_SHRINKABLE, true);
160: }
161:
162: /**
163: * Get the extensible flag value.
164: * A DirectoryResource is extensible, if it is allowed to create new
165: * resources out of the file system knowledge on the fly.
166: * <p>Setting this flag might slow down the server. It unfortunatelly
167: * defaults to <strong>true</strong> until I have a decent admin
168: * program.
169: * @return A boolean <strong>true</strong> if the directory is
170: * extensible.
171: */
172:
173: public boolean unsafeGetShrinkableFlag() {
174: Object value = unsafeGetValue(ATTR_SHRINKABLE, null);
175: if (value == null) {
176: return true;
177: } else if (value instanceof Boolean) {
178: return ((Boolean) value).booleanValue();
179: } else {
180: throw new IllegalAttributeAccess(this ,
181: attributes[ATTR_SHRINKABLE], "getBoolean");
182: }
183: }
184:
185: /**
186: * A resource is about to be removed
187: * This handles the <code>RESOURCE_REMOVED</code> kind of events.
188: * @param evt The event describing the change.
189: */
190: public void resourceRemoved(StructureChangedEvent evt) {
191: super .resourceRemoved(evt);
192: if (!isUnloaded())
193: markModified();
194: }
195:
196: /**
197: * Create a DirectoryResource and the physical directory too.
198: * @param name the name of the resource.
199: * @return A ResourceReference instance.
200: */
201: public ResourceReference createDirectoryResource(String name) {
202: // Create an empty file:
203: File file = new File(getDirectory(), name);
204: boolean created = false;
205: boolean exists_before = false;
206:
207: try {
208: if (file.exists()) {
209: if (!file.isDirectory())
210: created = false;
211: else
212: exists_before = true;
213: } else {
214: file.mkdir();
215: created = true;
216: }
217: } catch (Exception ex) {
218: created = false;
219: }
220:
221: if (!created)
222: return null;
223:
224: ResourceReference rr = createDefaultResource(name);
225: if (rr == null) {
226: if (!exists_before)
227: file.delete();
228: return null;
229: }
230:
231: try {
232: Resource r = rr.lock();
233: if (!(r instanceof DirectoryResource)) {
234: try {
235: r.delete();
236: } catch (MultipleLockException ex) {
237: //OUCH!
238: //manual delete
239: }
240: if (!exists_before)
241: file.delete();
242: return null;
243: }
244: } catch (InvalidResourceException ex) {
245: if (!exists_before)
246: file.delete();
247: return null;
248: } finally {
249: rr.unlock();
250: }
251: return rr;
252: }
253:
254: /**
255: * Create a Resource and the physical file too.
256: * @param name the name of the resource.
257: * @return A ResourceReference instance.
258: */
259: public ResourceReference createResource(String name) {
260: return createResource(name, null);
261: }
262:
263: /**
264: * Create a Resource and the physical file too.
265: * @param name the name of the resource.
266: * @param req the protocol request.
267: * @return A ResourceReference instance.
268: */
269: public ResourceReference createResource(String name,
270: RequestInterface req) {
271: // Create an empty file:
272: File file = new File(getDirectory(), name);
273: boolean created = false;
274:
275: if (!file.exists()) {
276: try {
277: (new RandomAccessFile(file, "rw")).close();
278: created = true;
279: } catch (Exception ex) {
280: created = false;
281: }
282: }
283: if (!created)
284: return null;
285:
286: ResourceReference rr = createDefaultResource(name, req);
287: //if (rr == null)
288: file.delete();
289: return rr;
290: }
291:
292: /**
293: * Index a Resource. Call the indexer.
294: * @param name The name of the resource to index.
295: * @param defs The defaults attributes.
296: * @return A resource instance.
297: * @see org.w3c.tools.resources.indexer.SampleResourceIndexer
298: */
299: private Resource index(String name, Hashtable defs) {
300: return index(name, defs, null);
301: }
302:
303: /**
304: * Index a Resource. Call the indexer.
305: * @param name The name of the resource to index.
306: * @param defs The defaults attributes.
307: * @param req The protocol request.
308: * @return A resource instance.
309: * @see org.w3c.tools.resources.indexer.SampleResourceIndexer
310: */
311: protected Resource index(String name, Hashtable defs,
312: RequestInterface req) {
313: // Prepare a set of default parameters for the resource:
314: defs.put(id, name);
315: updateDefaultChildAttributes(defs);
316: ResourceContext context = getContext();
317: // Try to get the indexer to create the resource:
318: Resource resource = null;
319: ResourceReference rr_indexer = null;
320: ResourceReference rr_lastidx = null;
321: while (context != null) {
322: // Lookup for next indexer in hierarchy:
323: do {
324: rr_indexer = getIndexer(context);
325: context = context.getParent();
326: } while ((rr_indexer == rr_lastidx) && (context != null));
327: // Is this a useful indexer ?
328: if ((rr_lastidx = rr_indexer) != null) {
329: try {
330: ResourceIndexer indexer = (ResourceIndexer) rr_indexer
331: .lock();
332: resource = indexer.createResource(this , req,
333: getDirectory(), name, defs);
334: if (resource != null)
335: break;
336: } catch (InvalidResourceException ex) {
337: resource = null;
338: } finally {
339: rr_indexer.unlock();
340: }
341: }
342: }
343: return resource;
344: }
345:
346: /**
347: * Get the name of the resource relative to the given filename.
348: * @param name The name of the file.
349: * @return a String, the resource name.
350: * @see org.w3c.tools.resources.indexer.SampleResourceIndexer
351: */
352: protected String getIndexedName(String name) {
353: ResourceContext context = getContext();
354: String indexed = null;
355: ResourceReference rr_indexer = null;
356: ResourceReference rr_lastidx = null;
357: while (context != null) {
358: // Lookup for next indexer in hierarchy:
359: do {
360: rr_indexer = getIndexer(context);
361: context = context.getParent();
362: } while ((rr_indexer == rr_lastidx) && (context != null));
363: if ((rr_lastidx = rr_indexer) != null) {
364: try {
365: ResourceIndexer indexer = (ResourceIndexer) rr_indexer
366: .lock();
367: indexed = indexer.getIndexedName(getDirectory(),
368: name);
369: if (indexed != null)
370: break;
371: } catch (InvalidResourceException ex) {
372: indexed = null;
373: } finally {
374: rr_indexer.unlock();
375: }
376: }
377: }
378: return ((indexed == null) ? name : indexed);
379: }
380:
381: public synchronized ResourceReference createDefaultResource(
382: String name) {
383: return createDefaultResource(name, null);
384: }
385:
386: /**
387: * Try creating a default resource having the given name.
388: * This method will make its best effort to create a default resource
389: * having this name in the directory. If a file with this name exists,
390: * it will check the pre-defined admin extensions and look for a match.
391: * If a directory with this name exists, and admin allows to do so, it
392: * will create a sub-directory resource.
393: * @param name The name of the resource to try to create.
394: * @param req The incomming request
395: * @return A Resource instance, if possible, <strong>null</strong>
396: * otherwise.
397: */
398:
399: protected synchronized ResourceReference createDefaultResource(
400: String name, RequestInterface req) {
401: // Don't automagically create resources of name '..' or '.'
402: if (name.equals("..") || name.equals(".")
403: || (name.indexOf('\\') >= 0)) {
404: return null;
405: }
406: Hashtable defs = new Hashtable(10);
407: Resource resource = index(name, defs, req);
408: // Did we finally create a resource ?
409: ResourceReference rr = null;
410: if (resource != null) {
411: // Register this child in our store:
412: rr = addResource(resource, defs);
413: markModified();
414: }
415: return rr;
416: }
417:
418: /**
419: * Initialize and register a new resource into this directory.
420: * @param resource The uninitialized resource to be added.
421: */
422: protected ResourceContext updateDefaultChildAttributes(
423: Hashtable attrs) {
424: ResourceContext context = null;
425: context = super .updateDefaultChildAttributes(attrs);
426: String name = (String) attrs.get(id);
427: if ((name != null) && (getDirectory() != null)) {
428: attrs.put(di, new File(getDirectory(), name));
429: }
430: return context;
431: }
432:
433: /**
434: * Reindex recursivly all the resources from this DirectoryResource.
435: * @param rec recursivly?
436: */
437: public synchronized void reindex(boolean rec) {
438: if (getExtensibleFlag()) {
439: Enumeration e = enumerateAllResourceIdentifiers();
440: String name = null;
441: ResourceReference rr = null;
442: Resource r = null;
443: while (e.hasMoreElements()) {
444: name = (String) e.nextElement();
445: rr = lookup(name);
446: if (rr != null) {
447: try {
448: r = rr.lock();
449: // forbid cycles
450: if (r == this )
451: continue;
452: if (r instanceof DirectoryResource) {
453: //launch reindex
454: DirectoryResource dir = (DirectoryResource) r;
455: //reindex directory itself
456: //the new diretory must have the same context
457: Hashtable defs = new Hashtable(5);
458: defs.put(co, dir.getContext());
459: //indexing ...
460: Resource newdir = index(name, defs);
461: // do we want it to keep its indexer?
462: if ((newdir == null) && rec) {
463: dir.reindex(true);
464: } else {
465: if (!(newdir instanceof DirectoryResource)) {
466: throw new RuntimeException(
467: "Reindex Error : "
468: + name
469: + " can't be reindexed. "
470: + "The reindexed resource is "
471: + "no more a DirectoryResource.");
472: }
473: DirectoryResource reindexed = (DirectoryResource) newdir;
474: String indexer = reindexed.getString(
475: ATTR_INDEXER, "");
476: if (indexer.equals("")) {
477: if (rec) {
478: dir.reindex(true);
479: }
480: indexer = dir.getString(
481: ATTR_INDEXER, null);
482: reindexed.setValue(ATTR_INDEXER,
483: indexer);
484: } else {
485: dir.setValue(ATTR_INDEXER, indexer);
486: if (rec) {
487: dir.reindex(true);
488: }
489: }
490: //move children to the reindexed directory
491: reindexed.setValue(ATTR_KEY, dir
492: .getKey());
493: dir.setValue(ATTR_IDENTIFIER, name
494: + "-bakindex");
495: addResource(reindexed, defs);
496: // Now replace the old DirectoryResource
497: // by the new one
498: try {
499: dir.replace(reindexed);
500: } catch (MultipleLockException ex) {
501: throw new RuntimeException(
502: "Reindex Error : "
503: + ex.getMessage());
504: }
505: }
506: } else if (!(r instanceof AbstractContainer)) {
507: //leaf
508: Hashtable resdefs = new Hashtable(10);
509: Resource resource = index(name, resdefs);
510: if (resource != null) {
511: try {
512: r.delete();
513: } catch (MultipleLockException ex) {
514: throw new RuntimeException(
515: "Reindex Error : "
516: + ex.getMessage());
517: }
518: addResource(resource, resdefs);
519: }
520: }
521: } catch (InvalidResourceException ex) {
522: System.out.println(ex.getMessage());
523: } finally {
524: rr.unlock();
525: }
526: }
527: }
528: markModified();
529: }
530: }
531:
532: /**
533: * Enumerate all available children resource identifiers.
534: * This method <em>requires</em> that we create all our pending resources.
535: * @return An enumeration of all our resources.
536: */
537: protected synchronized Enumeration enumerateAllResourceIdentifiers() {
538: File directory = getDirectory();
539: if (directory != null) {
540: synchronized (this ) {
541: String lst[] = directory.list();
542: if (lst != null) {
543: for (int i = 0; i < lst.length; i++) {
544: if (lst[i].equals(".") || lst[i].equals(".."))
545: continue;
546: if (super .lookup(lst[i]) == null) {
547: String indexed = getIndexedName(lst[i]);
548: if (indexed.equals(lst[i]))
549: createDefaultResource(lst[i]);
550: else if (super .lookup(indexed) == null)
551: createDefaultResource(lst[i]);
552: }
553: }
554: }
555: }
556: }
557: return super .enumerateResourceIdentifiers(true);
558: }
559:
560: /**
561: * Enumerate all available children resource identifiers.
562: * This method <em>requires</em> that we create all our pending resources
563: * if we are in the extensible mode...too bad !
564: * @return An enumeration of all our resources.
565: */
566: public synchronized Enumeration enumerateResourceIdentifiers(
567: boolean all) {
568: // If extensible, update if needed:
569: if (all && getExtensibleFlag()) {
570: File directory = getDirectory();
571: if (directory != null) {
572: synchronized (this ) {
573: long dirstamp = directory.lastModified();
574: if (dirstamp > getDirStamp()) {
575: String lst[] = directory.list();
576: if (lst != null) {
577: for (int i = 0; i < lst.length; i++) {
578: if (lst[i].equals(".")
579: || lst[i].equals(".."))
580: continue;
581: if (super .lookup(lst[i]) == null) {
582: String indexed = getIndexedName(lst[i]);
583: if (indexed.equals(lst[i]))
584: createDefaultResource(lst[i]);
585: else if (super .lookup(indexed) == null)
586: createDefaultResource(lst[i]);
587: }
588: }
589: }
590: setLong(ATTR_DIRSTAMP, dirstamp);
591: }
592: }
593: }
594: }
595: return super .enumerateResourceIdentifiers(all);
596: }
597:
598: /**
599: * Lookup the resource having the given name in this directory.
600: * @param name The name of the resource.
601: * @return A resource instance, or <strong>null</strong>.
602: */
603: public ResourceReference lookup(String name) {
604: ResourceReference rr = null;
605: // Try our store:
606: rr = super .lookup(name);
607: if (rr != null)
608: return rr;
609: // If allowed, than try a default fallback:
610: return getExtensibleFlag() ? createDefaultResource(name) : null;
611: }
612:
613: /**
614: * Delete this directory resource, for ever.
615: * This method will delete the directory resource, and its associated
616: * store, <strong>along</strong> with any of the sub-resources it contains.
617: * Deleting the root directory of your server might take sometime...
618: * <p>Once the resource is deleted, it isx1 removed from its inital store
619: * and will not be unpickleable any more.
620: * @exception MultipleLockException if someone has locked this resource.
621: */
622:
623: public synchronized void delete() throws MultipleLockException {
624: disableEvent();
625: // Remove all the defined resources in this directory
626: // Set the extensible flag to false, otherwise, the directory grows
627: // as we shrink it :-)
628: setBoolean(ATTR_EXTENSIBLE, false);
629: super .delete();
630: }
631:
632: /**
633: * Was return false (don't khow why)
634: */
635: public synchronized boolean verify() {
636: return getDirectory().exists();
637: }
638:
639: /**
640: * Initialize this directory resource with the given set of attributes.
641: * @param values The attribute values.
642: */
643: public void initialize(Object values[]) {
644: super .initialize(values);
645: disableEvent();
646: // Get our parent resource and compute our directory:
647: File dir = null;
648: if (!definesAttribute(ATTR_DIRECTORY)) {
649: // Get our parent:
650: ResourceReference rr = getParent();
651: if (rr != null) {
652: try {
653: Resource parent = rr.lock();
654: if (parent.definesAttribute(di)) {
655: File pdir = (File) parent.getValue(di, null);
656: if (pdir != null) {
657: // Compute and set our directory attribute:
658: dir = new File(pdir, getIdentifier());
659: setValue(ATTR_DIRECTORY, dir);
660: }
661: }
662: } catch (InvalidResourceException ex) {
663:
664: } finally {
665: rr.unlock();
666: }
667: }
668: } else {
669: dir = getDirectory();
670: }
671: // Register our specific indexer, if any:
672: ResourceContext c = getContext();
673: String indexer = getString(ATTR_INDEXER, null);
674:
675: if ((indexer != null) && (!indexer.equals(""))) {
676: IndexerModule m = (IndexerModule) c
677: .getModule(IndexerModule.NAME);
678: m.registerIndexer(c, indexer);
679: }
680: enableEvent();
681: }
682:
683: }
|