KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > sape > carbon > services > config > jar > JarFolder


1 /*
2  * The contents of this file are subject to the Sapient Public License
3  * Version 1.0 (the "License"); you may not use this file except in compliance
4  * with the License. You may obtain a copy of the License at
5  * http://carbon.sf.net/License.html.
6  *
7  * Software distributed under the License is distributed on an "AS IS" basis,
8  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for
9  * the specific language governing rights and limitations under the License.
10  *
11  * The Original Code is The Carbon Component Framework.
12  *
13  * The Initial Developer of the Original Code is Sapient Corporation
14  *
15  * Copyright (C) 2003 Sapient Corporation. All Rights Reserved.
16  */

17
18 package org.sape.carbon.services.config.jar;
19
20 import java.io.File JavaDoc;
21 import java.io.IOException JavaDoc;
22 import java.util.ArrayList JavaDoc;
23 import java.util.Collection JavaDoc;
24 import java.util.HashSet JavaDoc;
25 import java.util.Iterator JavaDoc;
26 import java.util.Set JavaDoc;
27 import java.util.jar.JarEntry JavaDoc;
28
29 import org.sape.carbon.core.config.InvalidConfigurationException;
30 import org.sape.carbon.core.config.format.ConfigurationFormatException;
31 import org.sape.carbon.core.config.format.DefaultConfigurationFormatService;
32 import org.sape.carbon.core.config.node.AbstractFolder;
33 import org.sape.carbon.core.config.node.ConfigurationDocument;
34 import org.sape.carbon.core.config.node.Node;
35 import org.sape.carbon.core.config.node.NodeCreationException;
36 import org.sape.carbon.core.config.node.NodeFactory;
37 import org.sape.carbon.core.config.node.NodeIOException;
38 import org.sape.carbon.core.config.node.NodeNotFoundException;
39 import org.sape.carbon.core.config.node.NodeRemovalException;
40 import org.sape.carbon.core.config.node.file.FileNodeFilter;
41 import org.sape.carbon.core.config.node.link.LinkNodeConfiguration;
42 import org.sape.carbon.core.config.node.link.LinkNodeFactory;
43 import org.sape.carbon.core.exception.IllegalStateException;
44 import org.sape.carbon.core.exception.InvalidParameterException;
45 import org.sape.carbon.core.util.jar.EnhancedJarFile;
46 import org.sape.carbon.core.util.thread.ReadWriteLock;
47
48 import org.apache.commons.logging.Log;
49 import org.apache.commons.logging.LogFactory;
50
51 /**
52  * Implementation of folder node for folders residing in Jars
53  *
54  * Copyright 2002 Sapient
55  * @since carbon 1.0
56  * @author Douglas Voet, April 2002
57  * @version $Revision: 1.4 $($Author: dvoet $ / $Date: 2003/10/16 20:57:28 $)
58  */

