KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > lutris > classloader > ClassPathEntry


1
2 /*
3  * Enhydra Java Application Server Project
4  *
5  * The contents of this file are subject to the Enhydra Public License
6  * Version 1.1 (the "License"); you may not use this file except in
7  * compliance with the License. You may obtain a copy of the License on
8  * the Enhydra web site ( http://www.enhydra.org/ ).
9  *
10  * Software distributed under the License is distributed on an "AS IS"
11  * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
12  * the License for the specific terms governing rights and limitations
13  * under the License.
14  *
15  * The Initial Developer of the Enhydra Application Server is Lutris
16  * Technologies, Inc. The Enhydra Application Server and portions created
17  * by Lutris Technologies, Inc. are Copyright Lutris Technologies, Inc.
18  * All Rights Reserved.
19  *
20  * Contributor(s):
21  *
22  * $Id: ClassPathEntry.java,v 1.2 2005/03/24 10:51:25 slobodan Exp $
23  */

24
25
26
27
28
29 package com.lutris.classloader;
30
31 // lutris packages
32
// v. strahinja, 24 sep 2002
33
import java.io.File JavaDoc;
34 import java.io.FileNotFoundException JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.net.MalformedURLException JavaDoc;
37 import java.net.URL JavaDoc;
38 import java.util.zip.ZipFile JavaDoc;
39
40 import com.lutris.logging.LogChannel;
41
42 /**
43  * <P><H3>Summary:</H3>
44  * <P>A class path entry which is a URL representing either a local or a
45  * remote directory or zip file. Manages all the details for zip file support.
46  * For example, local zip files (once opened) are kept open for the lifetime
47  * of the entry for increased performance.
48  *
49  * <P><H3>Features:</H3>
50  * <UL>
51  * <LI>Supports absolute and relative file names
52  * <LI>Creates URL entry from given String, File, or URL
53  * <LI>Adds "/" to end of URL for directories if necessary
54  * <LI>Knows if URL is zip local or remote
55  * <LI>Knows if URL is a zip file or directory
56  * <LI>Keeps ZipFile open if the zip file is local
57  * </UL>
58  *
59  * <P><B>Note: "zip files" are files with ".zip" or ".jar" extensions.</B>
60  *
61  * <P><H3>Class Path Entries:</H3>
62  * <P>Example valid class path entries are:
63  * <PRE>
64  * <EM>Files and directories on the local file system</EM>
65  * ../../java/classes
66  * /users/kristen/java/classes
67  * /users/kristen/java/classes/
68  * /users/kristen/java/zipfiles/MyClasses.zip
69  * /users/kristen/java/jarfiles/MyClasses.jar
70  * file:///users/kristen/java/classes
71  * file://localhost/users/kristen/java/classes
72  * <BR>
73  * <EM>Files and directories on a remote file system
74  * (must be in URL format)</EM>
75  * ftp://www.foo.com/pub/java/classes
76  * file://www.foo.com/pub/java/classes/
77  * http://www.foo.com/web/java/classes/
78  * file://www.foo.com:8080/pub/java/zipfiles/MyClasses.zip
79  * http://www.foo.com:8080/web/java/jarfiles/MyClasses.jar
80  * </PRE>
81  *
82  * <P>Note that the location of the entry includes the protocol, the host name,
83  * and the port while the file name is everything else. For example,
84  *
85  * <PRE>
86  * http://www.foo.com:8080/web/java/jarfiles/MyClasses.jar
87  * </PRE>
88  * has the form [location][name] or
89  * <PRE>
90  * [http://www.foo.com:8080/][/web/java/jarfiles/MyClasses.jar]
91  * </PRE>
92  * so the location is "http://www.foo.com:8080/" and the name is
93  * "/web/java/jarfiles/MyClasses.jar".
94  *
95  * <P>Note that the two references
96  * <PRE>
97  * /users/kristen/java/classes/
98  * file:///users/kristen/java/classes/
99  * </PRE>
100  * represent the same directory on a Unix machine, and
101  * <PRE>
102  * C|/windows/java/classes/
103  * file:///C|/windows/java/classes/
104  * </PRE>
105  * are equivalent directories on a Windows box.
106  *
107  * <P>But the two references
108  * <PRE>
109  * /users/kristen/java/classes/
110  * file://monet.lutris.com/users/kristen/java/classes/
111  * </PRE>
112  * are not equivalent even if the directory
113  * <EM>/users/kristen/java/classes/</EM> lives
114  * on the machine named <EM>monet.lutris.com</EM> and all development
115  * is on this machine. Why? Because the web (browser?) protocol is different
116  * for URLs with host information and those without. If no host is
117  * specified, the file is assumed to be on the local machine and the
118  * path is determined from the <EM>ROOT</EM> of the machine. If the
119  * host is specified, then the <EM>ftp protocol</EM> is used and the path
120  * is determined from the <EM>ftp ROOT</EM> (e.g. /users/ftp/) rather
121  * than the machine's ROOT. Thus, on a machine that support's anonymous
122  * ftp, the following two directories are the same:
123  * <PRE>
124  * /users/ftp/pub/classes/
125  * file://picasso.lutris.com/pub/classes/
126  * </PRE>
127  * assuming the development is being done on <EM>picasso.lutris.com</EM>.
128  *
129  * <H3>System Class Path Entries</H3>
130  *
131  * <P>The system class path is the system-dependent path of directories
132  * and files (e.g. CLASSPATH on Unix and Windows) used by the system
133  * class loader to load classes. Valid system class path entries are
134  * directories and zip files, specified by absolute path or relative path
135  * on the system. Any valid system class path entry is also valid to
136  * create a <CODE>ClassPathEntry</CODE>.
137  *
138  * <H3>Example</H3>
139  *
140  * <P>Here is an example of how to use a ClassPathEntry:
141  * <PRE>
142  * ClassPathEntry entry = new ClassPathEntry("/home/java/Test.jar");
143  * System.out.println("My entry URL is " + entry.get());
144  * System.out.println("My entry string is " + entry.toString());
145  * System.out.println("My entry name " + entry.getName());
146  * System.out.println("My entry location " + entry.getLocation());
147  * System.out.println("My entry is a zip file? " + entry.isZipFile());
148  * Resource resource = entry.getResource("foo.gif");
149  * </PRE>
150  *
151  * @author Kristen Pol, Lutris Technologies
152  * @version $Revision : 1.0 $
153  * @see com.lutris.classloader.Resource
154  * @see java.net.URL
155  */

