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