KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > edu > umd > cs > findbugs > ba > URLClassPath


1 /*
2  * FindBugs - Find bugs in Java programs
3  * Copyright (C) 2004, University of Maryland
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18  */

19
20 package edu.umd.cs.findbugs.ba;
21
22 import java.io.BufferedInputStream JavaDoc;
23 import java.io.File JavaDoc;
24 import java.io.FileInputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.Serializable JavaDoc;
28 import java.net.URL JavaDoc;
29 import java.util.HashSet JavaDoc;
30 import java.util.LinkedList JavaDoc;
31 import java.util.List JavaDoc;
32 import java.util.Set JavaDoc;
33 import java.util.zip.ZipEntry JavaDoc;
34 import java.util.zip.ZipFile JavaDoc;
35
36 import org.apache.bcel.classfile.ClassFormatException;
37 import org.apache.bcel.classfile.ClassParser;
38 import org.apache.bcel.classfile.JavaClass;
39
40 import edu.umd.cs.findbugs.FindBugs;
41 import edu.umd.cs.findbugs.util.Archive;
42
43 /**
44  * A work-alike class to use instead of BCEL's ClassPath class.
45  * The main difference is that URLClassPath can load
46  * classfiles from URLs.
47  *
48  * @author David Hovemeyer
49  */

