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