001: /*
002: * Copyright 1999,2004 The Apache Software Foundation.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.naming.resources;
018:
019: import java.io.File;
020: import java.io.IOException;
021: import java.io.InputStream;
022: import java.util.Arrays;
023: import java.util.Date;
024: import java.util.Enumeration;
025: import java.util.Hashtable;
026: import java.util.Vector;
027: import java.util.zip.ZipEntry;
028: import java.util.zip.ZipException;
029: import java.util.zip.ZipFile;
030:
031: import javax.naming.CompositeName;
032: import javax.naming.Name;
033: import javax.naming.NamingEnumeration;
034: import javax.naming.NamingException;
035: import javax.naming.OperationNotSupportedException;
036: import javax.naming.directory.Attributes;
037: import javax.naming.directory.DirContext;
038: import javax.naming.directory.ModificationItem;
039: import javax.naming.directory.SearchControls;
040:
041: import org.apache.naming.NamingContextBindingsEnumeration;
042: import org.apache.naming.NamingContextEnumeration;
043: import org.apache.naming.NamingEntry;
044:
045: /**
046: * WAR Directory Context implementation.
047: *
048: * @author Remy Maucherat
049: * @version $Revision: 1.5 $ $Date: 2004/03/13 17:51:53 $
050: */
051:
052: public class WARDirContext extends BaseDirContext {
053:
054: // ----------------------------------------------------------- Constructors
055:
056: /**
057: * Builds a WAR directory context using the given environment.
058: */
059: public WARDirContext() {
060: super ();
061: }
062:
063: /**
064: * Builds a WAR directory context using the given environment.
065: */
066: public WARDirContext(Hashtable env) {
067: super (env);
068: }
069:
070: /**
071: * Constructor used for returning fake subcontexts.
072: */
073: protected WARDirContext(ZipFile base, Entry entries) {
074: this .base = base;
075: this .entries = entries;
076: }
077:
078: // ----------------------------------------------------- Instance Variables
079:
080: /**
081: * The WAR file.
082: */
083: protected ZipFile base = null;
084:
085: /**
086: * WAR entries.
087: */
088: protected Entry entries = null;
089:
090: // ------------------------------------------------------------- Properties
091:
092: /**
093: * Set the document root.
094: *
095: * @param docBase The new document root
096: *
097: * @exception IllegalArgumentException if the specified value is not
098: * supported by this implementation
099: * @exception IllegalArgumentException if this would create a
100: * malformed URL
101: */
102: public void setDocBase(String docBase) {
103:
104: // Validate the format of the proposed document root
105: if (docBase == null)
106: throw new IllegalArgumentException(sm
107: .getString("resources.null"));
108: if (!(docBase.endsWith(".war")))
109: throw new IllegalArgumentException(sm
110: .getString("warResources.notWar"));
111:
112: // Calculate a File object referencing this document base directory
113: File base = new File(docBase);
114:
115: // Validate that the document base is an existing directory
116: if (!base.exists() || !base.canRead() || base.isDirectory())
117: throw new IllegalArgumentException(sm.getString(
118: "warResources.invalidWar", docBase));
119: try {
120: this .base = new ZipFile(base);
121: } catch (Exception e) {
122: throw new IllegalArgumentException(sm.getString(
123: "warResources.invalidWar", e.getMessage()));
124: }
125: super .setDocBase(docBase);
126:
127: loadEntries();
128:
129: }
130:
131: // --------------------------------------------------------- Public Methods
132:
133: /**
134: * Release any resources allocated for this directory context.
135: */
136: public void release() {
137:
138: entries = null;
139: if (base != null) {
140: try {
141: base.close();
142: } catch (IOException e) {
143: System.out.println("Exception closing WAR File "
144: + base.getName());
145: e.printStackTrace(System.out);
146: }
147: }
148: base = null;
149: super .release();
150:
151: }
152:
153: // -------------------------------------------------------- Context Methods
154:
155: /**
156: * Retrieves the named object.
157: *
158: * @param name the name of the object to look up
159: * @return the object bound to name
160: * @exception NamingException if a naming exception is encountered
161: */
162: public Object lookup(String name) throws NamingException {
163: return lookup(new CompositeName(name));
164: }
165:
166: /**
167: * Retrieves the named object. If name is empty, returns a new instance
168: * of this context (which represents the same naming context as this
169: * context, but its environment may be modified independently and it may
170: * be accessed concurrently).
171: *
172: * @param name the name of the object to look up
173: * @return the object bound to name
174: * @exception NamingException if a naming exception is encountered
175: */
176: public Object lookup(Name name) throws NamingException {
177: if (name.isEmpty())
178: return this ;
179: Entry entry = treeLookup(name);
180: if (entry == null)
181: throw new NamingException(sm.getString(
182: "resources.notFound", name));
183: ZipEntry zipEntry = entry.getEntry();
184: if (zipEntry.isDirectory())
185: return new WARDirContext(base, entry);
186: else
187: return new WARResource(entry.getEntry());
188: }
189:
190: /**
191: * Unbinds the named object. Removes the terminal atomic name in name
192: * from the target context--that named by all but the terminal atomic
193: * part of name.
194: * <p>
195: * This method is idempotent. It succeeds even if the terminal atomic
196: * name is not bound in the target context, but throws
197: * NameNotFoundException if any of the intermediate contexts do not exist.
198: *
199: * @param name the name to bind; may not be empty
200: * @exception NameNotFoundException if an intermediate context does not
201: * exist
202: * @exception NamingException if a naming exception is encountered
203: */
204: public void unbind(String name) throws NamingException {
205: throw new OperationNotSupportedException();
206: }
207:
208: /**
209: * Binds a new name to the object bound to an old name, and unbinds the
210: * old name. Both names are relative to this context. Any attributes
211: * associated with the old name become associated with the new name.
212: * Intermediate contexts of the old name are not changed.
213: *
214: * @param oldName the name of the existing binding; may not be empty
215: * @param newName the name of the new binding; may not be empty
216: * @exception NameAlreadyBoundException if newName is already bound
217: * @exception NamingException if a naming exception is encountered
218: */
219: public void rename(String oldName, String newName)
220: throws NamingException {
221: throw new OperationNotSupportedException();
222: }
223:
224: /**
225: * Enumerates the names bound in the named context, along with the class
226: * names of objects bound to them. The contents of any subcontexts are
227: * not included.
228: * <p>
229: * If a binding is added to or removed from this context, its effect on
230: * an enumeration previously returned is undefined.
231: *
232: * @param name the name of the context to list
233: * @return an enumeration of the names and class names of the bindings in
234: * this context. Each element of the enumeration is of type NameClassPair.
235: * @exception NamingException if a naming exception is encountered
236: */
237: public NamingEnumeration list(String name) throws NamingException {
238: return list(new CompositeName(name));
239: }
240:
241: /**
242: * Enumerates the names bound in the named context, along with the class
243: * names of objects bound to them. The contents of any subcontexts are
244: * not included.
245: * <p>
246: * If a binding is added to or removed from this context, its effect on
247: * an enumeration previously returned is undefined.
248: *
249: * @param name the name of the context to list
250: * @return an enumeration of the names and class names of the bindings in
251: * this context. Each element of the enumeration is of type NameClassPair.
252: * @exception NamingException if a naming exception is encountered
253: */
254: public NamingEnumeration list(Name name) throws NamingException {
255: if (name.isEmpty())
256: return new NamingContextEnumeration(list(entries));
257: Entry entry = treeLookup(name);
258: if (entry == null)
259: throw new NamingException(sm.getString(
260: "resources.notFound", name));
261: return new NamingContextEnumeration(list(entry));
262: }
263:
264: /**
265: * Enumerates the names bound in the named context, along with the
266: * objects bound to them. The contents of any subcontexts are not
267: * included.
268: * <p>
269: * If a binding is added to or removed from this context, its effect on
270: * an enumeration previously returned is undefined.
271: *
272: * @param name the name of the context to list
273: * @return an enumeration of the bindings in this context.
274: * Each element of the enumeration is of type Binding.
275: * @exception NamingException if a naming exception is encountered
276: */
277: public NamingEnumeration listBindings(String name)
278: throws NamingException {
279: return listBindings(new CompositeName(name));
280: }
281:
282: /**
283: * Enumerates the names bound in the named context, along with the
284: * objects bound to them. The contents of any subcontexts are not
285: * included.
286: * <p>
287: * If a binding is added to or removed from this context, its effect on
288: * an enumeration previously returned is undefined.
289: *
290: * @param name the name of the context to list
291: * @return an enumeration of the bindings in this context.
292: * Each element of the enumeration is of type Binding.
293: * @exception NamingException if a naming exception is encountered
294: */
295: public NamingEnumeration listBindings(Name name)
296: throws NamingException {
297: if (name.isEmpty())
298: return new NamingContextBindingsEnumeration(list(entries));
299: Entry entry = treeLookup(name);
300: if (entry == null)
301: throw new NamingException(sm.getString(
302: "resources.notFound", name));
303: return new NamingContextBindingsEnumeration(list(entry));
304: }
305:
306: /**
307: * Destroys the named context and removes it from the namespace. Any
308: * attributes associated with the name are also removed. Intermediate
309: * contexts are not destroyed.
310: * <p>
311: * This method is idempotent. It succeeds even if the terminal atomic
312: * name is not bound in the target context, but throws
313: * NameNotFoundException if any of the intermediate contexts do not exist.
314: *
315: * In a federated naming system, a context from one naming system may be
316: * bound to a name in another. One can subsequently look up and perform
317: * operations on the foreign context using a composite name. However, an
318: * attempt destroy the context using this composite name will fail with
319: * NotContextException, because the foreign context is not a "subcontext"
320: * of the context in which it is bound. Instead, use unbind() to remove
321: * the binding of the foreign context. Destroying the foreign context
322: * requires that the destroySubcontext() be performed on a context from
323: * the foreign context's "native" naming system.
324: *
325: * @param name the name of the context to be destroyed; may not be empty
326: * @exception NameNotFoundException if an intermediate context does not
327: * exist
328: * @exception NotContextException if the name is bound but does not name
329: * a context, or does not name a context of the appropriate type
330: */
331: public void destroySubcontext(String name) throws NamingException {
332: throw new OperationNotSupportedException();
333: }
334:
335: /**
336: * Retrieves the named object, following links except for the terminal
337: * atomic component of the name. If the object bound to name is not a
338: * link, returns the object itself.
339: *
340: * @param name the name of the object to look up
341: * @return the object bound to name, not following the terminal link
342: * (if any).
343: * @exception NamingException if a naming exception is encountered
344: */
345: public Object lookupLink(String name) throws NamingException {
346: // Note : Links are not supported
347: return lookup(name);
348: }
349:
350: /**
351: * Retrieves the full name of this context within its own namespace.
352: * <p>
353: * Many naming services have a notion of a "full name" for objects in
354: * their respective namespaces. For example, an LDAP entry has a
355: * distinguished name, and a DNS record has a fully qualified name. This
356: * method allows the client application to retrieve this name. The string
357: * returned by this method is not a JNDI composite name and should not be
358: * passed directly to context methods. In naming systems for which the
359: * notion of full name does not make sense,
360: * OperationNotSupportedException is thrown.
361: *
362: * @return this context's name in its own namespace; never null
363: * @exception OperationNotSupportedException if the naming system does
364: * not have the notion of a full name
365: * @exception NamingException if a naming exception is encountered
366: */
367: public String getNameInNamespace() throws NamingException {
368: return docBase;
369: }
370:
371: // ----------------------------------------------------- DirContext Methods
372:
373: /**
374: * Retrieves selected attributes associated with a named object.
375: * See the class description regarding attribute models, attribute type
376: * names, and operational attributes.
377: *
378: * @return the requested attributes; never null
379: * @param name the name of the object from which to retrieve attributes
380: * @param attrIds the identifiers of the attributes to retrieve. null
381: * indicates that all attributes should be retrieved; an empty array
382: * indicates that none should be retrieved
383: * @exception NamingException if a naming exception is encountered
384: */
385: public Attributes getAttributes(String name, String[] attrIds)
386: throws NamingException {
387: return getAttributes(new CompositeName(name), attrIds);
388: }
389:
390: /**
391: * Retrieves all of the attributes associated with a named object.
392: *
393: * @return the set of attributes associated with name.
394: * Returns an empty attribute set if name has no attributes; never null.
395: * @param name the name of the object from which to retrieve attributes
396: * @exception NamingException if a naming exception is encountered
397: */
398: public Attributes getAttributes(Name name, String[] attrIds)
399: throws NamingException {
400:
401: Entry entry = null;
402: if (name.isEmpty())
403: entry = entries;
404: else
405: entry = treeLookup(name);
406: if (entry == null)
407: throw new NamingException(sm.getString(
408: "resources.notFound", name));
409:
410: ZipEntry zipEntry = entry.getEntry();
411:
412: ResourceAttributes attrs = new ResourceAttributes();
413: attrs.setCreationDate(new Date(zipEntry.getTime()));
414: attrs.setName(entry.getName());
415: if (!zipEntry.isDirectory())
416: attrs.setResourceType("");
417: attrs.setContentLength(zipEntry.getSize());
418: attrs.setLastModified(zipEntry.getTime());
419:
420: return attrs;
421:
422: }
423:
424: /**
425: * Modifies the attributes associated with a named object. The order of
426: * the modifications is not specified. Where possible, the modifications
427: * are performed atomically.
428: *
429: * @param name the name of the object whose attributes will be updated
430: * @param mod_op the modification operation, one of: ADD_ATTRIBUTE,
431: * REPLACE_ATTRIBUTE, REMOVE_ATTRIBUTE
432: * @param attrs the attributes to be used for the modification; may not
433: * be null
434: * @exception AttributeModificationException if the modification cannot be
435: * completed successfully
436: * @exception NamingException if a naming exception is encountered
437: */
438: public void modifyAttributes(String name, int mod_op,
439: Attributes attrs) throws NamingException {
440: throw new OperationNotSupportedException();
441: }
442:
443: /**
444: * Modifies the attributes associated with a named object using an an
445: * ordered list of modifications. The modifications are performed in the
446: * order specified. Each modification specifies a modification operation
447: * code and an attribute on which to operate. Where possible, the
448: * modifications are performed atomically.
449: *
450: * @param name the name of the object whose attributes will be updated
451: * @param mods an ordered sequence of modifications to be performed; may
452: * not be null
453: * @exception AttributeModificationException if the modification cannot be
454: * completed successfully
455: * @exception NamingException if a naming exception is encountered
456: */
457: public void modifyAttributes(String name, ModificationItem[] mods)
458: throws NamingException {
459: throw new OperationNotSupportedException();
460: }
461:
462: /**
463: * Binds a name to an object, along with associated attributes. If attrs
464: * is null, the resulting binding will have the attributes associated
465: * with obj if obj is a DirContext, and no attributes otherwise. If attrs
466: * is non-null, the resulting binding will have attrs as its attributes;
467: * any attributes associated with obj are ignored.
468: *
469: * @param name the name to bind; may not be empty
470: * @param obj the object to bind; possibly null
471: * @param attrs the attributes to associate with the binding
472: * @exception NameAlreadyBoundException if name is already bound
473: * @exception InvalidAttributesException if some "mandatory" attributes
474: * of the binding are not supplied
475: * @exception NamingException if a naming exception is encountered
476: */
477: public void bind(String name, Object obj, Attributes attrs)
478: throws NamingException {
479: throw new OperationNotSupportedException();
480: }
481:
482: /**
483: * Binds a name to an object, along with associated attributes,
484: * overwriting any existing binding. If attrs is null and obj is a
485: * DirContext, the attributes from obj are used. If attrs is null and obj
486: * is not a DirContext, any existing attributes associated with the object
487: * already bound in the directory remain unchanged. If attrs is non-null,
488: * any existing attributes associated with the object already bound in
489: * the directory are removed and attrs is associated with the named
490: * object. If obj is a DirContext and attrs is non-null, the attributes
491: * of obj are ignored.
492: *
493: * @param name the name to bind; may not be empty
494: * @param obj the object to bind; possibly null
495: * @param attrs the attributes to associate with the binding
496: * @exception InvalidAttributesException if some "mandatory" attributes
497: * of the binding are not supplied
498: * @exception NamingException if a naming exception is encountered
499: */
500: public void rebind(String name, Object obj, Attributes attrs)
501: throws NamingException {
502: throw new OperationNotSupportedException();
503: }
504:
505: /**
506: * Creates and binds a new context, along with associated attributes.
507: * This method creates a new subcontext with the given name, binds it in
508: * the target context (that named by all but terminal atomic component of
509: * the name), and associates the supplied attributes with the newly
510: * created object. All intermediate and target contexts must already
511: * exist. If attrs is null, this method is equivalent to
512: * Context.createSubcontext().
513: *
514: * @param name the name of the context to create; may not be empty
515: * @param attrs the attributes to associate with the newly created context
516: * @return the newly created context
517: * @exception NameAlreadyBoundException if the name is already bound
518: * @exception InvalidAttributesException if attrs does not contain all
519: * the mandatory attributes required for creation
520: * @exception NamingException if a naming exception is encountered
521: */
522: public DirContext createSubcontext(String name, Attributes attrs)
523: throws NamingException {
524: throw new OperationNotSupportedException();
525: }
526:
527: /**
528: * Retrieves the schema associated with the named object. The schema
529: * describes rules regarding the structure of the namespace and the
530: * attributes stored within it. The schema specifies what types of
531: * objects can be added to the directory and where they can be added;
532: * what mandatory and optional attributes an object can have. The range
533: * of support for schemas is directory-specific.
534: *
535: * @param name the name of the object whose schema is to be retrieved
536: * @return the schema associated with the context; never null
537: * @exception OperationNotSupportedException if schema not supported
538: * @exception NamingException if a naming exception is encountered
539: */
540: public DirContext getSchema(String name) throws NamingException {
541: throw new OperationNotSupportedException();
542: }
543:
544: /**
545: * Retrieves a context containing the schema objects of the named
546: * object's class definitions.
547: *
548: * @param name the name of the object whose object class definition is to
549: * be retrieved
550: * @return the DirContext containing the named object's class
551: * definitions; never null
552: * @exception OperationNotSupportedException if schema not supported
553: * @exception NamingException if a naming exception is encountered
554: */
555: public DirContext getSchemaClassDefinition(String name)
556: throws NamingException {
557: throw new OperationNotSupportedException();
558: }
559:
560: /**
561: * Searches in a single context for objects that contain a specified set
562: * of attributes, and retrieves selected attributes. The search is
563: * performed using the default SearchControls settings.
564: *
565: * @param name the name of the context to search
566: * @param matchingAttributes the attributes to search for. If empty or
567: * null, all objects in the target context are returned.
568: * @param attributesToReturn the attributes to return. null indicates
569: * that all attributes are to be returned; an empty array indicates that
570: * none are to be returned.
571: * @return a non-null enumeration of SearchResult objects. Each
572: * SearchResult contains the attributes identified by attributesToReturn
573: * and the name of the corresponding object, named relative to the
574: * context named by name.
575: * @exception NamingException if a naming exception is encountered
576: */
577: public NamingEnumeration search(String name,
578: Attributes matchingAttributes, String[] attributesToReturn)
579: throws NamingException {
580: throw new OperationNotSupportedException();
581: }
582:
583: /**
584: * Searches in a single context for objects that contain a specified set
585: * of attributes. This method returns all the attributes of such objects.
586: * It is equivalent to supplying null as the atributesToReturn parameter
587: * to the method search(Name, Attributes, String[]).
588: *
589: * @param name the name of the context to search
590: * @param matchingAttributes the attributes to search for. If empty or
591: * null, all objects in the target context are returned.
592: * @return a non-null enumeration of SearchResult objects. Each
593: * SearchResult contains the attributes identified by attributesToReturn
594: * and the name of the corresponding object, named relative to the
595: * context named by name.
596: * @exception NamingException if a naming exception is encountered
597: */
598: public NamingEnumeration search(String name,
599: Attributes matchingAttributes) throws NamingException {
600: throw new OperationNotSupportedException();
601: }
602:
603: /**
604: * Searches in the named context or object for entries that satisfy the
605: * given search filter. Performs the search as specified by the search
606: * controls.
607: *
608: * @param name the name of the context or object to search
609: * @param filter the filter expression to use for the search; may not be
610: * null
611: * @param cons the search controls that control the search. If null,
612: * the default search controls are used (equivalent to
613: * (new SearchControls())).
614: * @return an enumeration of SearchResults of the objects that satisfy
615: * the filter; never null
616: * @exception InvalidSearchFilterException if the search filter specified
617: * is not supported or understood by the underlying directory
618: * @exception InvalidSearchControlsException if the search controls
619: * contain invalid settings
620: * @exception NamingException if a naming exception is encountered
621: */
622: public NamingEnumeration search(String name, String filter,
623: SearchControls cons) throws NamingException {
624: throw new OperationNotSupportedException();
625: }
626:
627: /**
628: * Searches in the named context or object for entries that satisfy the
629: * given search filter. Performs the search as specified by the search
630: * controls.
631: *
632: * @param name the name of the context or object to search
633: * @param filterExpr the filter expression to use for the search.
634: * The expression may contain variables of the form "{i}" where i is a
635: * nonnegative integer. May not be null.
636: * @param filterArgs the array of arguments to substitute for the
637: * variables in filterExpr. The value of filterArgs[i] will replace each
638: * occurrence of "{i}". If null, equivalent to an empty array.
639: * @param cons the search controls that control the search. If null, the
640: * default search controls are used (equivalent to (new SearchControls())).
641: * @return an enumeration of SearchResults of the objects that satisy the
642: * filter; never null
643: * @exception ArrayIndexOutOfBoundsException if filterExpr contains {i}
644: * expressions where i is outside the bounds of the array filterArgs
645: * @exception InvalidSearchControlsException if cons contains invalid
646: * settings
647: * @exception InvalidSearchFilterException if filterExpr with filterArgs
648: * represents an invalid search filter
649: * @exception NamingException if a naming exception is encountered
650: */
651: public NamingEnumeration search(String name, String filterExpr,
652: Object[] filterArgs, SearchControls cons)
653: throws NamingException {
654: throw new OperationNotSupportedException();
655: }
656:
657: // ------------------------------------------------------ Protected Methods
658:
659: /**
660: * Normalize the name of an entry read from the Zip.
661: */
662: protected String normalize(ZipEntry entry) {
663:
664: String result = "/" + entry.getName();
665: if (entry.isDirectory()) {
666: result = result.substring(0, result.length() - 1);
667: }
668: return result;
669:
670: }
671:
672: /**
673: * Constructs a tree of the entries contained in a WAR file.
674: */
675: protected void loadEntries() {
676:
677: try {
678:
679: Enumeration entryList = base.entries();
680: entries = new Entry("/", new ZipEntry("/"));
681:
682: while (entryList.hasMoreElements()) {
683:
684: ZipEntry entry = (ZipEntry) entryList.nextElement();
685: String name = normalize(entry);
686: int pos = name.lastIndexOf('/');
687: // Check that parent entries exist and, if not, create them.
688: // This fixes a bug for war files that don't record separate
689: // zip entries for the directories.
690: int currentPos = -1;
691: int lastPos = 0;
692: while ((currentPos = name.indexOf('/', lastPos)) != -1) {
693: Name parentName = new CompositeName(name.substring(
694: 0, lastPos));
695: Name childName = new CompositeName(name.substring(
696: 0, currentPos));
697: String entryName = name.substring(lastPos,
698: currentPos);
699: // Parent should have been created in last cycle through
700: // this loop
701: Entry parent = treeLookup(parentName);
702: Entry child = treeLookup(childName);
703: if (child == null) {
704: // Create a new entry for missing entry and strip off
705: // the leading '/' character and appended on by the
706: // normalize method and add '/' character to end to
707: // signify that it is a directory entry
708: String zipName = name.substring(1, currentPos)
709: + "/";
710: child = new Entry(entryName, new ZipEntry(
711: zipName));
712: if (parent != null)
713: parent.addChild(child);
714: }
715: // Increment lastPos
716: lastPos = currentPos + 1;
717: }
718: String entryName = name.substring(pos + 1, name
719: .length());
720: Name compositeName = new CompositeName(name.substring(
721: 0, pos));
722: Entry parent = treeLookup(compositeName);
723: Entry child = new Entry(entryName, entry);
724: if (parent != null)
725: parent.addChild(child);
726:
727: }
728:
729: } catch (Exception e) {
730: }
731:
732: }
733:
734: /**
735: * Entry tree lookup.
736: */
737: protected Entry treeLookup(Name name) {
738: if (name.isEmpty())
739: return entries;
740: Entry currentEntry = entries;
741: for (int i = 0; i < name.size(); i++) {
742: if (name.get(i).length() == 0)
743: continue;
744: currentEntry = currentEntry.getChild(name.get(i));
745: if (currentEntry == null)
746: return null;
747: }
748: return currentEntry;
749: }
750:
751: /**
752: * List children as objects.
753: */
754: protected Vector list(Entry entry) {
755:
756: Vector entries = new Vector();
757: Entry[] children = entry.getChildren();
758: Arrays.sort(children);
759: NamingEntry namingEntry = null;
760:
761: for (int i = 0; i < children.length; i++) {
762: ZipEntry current = children[i].getEntry();
763: Object object = null;
764: if (current.isDirectory()) {
765: object = new WARDirContext(base, children[i]);
766: } else {
767: object = new WARResource(current);
768: }
769: namingEntry = new NamingEntry(children[i].getName(),
770: object, NamingEntry.ENTRY);
771: entries.addElement(namingEntry);
772: }
773:
774: return entries;
775:
776: }
777:
778: // ---------------------------------------------------- Entries Inner Class
779:
780: /**
781: * Entries structure.
782: */
783: protected class Entry implements Comparable {
784:
785: // -------------------------------------------------------- Constructor
786:
787: public Entry(String name, ZipEntry entry) {
788: this .name = name;
789: this .entry = entry;
790: }
791:
792: // --------------------------------------------------- Member Variables
793:
794: protected String name = null;
795:
796: protected ZipEntry entry = null;
797:
798: protected Entry children[] = new Entry[0];
799:
800: // ----------------------------------------------------- Public Methods
801:
802: public int compareTo(Object o) {
803: if (!(o instanceof Entry))
804: return (+1);
805: return (name.compareTo(((Entry) o).getName()));
806: }
807:
808: public ZipEntry getEntry() {
809: return entry;
810: }
811:
812: public String getName() {
813: return name;
814: }
815:
816: public void addChild(Entry entry) {
817: Entry[] newChildren = new Entry[children.length + 1];
818: for (int i = 0; i < children.length; i++)
819: newChildren[i] = children[i];
820: newChildren[children.length] = entry;
821: children = newChildren;
822: }
823:
824: public Entry[] getChildren() {
825: return children;
826: }
827:
828: public Entry getChild(String name) {
829: for (int i = 0; i < children.length; i++) {
830: if (children[i].name.equals(name)) {
831: return children[i];
832: }
833: }
834: return null;
835: }
836:
837: }
838:
839: // ------------------------------------------------ WARResource Inner Class
840:
841: /**
842: * This specialized resource implementation avoids opening the IputStream
843: * to the WAR right away.
844: */
845: protected class WARResource extends Resource {
846:
847: // -------------------------------------------------------- Constructor
848:
849: public WARResource(ZipEntry entry) {
850: this .entry = entry;
851: }
852:
853: // --------------------------------------------------- Member Variables
854:
855: protected ZipEntry entry;
856:
857: // ----------------------------------------------------- Public Methods
858:
859: /**
860: * Content accessor.
861: *
862: * @return InputStream
863: */
864: public InputStream streamContent() throws IOException {
865: try {
866: if (binaryContent == null) {
867: inputStream = base.getInputStream(entry);
868: }
869: } catch (ZipException e) {
870: throw new IOException(e.getMessage());
871: }
872: return super.streamContent();
873: }
874:
875: }
876:
877: }
|