50 public class URLClassPath implements Serializable JavaDoc {
51     private static final long serialVersionUID = 1L;
52
53     /**
54      * Interface describing a single classpath entry.
55      */

56     private interface Entry {
57         /**
58          * Open an input stream to read a resource in the codebase
59          * described by this classpath entry.
60          *
61          * @param resourceName name of resource to load: e.g., "java/lang/Object.class"
62          * @return an InputStream, or null if the resource wasn't found
63          * @throws IOException if an I/O error occurs
64          */

65         public InputStream JavaDoc openStream(String JavaDoc resourceName) throws IOException JavaDoc;
66         
67         /**
68          * Get filename or URL as string.
69          */

70         public String JavaDoc getURL();
71         
72         /**
73          * Close the underlying resource.
74          */

75         public void close();
76     }
77
78     /**
79      * Classpath entry class to load files from a zip/jar file
80      * in the local filesystem.
81      */

82     private static class LocalArchiveEntry implements Entry JavaDoc {
83         private ZipFile JavaDoc zipFile;
84         
85         public LocalArchiveEntry(String JavaDoc fileName) throws IOException JavaDoc {
86             try {
87                 zipFile = new ZipFile JavaDoc(fileName);
88             } catch (IOException JavaDoc e) {
89                 IOException JavaDoc ioe = new IOException JavaDoc("Could not open archive file " + fileName);
90                 ioe.initCause(e);
91                 throw ioe;
92             }
93         }
94
95         /* (non-Javadoc)
96          * @see edu.umd.cs.findbugs.URLClassPath.Entry#openStream(java.lang.String)
97          */

98         public InputStream JavaDoc openStream(String JavaDoc resourceName) throws IOException JavaDoc {
99             ZipEntry JavaDoc zipEntry = zipFile.getEntry(resourceName);
100             if (zipEntry == null)
101                 return null;
102             return zipFile.getInputStream(zipEntry);
103         }
104
105         /* (non-Javadoc)
106          * @see edu.umd.cs.findbugs.URLClassPath.Entry#getURL()
107          */

108         public String JavaDoc getURL() {
109             return zipFile.getName();
110         }
111
112         public void close() {
113             try {
114                 zipFile.close();
115             } catch (IOException JavaDoc e) {
116                 // Ignore
117
}
118         }
119     }
120     
121     /**
122      * Classpath entry class to load files from a directory
123      * in the local filesystem.
124      */

125     private static class LocalDirectoryEntry implements Entry JavaDoc {
126         private String JavaDoc dirName;
127         
128         /**
129          * Constructor.
130          *
131          * @param dirName name of the local directory
132          * @throws IOException if dirName is not a directory
133          */

134         public LocalDirectoryEntry(String JavaDoc dirName) throws IOException JavaDoc {
135             this.dirName = dirName;
136             if (!(new File JavaDoc(dirName).isDirectory()))
137                 throw new IOException JavaDoc(dirName + " is not a directory");
138         }
139
140         /* (non-Javadoc)
141          * @see edu.umd.cs.findbugs.URLClassPath.Entry#openStream(java.lang.String)
142          */

143         public InputStream JavaDoc openStream(String JavaDoc resourceName) throws IOException JavaDoc {
144             File JavaDoc file = new File JavaDoc(dirName, resourceName);
145             if (!file.exists())
146                 return null;
147             return new BufferedInputStream JavaDoc(new FileInputStream JavaDoc(file));
148         }
149
150         /* (non-Javadoc)
151          * @see edu.umd.cs.findbugs.URLClassPath.Entry#getURL()
152          */

153         public String JavaDoc getURL() {
154             return dirName;
155         }
156
157         public void close() {
158             // Nothing to do here
159
}
160         
161     }
162     
163     /**
164      * Classpath entry class to load files from a remote archive URL.
165      * It uses jar URLs to specify individual files within the
166      * remote archive.
167      */

168     private static class RemoteArchiveEntry implements Entry JavaDoc {
169         private URL JavaDoc remoteArchiveURL;
170         
171         /**
172          * Constructor.
173          * @param remoteArchiveURL the remote zip/jar file URL
174          */

175         public RemoteArchiveEntry(URL JavaDoc remoteArchiveURL) {
176             this.remoteArchiveURL = remoteArchiveURL;
177         }
178
179         /* (non-Javadoc)
180          * @see edu.umd.cs.findbugs.URLClassPath.Entry#openStream(java.lang.String)
181          */

182         public InputStream JavaDoc openStream(String JavaDoc resourceName) throws IOException JavaDoc {
183             URL JavaDoc remoteFileURL = new URL JavaDoc("jar:" + remoteArchiveURL.toString() +
184                     "/" + resourceName);
185             try {
186                 return remoteFileURL.openStream();
187             } catch (IOException JavaDoc e) {
188                 return null;
189             }
190         }
191
192         /* (non-Javadoc)
193          * @see edu.umd.cs.findbugs.URLClassPath.Entry#getURL()
194          */

195         public String JavaDoc getURL() {
196             return remoteArchiveURL.toString();
197         }
198
199         public void close() {
200             // Nothing to do
201
}
202         
203     }
204     
205     /**
206      * Classpath entry class to load files from a remote directory URL.
207      */

208     private static class RemoteDirectoryEntry implements Entry JavaDoc {
209         private URL JavaDoc remoteDirURL;
210         
211         /**
212          * Constructor.
213          * @param remoteDirURL URL of the remote directory; must end in "/"
214          */

215         public RemoteDirectoryEntry(URL JavaDoc remoteDirURL) {
216             this.remoteDirURL = remoteDirURL;
217         }
218
219         /* (non-Javadoc)
220          * @see edu.umd.cs.findbugs.URLClassPath.Entry#openStream(java.lang.String)
221          */

222         public InputStream JavaDoc openStream(String JavaDoc resourceName) throws IOException JavaDoc {
223             URL JavaDoc remoteFileURL = new URL JavaDoc(remoteDirURL.toString() + resourceName);
224             try {
225                 return remoteFileURL.openStream();
226             } catch (IOException JavaDoc e) {
227                 return null;
228             }
229         }
230
231         /* (non-Javadoc)
232          * @see edu.umd.cs.findbugs.URLClassPath.Entry#getURL()
233          */

234         public String JavaDoc getURL() {
235             return remoteDirURL.toString();
236         }
237
238         public void close() {
239             // Nothing to do
240
}
241     }
242
243     // Fields
244
private List JavaDoc<Entry JavaDoc> entryList;
245
246     /**
247      * Constructor.
248      * Creates a classpath with no elements.
249      */

250     public URLClassPath() {
251         this.entryList = new LinkedList JavaDoc<Entry JavaDoc>();
252     }
253     
254     /**
255      * Add given filename/URL to the classpath.
256      * If no URL protocol is given, the filename is assumed
257      * to be a local file or directory.
258      * Remote directories must be specified with a "/" character at the
259      * end of the URL.
260      *
261      * @param fileName filename or URL of codebase (directory or archive file)
262      * @throws IOException if entry is invalid or does not exist
263      */

264     public void addURL(String JavaDoc fileName) throws IOException JavaDoc {
265         String JavaDoc protocol = URLClassPath.getURLProtocol(fileName);
266         if (protocol == null) {
267             fileName = "file:" + fileName;
268             protocol = "file";
269         }
270         
271         String JavaDoc fileExtension = URLClassPath.getFileExtension(fileName);
272         boolean isArchive = fileExtension != null && URLClassPath.isArchiveExtension(fileExtension);
273         
274         Entry JavaDoc entry;
275         if (protocol.equals("file")) {
276             String JavaDoc localFileName = fileName.substring("file:".length());
277             
278             if (fileName.endsWith("/") || new File JavaDoc(localFileName).isDirectory())
279                 entry = new LocalDirectoryEntry(localFileName);
280             else if (isArchive)
281                 entry = new LocalArchiveEntry(localFileName);
282             else
283                 throw new IOException JavaDoc("Classpath entry " + fileName +
284                         " is not a directory or archive file");
285         } else {
286             if (fileName.endsWith("/"))
287                 entry = new RemoteDirectoryEntry(new URL JavaDoc(fileName));
288             else if (isArchive)
289                 entry = new RemoteArchiveEntry(new URL JavaDoc(fileName));
290             else
291                 throw new IOException JavaDoc("Classpath entry " + fileName +
292                         " is not a remote directory or archive file");
293         }
294         
295         entryList.add(entry);
296     }
297     
298     /**
299      * Return the classpath string.
300      * @return the classpath string
301      */

302     public String JavaDoc getClassPath() {
303         StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
304         for (Entry JavaDoc entry : entryList) {
305             if (buf.length() > 0)
306                 buf.append(File.pathSeparator);
307             buf.append(entry.getURL());
308         }
309         return buf.toString();
310     }
311     
312     /**
313      * Open a stream to read given resource.
314      *
315      * @param resourceName name of resource to load, e.g. "java/lang/Object.class"
316      * @return input stream to read resource, or null if resource
317      * could not be found
318      * @throws IOException if an IO error occurs trying to determine
319      * whether or not the resource exists
320      */

321     private InputStream JavaDoc getInputStreamForResource(String JavaDoc resourceName) throws IOException JavaDoc {
322         // Try each classpath entry, in order, until we find one
323
// that has the resource. Catch and ignore IOExceptions.
324

325         // FIXME: The following code should throw IOException.
326
//
327
// URL.openStream() does not seem to distinguish
328
// whether the resource does not exist, vs. some
329
// transient error occurring while trying to access it.
330
// This is unfortunate, because we really should throw
331
// an exception out of this method in the latter case,
332
// since it means our knowledge of the classpath is
333
// incomplete.
334
//
335
// Short of reimplementing HTTP, etc., ourselves,
336
// there is probably nothing we can do about this problem.
337

338         for (Entry JavaDoc entry : entryList) {
339             InputStream JavaDoc in;
340             try {
341                 in = entry.openStream(resourceName);
342                 if (in != null) {
343                     if (URLClassPathRepository.DEBUG) {
344                         System.out.println("\t==> found " + resourceName + " in " + entry.getURL());
345                     }
346                     return in;
347                 }
348             } catch (IOException JavaDoc ignore) {
349                 // Ignore
350
}
351         }
352         if (URLClassPathRepository.DEBUG) {
353             System.out.println("\t==> could not find " + resourceName + " on classpath");
354         }
355         return null;
356     }
357     private Set JavaDoc<String JavaDoc> classesThatCantBeFound = new HashSet JavaDoc<String JavaDoc>();
358     /**
359      * Look up a class from the classpath.
360      *
361      * @param className name of class to look up
362      * @return the JavaClass object for the class
363      * @throws ClassNotFoundException if the class couldn't be found
364      * @throws ClassFormatException if the classfile format is invalid
365      */

366     public JavaClass lookupClass(String JavaDoc className) throws ClassNotFoundException JavaDoc {
367         if (classesThatCantBeFound.contains(className)) {
368             throw new ClassNotFoundException JavaDoc("Error while looking for class " +
369                     className + ": class not found");
370         }
371         String JavaDoc resourceName = className.replace('.', '/') + ".class";
372         InputStream JavaDoc in = null;
373         boolean parsedClass = false;
374         
375         try {
376             
377             in = getInputStreamForResource(resourceName);
378             if (in == null) {
379                 classesThatCantBeFound.add(className);
380                 throw new ClassNotFoundException JavaDoc("Error while looking for class " +
381                         className + ": class not found");
382             }
383             
384             ClassParser classParser = new ClassParser(in, resourceName);
385             JavaClass javaClass = classParser.parse();
386             parsedClass = true;
387             
388             return javaClass;
389         } catch (IOException JavaDoc e) {
390             classesThatCantBeFound.add(className);
391             throw new ClassNotFoundException JavaDoc("IOException while looking for class " +
392                     className + ": " + e.toString());
393         } finally {
394             if (in != null && !parsedClass) {
395                 try {
396                     in.close();
397                 } catch (IOException JavaDoc ignore) {
398                     // Ignore
399
}
400             }
401         }
402     }
403     
404     /**
405      * Close all underlying resources.
406      */

407     public void close() {
408         for (Entry JavaDoc entry : entryList) {
409             entry.close();
410         }
411         entryList.clear();
412     }
413
414     /**
415      * Get the URL protocol of given URL string.
416      * @param urlString the URL string
417      * @return the protocol name ("http", "file", etc.), or null if there is no protocol
418      */

419     public static String JavaDoc getURLProtocol(String JavaDoc urlString) {
420         String JavaDoc protocol = null;
421         int firstColon = urlString.indexOf(':');
422         if (firstColon >= 0) {
423             String JavaDoc specifiedProtocol = urlString.substring(0, firstColon);
424             if (FindBugs.knownURLProtocolSet.contains(specifiedProtocol))
425                 protocol = specifiedProtocol;
426         }
427         return protocol;
428     }
429
430     /**
431      * Get the file extension of given fileName.
432      * @return the file extension, or null if there is no file extension
433      */

434     public static String JavaDoc getFileExtension(String JavaDoc fileName) {
435         int lastDot = fileName.lastIndexOf('.');
436         return (lastDot >= 0)
437             ? fileName.substring(lastDot)
438             : null;
439     }
440
441     /**
442      * Determine if given file extension indicates an archive file.
443      *
444      * @param fileExtension the file extension (e.g., ".jar")
445      * @return true if the file extension indicates an archive,
446      * false otherwise
447      */

448     public static boolean isArchiveExtension(String JavaDoc fileExtension) {
449         return Archive.ARCHIVE_EXTENSION_SET.contains(fileExtension);
450     }
451 }
452
453 // vim:ts=4
454
Popular Tags