59 public class JarFolder extends AbstractFolder {
60
61     /**
62      * Provides a handle to Apache-commons logger
63      */

64     private Log log = LogFactory.getLog(this.getClass());
65
66     /** reference to file containing the jar */
67     private File JavaDoc jarFile;
68
69     /** name of the entry of this folder */
70     private String JavaDoc jarEntryName;
71
72     /** reference to the currently openned jar */
73     private EnhancedJarFile currentOpenJar = null;
74     /**
75      * flag to keep track of how the jar was openned:
76      * true for read, false for write
77      */

78     private boolean jarOpenForRead;
79
80     /** reference to the jar's monitor */
81     private final ReadWriteLock readWriteLock;
82
83     /**
84      * The filter used to obtain valid child entries that may be represented
85      * by <code>JarNodes</code> from the jarFile.
86      */

87     private FileNodeFilter fileFilter = new FileNodeFilter();
88
89     /** string that delimits entry names in the jar */
90     public static final String JavaDoc JAR_DELIMETER = "/";
91
92     /** Collection of node names that will not be loaded. */
93     private static final Collection JavaDoc EXCLUDE_NODE_NAMES;
94
95     /** The java meta-inf directory. */
96     private static final String JavaDoc META_INF_DIR = "META-INF";
97
98     /** A CVS directory. */
99     private static final String JavaDoc CVS_DIR = "CVS";
100
101     static {
102         EXCLUDE_NODE_NAMES = new ArrayList JavaDoc();
103         EXCLUDE_NODE_NAMES.add(META_INF_DIR);
104         EXCLUDE_NODE_NAMES.add(CVS_DIR);
105     }
106
107     /**
108      * Constructor for JarFolder.
109      * @param parent the parent of this folder
110      * @param name the name of this folder
111      * @param subFolderFactory the factory for creating sub folders
112      * @param configurationDocumentFactory the factory for creating documents
113      * @param linkNodeFactory the factory for creating sub links
114      * @param readWriteLock the monitor used to coordinate reads and writes
115      * to the jar
116      * @param jarFile the file that is the jar
117      * @param jarEntryName the entry name within the jar that this node
118      * represents
119      */

120     public JarFolder(
121         Node parent,
122         String JavaDoc name,
123         NodeFactory subFolderFactory,
124         NodeFactory configurationDocumentFactory,
125         NodeFactory linkNodeFactory,
126         ReadWriteLock readWriteLock,
127         File JavaDoc jarFile,
128         String JavaDoc jarEntryName) {
129
130         super(
131             parent,
132             name,
133             subFolderFactory,
134             configurationDocumentFactory,
135             linkNodeFactory);
136
137         // validate parameters and set member attributes
138
if (jarFile != null) {
139             this.jarFile = jarFile;
140         } else {
141             throw new InvalidParameterException(
142                 this.getClass(),
143                 "The ["
144                     + getAbsoluteName()
145                     + "] JarFolder node jarFile reference cannot be null");
146         }
147
148         if (!jarFile.exists() || jarFile.isDirectory()) {
149             throw new InvalidParameterException(
150                 this.getClass(),
151                 "jarFile does not exist or is a directory");
152         }
153
154         if (readWriteLock != null) {
155             this.readWriteLock = readWriteLock;
156         } else {
157             throw new InvalidParameterException(
158                 this.getClass(),
159                 "The ["
160                     + getAbsoluteName()
161                     + "] JarFolder node readWriteLock reference "
162                     + "cannot be null");
163         }
164
165         this.jarEntryName = (jarEntryName == null ? "" : jarEntryName);
166     }
167
168     /**
169      * This is not synchronized because it is called from
170      * AbstractFolder.fetchChild which is synchronized
171      *
172      * @see AbstractFolder#loadChild(String)
173      */

174     protected Node loadChild(String JavaDoc nodeName)
175         throws NodeNotFoundException {
176
177         EnhancedJarFile jar = null;
178         try {
179             jar = openJar(true);
180             String JavaDoc baseName = this.jarEntryName + nodeName;
181
182             Node child = null;
183
184             // skip excluded node names
185
if (!JarFolder.EXCLUDE_NODE_NAMES.contains(
186                     nodeName.toUpperCase())) {
187
188
189                 JarEntry JavaDoc childEntry = (JarEntry JavaDoc)
190                     jar.getEntry(baseName + EnhancedJarFile.JAR_DELIMETER);
191                 // if the child exists in the jar and it is a directory,
192
// it is a folder node
193
if (childEntry != null && childEntry.isDirectory()) {
194                     child = loadSubFolder(nodeName);
195                 }
196
197                 childEntry =
198                     (JarEntry JavaDoc) jar.getEntry(
199                         baseName + this.fileFilter.CONFIG_DOC_NODE_EXTENSION);
200                 // if the child exists in the jar, has the config doc ext
201
// and it is not a directory, it is a config doc node
202
if (childEntry != null && !childEntry.isDirectory()) {
203                     child = loadConfigurationDocument(nodeName);
204                 }
205
206                 childEntry =
207                     (JarEntry JavaDoc) jar.getEntry(
208                         baseName + this.fileFilter.LINK_NODE_EXTENSION);
209                 // if the child exists in the jar, has the link ext
210
// and it is not a directory, it is a link node
211
if (childEntry != null && !childEntry.isDirectory()) {
212                     // the child is a link
213
child = loadChildLinkNode(nodeName);
214                 }
215             }
216
217             if (child == null) {
218                 throw new NodeNotFoundException(
219                     this.getClass(),
220                     this.getAbsoluteName() + Node.DELIMITER + nodeName);
221             }
222
223             return child;
224
225         } catch (NodeCreationException nce) {
226             throw new NodeNotFoundException(
227                 this.getClass(),
228                 nodeName,
229                 nce);
230
231         } catch (IOException JavaDoc ioe) {
232             throw new InvalidParameterException(
233                 this.getClass(),
234                 "Caught IOException accessing jar file: ["
235                     + this.jarFile.getAbsolutePath()
236                     + "]",
237                 ioe);
238
239         } finally {
240             // close jar
241
try {
242                 if (jar != null) {
243                     closeJar();
244                 }
245             } catch (IOException JavaDoc ioe) {
246                 // eat it, just wanted to close stream
247
}
248         }
249     }
250
251     /**
252      * This is sychronized because it is called from
253      * AbstractFolder.fetchAllChildren which is not synchronized, but the
254      * design of the class requires that openJar is not called multiple times
255      * without intervening calls to close jar
256      *
257      * @see AbstractFolder#getAllChildNames()
258      */

259     protected Set JavaDoc getAllChildNames() {
260         synchronized (getReadOrAlterNodeLock()) {
261             EnhancedJarFile jar = null;
262             try {
263                 jar = openJar(true);
264                 Iterator JavaDoc entries =
265                     jar.listSubEntries(this.jarEntryName).iterator();
266     
267                 Set JavaDoc childNames = new HashSet JavaDoc();
268     
269                 while (entries.hasNext()) {
270                     JarEntry JavaDoc nextEntry = (JarEntry JavaDoc) entries.next();
271     
272                     String JavaDoc childName = null;
273                     String JavaDoc entryName = nextEntry.getName();
274     
275                     if (nextEntry.isDirectory()) {
276                         childName =
277                             entryName.substring(
278                                 this.jarEntryName.length(),
279                                 entryName.lastIndexOf(
280                                     EnhancedJarFile.JAR_DELIMETER));
281     
282                     } else if (entryName.endsWith(
283                         this.fileFilter.CONFIG_DOC_NODE_EXTENSION)) {
284     
285                         childName =
286                             entryName.substring(
287                                 this.jarEntryName.length(),
288                                 entryName.lastIndexOf(
289                                     this.fileFilter.CONFIG_DOC_NODE_EXTENSION));
290     
291                     } else if (entryName.endsWith(
292                         this.fileFilter.LINK_NODE_EXTENSION)) {
293     
294                         childName =
295                             entryName.substring(
296                                 this.jarEntryName.length(),
297                                 entryName.lastIndexOf(
298                                     this.fileFilter.LINK_NODE_EXTENSION));
299                     }
300     
301                     if (childName != null
302                         && !JarFolder.EXCLUDE_NODE_NAMES.contains(
303                             childName.toUpperCase())) {
304     
305                         childNames.add(childName);
306                     }
307                 }
308     
309                 return childNames;
310     
311             } catch (IOException JavaDoc ioe) {
312                 throw new IllegalStateException JavaDoc(
313                     this.getClass(),
314                     "Could not open jar file",
315                     ioe);
316     
317             } finally {
318                 // close jar
319
try {
320                     if (jar != null) {
321                         closeJar();
322                     }
323                 } catch (IOException JavaDoc ioe) {
324                     // eat it, just wanted to close stream
325
}
326             }
327         }
328     }
329
330     /**
331      * This implementation does nothing as
332      * jars have no concept of folders, so there is nothing to remove
333      *
334      * @see org.sape.carbon.core.config.node.AbstractNode#destroyBackingData()
335      */

336     protected void destroyBackingData() throws NodeRemovalException {
337     }
338
339     /**
340      * @see org.sape.carbon.core.config.node.AbstractFolder#backingDataExists()
341      */

342     protected boolean backingDataExists() {
343         return (this.jarFile.exists() && !this.jarFile.isDirectory());
344     }
345
346     /**
347      * Returns the file object pointing to the jar file. Called by
348      * factories to propagate the file object to child nodes
349      *
350      * @return File
351      */

352     File JavaDoc getInternalJarFile() {
353         return this.jarFile;
354     }
355
356     /**
357      * Returns the entry name of this node. Called by
358      * factories to preprend to entry names of child nodes
359      *
360      * @return String
361      */

362     String JavaDoc getInternalJarEntryName() {
363         return this.jarEntryName;
364     }
365
366     /**
367      * Returns the monitor for this jar. Called by
368      * factories to propagate the monitor to child nodes so that all
369      * nodes within this structure use the same monitory
370      *
371      * @return a read write lock used to synchronize access to the jar
372      * containing this folder.
373      */

374     ReadWriteLock getReadWriteLock() {
375         return this.readWriteLock;
376     }
377
378     /**
379      * Helper method used to create sub folders
380      *
381      * @param nodeName the name of the sub-node
382      * @return Node the sub-node
383      * @throws NodeCreationException indicates an error loading a subfolder
384      */

385     private Node loadSubFolder(String JavaDoc nodeName)
386         throws NodeCreationException {
387         return getSubFolderFactory().getInstance(this, nodeName);
388     }
389
390     /**
391      * Helper method used to create sub config docs
392      *
393      * @param nodeName name of the sub-config to load
394      * @return Node the loaded configuration document
395      * @throws NodeCreationException indicates an error loading the child
396      * node
397      */

398     private Node loadConfigurationDocument(String JavaDoc nodeName)
399         throws NodeCreationException {
400
401         return getConfigurationDocumentFactory().getInstance(this, nodeName);
402     }
403
404     /**
405      * Helper method used to create sub links
406      *
407      * @param nodeName name of the child link node
408      * @return Node the child link node specified by the given name
409      * @throws NodeCreationException indicates an error loading the
410      * child link node
411      */

412     private Node loadChildLinkNode(String JavaDoc nodeName)
413         throws NodeCreationException {
414
415         try {
416
417             ConfigurationDocument linkConfiguraitonDoc =
418                 new JarConfigurationDocument(
419                     this,
420                     nodeName,
421                     new DefaultConfigurationFormatService(),
422                     this.readWriteLock,
423                     this.jarFile,
424                     this.jarEntryName
425                         + nodeName
426                         + this.fileFilter.LINK_NODE_EXTENSION);
427
428             LinkNodeConfiguration config =
429                 (LinkNodeConfiguration)
430                     linkConfiguraitonDoc.readConfiguration();
431
432             if (config.getLinkNodeFactoryClass() == null) {
433                 throw new InvalidConfigurationException(
434                     this.getClass(),
435                     config.getConfigurationName(),
436                     "LinkNodeFactoryClass",
437                     "Cannot be null");
438             }
439
440             LinkNodeFactory factory =
441                 (LinkNodeFactory)
442                     config.getLinkNodeFactoryClass().newInstance();
443
444             return factory.getInstance(this, nodeName, linkConfiguraitonDoc);
445
446         } catch (ConfigurationFormatException e) {
447             throw new NodeCreationException(
448                 this.getClass(),
449                 this,
450                 nodeName,
451                 "exception occured",
452                 e);
453         } catch (NodeIOException e) {
454             throw new NodeCreationException(
455                 this.getClass(),
456                 this,
457                 nodeName,
458                 "exception occured",
459                 e);
460         } catch (InstantiationException JavaDoc e) {
461             throw new NodeCreationException(
462                 this.getClass(),
463                 this,
464                 nodeName,
465                 "exception occured",
466                 e);
467         } catch (IllegalAccessException JavaDoc e) {
468             throw new NodeCreationException(
469                 this.getClass(),
470                 this,
471                 nodeName,
472                 "exception occured",
473                 e);
474         }
475     }
476
477     /**
478      * Opens the jar file. Based on the boolean value of forRead, a
479      * read or a write lock will be acquired
480      *
481      * @param forRead if true a read lock will be acquired, if false,
482      * a write lock will be acquired
483      * @return the opened jar file
484      * @throws IOException indicates an error manipulating the jar file
485      */

486     private EnhancedJarFile openJar(boolean forRead) throws IOException JavaDoc {
487
488         if (this.currentOpenJar != null) {
489             // if the jar is already open, don't open a new one, but
490
// log it as a warning since we should not maintain open jars
491
if (log.isWarnEnabled()) {
492                 log.warn("Jar file [" + currentOpenJar.getName()
493                        + "] has not been closed");
494             }
495
496         } else {
497             // get the monitor for reading or writing
498
try {
499                 if (forRead) {
500                     getReadWriteLock().readLock().acquire();
501                     this.jarOpenForRead = true;
502                 } else {
503                     getReadWriteLock().writeLock().acquire();
504                     this.jarOpenForRead = false;
505                 }
506             } catch (InterruptedException JavaDoc ie) {
507                 Thread.currentThread().interrupt();
508                 throw new IllegalStateException JavaDoc(
509                     this.getClass(),
510                     "Caught interupt exception before acquiring jar monitor",
511                     ie);
512             }
513             // open the jar
514
this.currentOpenJar = new EnhancedJarFile(this.jarFile);
515         }
516
517         return this.currentOpenJar;
518     }
519
520     /**
521      * Closes the jar file if it is open, releasing the lock based upon how
522      * it was opened
523      *
524      * @throws IOException indicates an error manipulating the jar file
525      */

526     private void closeJar() throws IOException JavaDoc {
527         if (this.currentOpenJar != null) {
528
529             // NG (Bug 371) Make sured that the read/write lock is released
530
// under any circumstances.
531
try {
532                 this.currentOpenJar.close();
533             } catch (IOException JavaDoc ioe) {
534                 throw ioe;
535             } finally {
536                 this.currentOpenJar = null;
537
538                 if (this.jarOpenForRead) {
539                     getReadWriteLock().readLock().release();
540                 } else {
541                     getReadWriteLock().writeLock().release();
542                 }
543             }
544         }
545     }
546 }
Popular Tags