156 public class ClassPathEntry {
157     /*
158      * This could be made into an abstract class and be subclassed by
159      * LocalDirEntry, LocalZipEntry, RemoteDirEntry, and RemoteZipEntry.
160      */

161
162     // private data members
163

164     /** The class path entry represented by a URL. */
165     private URL JavaDoc entryURL = null;
166
167     /** The ZipFile for this class path entry, if appropriate. */
168     private ZipFile JavaDoc zipFile = null;
169
170     /** Is logging enabled? */
171     private boolean loggingEnabled = false;
172
173     /** Log channel to write messages to */
174 // v. strahinja, 24 sep 2002
175
private LogChannel logChannel;
176 // private Logger logger;
177

178     /** Numeric log level number for LOGLEVEL string */
179 // v. strahinja, 24 sep 2002
180
private int logLevel;
181 // private Level logLevel;
182

183     /** Cache this information as it is accessed quite a bit */
184     private boolean isZipFile;
185
186     // constructors
187

188     /**
189      * Constructs class path entry with specified String.
190      * The String is assumed to be either a directory or zip file on
191      * the local machine or a remote machine, which can be represented by the
192      * absolute file name, the relative file name, or a URL.
193      *
194      * <P>Examples:
195      * <PRE>
196      * ClassPathEntry("../../java/classes");
197      * ClassPathEntry("/users/kristen/java/classes");
198      * ClassPathEntry("/users/kristen/java/classes/");
199      * ClassPathEntry("/users/kristen/java/MyClasses.zip");
200      * ClassPathEntry("/users/kristen/java/MyClasses.jar");
201      * ClassPathEntry("ftp://www.foo.com/pub/classes/");
202      * ClassPathEntry("file://www.foo.com/web/classes/");
203      * ClassPathEntry("http://www.foo.com/web/classes/");
204      * ClassPathEntry("http://www.foo.com:8080/web/classes/");
205      * ClassPathEntry("http://www.foo.com/web/classes/MyClasses.jar");
206      * </PRE>
207      *
208      * @param entry The class path entry.
209      * @param loadLogChannel The log channel.
210      */

211 // v. strahinja, 24 sep 2002
212
public ClassPathEntry(String JavaDoc entry, LogChannel loadLogChannel) {
213 // v. strahinja, 24 sep 2002
214
this(convertEntryToURL(entry), loadLogChannel);
215 // public ClassPathEntry(String entry, Logger loadLogger) {
216
// this(convertEntryToURL(entry), loadLogger);
217
}
218
219     /**
220      * Constructs class path entry with specified File.
221      * The File can represent a directory or zip file on the local machine.
222      *
223      * <P>Examples:
224      * <PRE>
225      * ClassPathEntry(new File("../../java/classes"));
226      * ClassPathEntry(new File("/users/kristen/java/classes"));
227      * ClassPathEntry(new File("/users/kristen/java/classes/"));
228      * ClassPathEntry(new File("/users/kristen/java/MyClasses.zip"));
229      * ClassPathEntry(new File("/users/kristen/java/MyClasses.jar"));
230      * </PRE>
231      *
232      * @param entry The class path entry.
233      * @param loadLogChannel The log channel.
234      */

235 // v. strahinja, 24 sep 2002
236
public ClassPathEntry(File JavaDoc entry, LogChannel loadLogChannel) {
237 // v. strahinja, 24 sep 2002
238
this(convertEntryToURL(entry), loadLogChannel);
239 // public ClassPathEntry(File entry, Logger loadLogger) {
240
// this(convertEntryToURL(entry), loadLogger);
241
}
242
243     /**
244      * Constructs class path entry with specified URL.
245      * The URL can represent a directory or zip file on the local machine
246      * or a remote machine.
247      *
248      * <P>Examples:
249      * <PRE>
250      * ClassPathEntry(new URL("ftp://www.foo.com/pub/classes/"));
251      * ClassPathEntry(new URL("file://www.foo.com/web/classes/"));
252      * ClassPathEntry(new URL("http://www.foo.com/web/classes/"));
253      * ClassPathEntry(new URL("http://www.foo.com:8080/web/classes/"));
254      * ClassPathEntry(new URL("http://www.foo.com/web/MyClasses.jar"));
255      * </PRE>
256      *
257      * @param entry The class path entry.
258      * @param loadLogChannel The log channel.
259      */

260 // v. strahinja, 24 sep 2002
261
public ClassPathEntry(URL JavaDoc entry, LogChannel loadLogChannel) {
262 // public ClassPathEntry(URL entry, Logger loadLogger) {
263
/*
264      * This constructor actually does all the work, because all
265      * other constructors call this one.
266      */

267      try {
268 // v. strahinja, 24 sep 2002
269
this.logChannel = loadLogChannel;
270 // v. strahinja, 24 sep 2002
271
if (logChannel != null) {
272 // v. strahinja, 24 sep 2002
273
this.logLevel = logChannel.getLevel(MultiClassLoader.LOG_LEVEL);
274 // v. strahinja, 24 sep 2002
275
loggingEnabled = logChannel.isEnabled(logLevel);
276 // this.logger = loadLogger;
277
// if (logger != null) {
278
//sinisa 24.08.2003 logLevel = logger.getLevel();
279
// logLevel = Level.DEBUG;
280
// if (logLevel == null) {
281
// logLevel = Level.DEBUG;
282
// }
283
// loggingEnabled = logger.isEnabledFor(logLevel);
284
}
285          this.entryURL = new URL JavaDoc(cleanUpURL(entry).toString());
286      } catch (MalformedURLException JavaDoc mue) {
287              if (loggingEnabled) {
288 // v. strahinja, 24 sep 2002
289
logChannel.write(logLevel, "Illegal class path entry: " + entry);
290 // logger.log(logLevel, "Illegal class path entry: " + entry);
291
}
292          this.entryURL = null;
293      }
294          initIsZipFile();
295     }
296
297     // public methods
298

299     /**
300      * Gets class path entry set previously by constructor.
301      *
302      * @return the class path entry represented by a URL.
303      */

304     public URL JavaDoc getURL() {
305     return entryURL;
306     }
307
308     /**
309      * Gets file name of class path entry. For example, if the entry is
310      * "http://www.foo.com:8080/java/classes/MyClasses.jar", the name is
311      * "/java/classes/MyClasses.jar". The beginning slash does not mean
312      * that its an absolute file name on its host machine. The file name
313      * is always <EM>relative</EM> to the location.
314      *
315      * @return the file name of the class path entry.
316      */

317     public String JavaDoc getName() {
318     // XXX: Don't use a beginning slash? (kp)
319
return entryURL.getFile();
320     }
321
322     /**
323      * Gets location of class path entry. For example, if the entry is
324      * "http://www.foo.com:8080/java/classes/MyClasses.jar", the location is
325      * "http://www.foo.com:8080/".
326      *
327      * @return the file name for the class path entry.
328      */

329     public String JavaDoc getLocation() {
330     if (entryURL.getPort() != -1) {
331         return entryURL.getProtocol() + "://" + entryURL.getHost() + ":" +
332            entryURL.getPort() + "/";
333     } else {
334         return entryURL.getProtocol() + "://" + entryURL.getHost() + "/";
335     }
336     }
337
338     /**
339      * Stringifies class path entry set previously by constructor.
340      *
341      * @return the class path entry represented by a stringified URL.
342      */

343     public String JavaDoc toString() {
344     if (entryURL != null) {
345         return entryURL.toString();
346     } else {
347         return "null";
348     }
349     }
350
351     /**
352      * Gets resource with the specified name from class path entry.
353      * The result will be null if the resource can not be found.
354      *
355      * @param name The file name associated with the resource.
356      * @return the resource from the class path entry, or null if it is
357      * not found.
358      * @see Resource
359      */

360     public Resource getResource(String JavaDoc name) {
361     name = convertSlashes(name);
362         // FIXME: This is rather inefficient, as it creates a
363
// resource and throws an exception just to check a path
364
// entry. Probably need to have a ClassPathEntry per resource
365
// type .
366
Resource resource = null;
367     try {
368         if (isDirectory() && isLocal()) {
369 // v. strahinja, 24 sep 2002
370
resource = new LocalDirResource(name, this, logChannel);
371 // resource = new LocalDirResource(name, this, logger);
372
} else if (isZipFile() && isLocal()) {
373 // v. strahinja, 24 sep 2002
374
resource = new LocalZipResource(name, this, logChannel);
375 // resource = new LocalZipResource(name, this, logger);
376
} else if (isDirectory() && isRemote()) {
377         // FIXME: Test RemoteDirResource, then add (kp)
378
// resource = new RemoteDirResource(name, this, logChannel);
379
if (loggingEnabled) {
380 // v. strahinja, 24 sep 2002
381
logChannel.write(logLevel, "Cannot get remote directory resource.");
382 // logger.log(logLevel, "Cannot get remote directory resource.");
383
}
384         resource = null;
385         } else if (isZipFile() && isRemote()) {
386         // FIXME: Test RemoteZipResource, then add (kp)
387
// resource = new RemoteZipResource(name, this, logChannel);
388
if (loggingEnabled) {
389 // v. strahinja, 24 sep 2002
390
logChannel.write(logLevel, "Cannot get remote jar file resource.");
391 // logger.log(logLevel, "Cannot get remote jar file resource.");
392
}
393         resource = null;
394         }
395     } catch (FileNotFoundException JavaDoc e) {
396             if (loggingEnabled) {
397                 // Don't log stack trace, as this error is a `normal'
398
// when searching a path.
399
// v. strahinja, 24 sep 2002
400
logChannel.write(logLevel, "File not found: " + name
401 // logger.log(logLevel, "File not found: " + name
402
+ ": " + e.getClass().getName() + ": "
403                             + e.getMessage());
404             }
405         resource = null;
406     }
407     return resource;
408     }
409
410     /**
411      * Determines if class path entry is a zip file. Anything ending
412      * in ".zip" or ".jar" is considered a zip file.
413      *
414      * @return true if the class path entry is a zip file, false if it is not.
415      */

416     public boolean isZipFile() {
417         return isZipFile;
418     //return (toString().endsWith(".jar") || toString().endsWith(".zip"));
419
}
420
421     /**
422      * Called at init time or when ever the entryURL can change.
423      */

424     private void initIsZipFile() {
425         isZipFile = (toString().endsWith(".jar") || toString().endsWith(".zip"));
426     }
427
428     /**
429      * Determines if class path entry is a directory. Anything that is
430      * not a zip file is considered a directory.
431      *
432      * @return true if the class path entry is a directory, false if it is not.
433      * @see #isZipFile
434      */

435     public boolean isDirectory() {
436     return (!isZipFile());
437     }
438
439     /**
440      * Determines if class path entry is local. Anything that has
441      * no host name or a host name of "localhost" is considered local.
442      *
443      * @return true if the class path entry is remote, false if it is not.
444      */

445     public boolean isLocal() {
446     String JavaDoc host = entryURL.getHost();
447     if (host.equals("") || host.equalsIgnoreCase("localhost") ||
448         host.equals("127.0.0.1")) {
449         return true;
450     }
451     return false;
452     }
453
454     /**
455      * Determines if class path entry is remote. Anything that is not
456      * local is considered remote.
457      *
458      * @return true if the class path entry is remote, false if it is not.
459      * @see #isLocal
460      */

461     public boolean isRemote() {
462     return (!isLocal());
463     }
464
465     /**
466      * Determines if specified class path entry is equal to this entry.
467      * The entries are considered equal if the URLs are the same.
468      *
469      * @param cpe The class path entry to check for equality.
470      * @return true if the class path entry is equal, false if it is not.
471      */

472     public boolean equals(Object JavaDoc o) {
473     if (o instanceof ClassPathEntry) {
474         return entryURL.equals(((ClassPathEntry)o).getURL());
475     }
476     return false;
477     }
478
479     /**
480      * Gets zip file associated with class path entry if appropriate. If
481      * a zip file is not found, null will be returned.
482      *
483      * @return the zip file associated with this class path entry if
484      * found, null if not.
485      */

486     public ZipFile JavaDoc getZipFile() {
487     if (zipFile == null) {
488         return doGetZipFile();
489     }
490     return zipFile;
491     }
492
493     /**
494      * Gets zip file associated with class path entry if appropriate. If
495      * a zip file is not found, null will be returned.
496      *
497      * @return the zip file associated with this class path entry if
498      * found, null if not.
499      */

500     private synchronized ZipFile JavaDoc doGetZipFile() {
501     if (zipFile == null) {
502         if (isZipFile() && isLocal()) {
503         try {
504             zipFile = new ZipFile JavaDoc(getName());
505         } catch (IOException JavaDoc e) {
506                     if (loggingEnabled) {
507 // v. strahinja, 24 sep 2002
508
logChannel.write(logLevel, "Cannot create zip file " + getName() + ".", e);
509 // logger.log(logLevel,
510
// "Cannot create zip file " + getName() + ".", e);
511
}
512             zipFile = null;
513         }
514         } else {
515         zipFile = null;
516         }
517     }
518     return zipFile;
519     }
520     // private helper methods
521

522     /**
523      * Cleans up URL by fixing slashes. All back slashes are converted to
524      * forward slashes ("/"). A slash is added to the end of the URL if
525      * it is a directory and does not already have an ending slash.
526      *
527      * @param url The URL to clean up.
528      * @return a cleaned up version of the URL.
529      */

530     private static URL JavaDoc cleanUpURL(URL JavaDoc url) {
531     URL JavaDoc newURL = null;
532     try {
533             //FIXME: This code converts `.' to `/'.
534
String JavaDoc urlString = url.toString();
535         if (!urlString.endsWith("/") && !urlString.endsWith(".jar") &&
536         !urlString.endsWith(".zip")) {
537         newURL = new URL JavaDoc(convertSlashes(url.toString() + "/"));
538         } else {
539         newURL = new URL JavaDoc(convertSlashes(url.toString()));
540         }
541     } catch (MalformedURLException JavaDoc e) {
542         // FIXME: Log. Cannot use logChannel because it's not static.
543
// FIXME: should just throw exception..
544
newURL = null;
545     }
546     return newURL;
547     }
548
549     /**
550      * Converts Object to URL. Only URLs, Files, and Strings
551      * can be successfully converted. All other object types will
552      * result in a null return.
553      *
554      * @param object The object to convert.
555      * @return the URL representing the object, or null if the conversion
556      * was not successful.
557      */

558     private static URL JavaDoc convertEntryToURL(Object JavaDoc object) {
559     if (object instanceof java.net.URL JavaDoc) {
560         return (URL JavaDoc)object;
561     } else if (object instanceof java.lang.String JavaDoc) {
562         return convertEntryToURL((String JavaDoc)object);
563     } else if (object instanceof java.io.File JavaDoc) {
564         return convertEntryToURL(object.toString());
565     } else {
566         // FIXME: Log. Cannot use logChannel because it's not static.
567
// FIXME: should just throw exception.
568
return null;
569     }
570     }
571
572     /**
573      * Converts String to URL.
574      *
575      * @param string The string to convert.
576      * @return the URL representation of the given string.
577      */

578     private static URL JavaDoc convertEntryToURL(String JavaDoc string) {
579     try {
580         return new URL JavaDoc(string);
581     } catch (MalformedURLException JavaDoc e) {
582         try {
583                 String JavaDoc absPath = (new File JavaDoc(string)).getAbsolutePath();
584                 // Convert directory separator to "/", since only "/" is a
585
// legal separator in URLs
586
absPath = absPath.replace(File.separatorChar, '/');
587                 // If an absolute path doesn't start with a '/', assume it's
588
// windows and add it.
589
if (!absPath.startsWith("/")) {
590                     absPath = "/" + absPath;
591         }
592                 return new URL JavaDoc("file://" + absPath);
593         } catch (MalformedURLException JavaDoc e2) {
594         // FIXME: Log. Cannot use logChannel because it's not static.
595
return null;
596         }
597     }
598     }
599
600     /**
601      * Converts all system path separators to "/". This is to accommodate
602      * both Windows and Unix for URL generation and searching zip files.
603      *
604      * @param string The string to convert.
605      * @return the string with all slashes converted.
606      */

607     private static String JavaDoc convertSlashes(String JavaDoc string) {
608     string.replace(File.separatorChar, '/');
609     return string.replace('\\', '/');
610     }
611 }
612
Popular Tags