KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > jdt > apt > core > internal > JarClassLoader


1 /*******************************************************************************
2  * Copyright (c) 2005, 2007 BEA Systems, Inc.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * jgarms@bea.com - initial API and implementation
10  *
11  *******************************************************************************/

12 package org.eclipse.jdt.apt.core.internal;
13
14 import java.io.ByteArrayOutputStream JavaDoc;
15 import java.io.File JavaDoc;
16 import java.io.IOException JavaDoc;
17 import java.io.InputStream JavaDoc;
18 import java.net.URL JavaDoc;
19 import java.util.ArrayList JavaDoc;
20 import java.util.Enumeration JavaDoc;
21 import java.util.LinkedHashSet JavaDoc;
22 import java.util.LinkedList JavaDoc;
23 import java.util.List JavaDoc;
24 import java.util.Set JavaDoc;
25 import java.util.jar.Attributes JavaDoc;
26 import java.util.jar.JarFile JavaDoc;
27 import java.util.jar.Manifest JavaDoc;
28 import java.util.zip.ZipEntry JavaDoc;
29
30
31
32 /**
33  * This classloader allows us to close out underlying jars,
34  * so that projects can be deleted even if they contain
35  * factory jars that are in use.<P>
36  *
37  * This classloader caches open jars while it is in use,
38  * and once closed it will close those jars. It can still be used
39  * after that point, but it will open and close on each classloader
40  * operation.
41  */

42 public class JarClassLoader extends ClassLoader JavaDoc {
43     
44     // This is nulled out when the classloader is closed
45
private List JavaDoc<JarFile JavaDoc> _jars;
46     private final LinkedHashSet JavaDoc<File JavaDoc> _files;
47     private List JavaDoc<JarCLInputStream> _openStreams = new LinkedList JavaDoc<JarCLInputStream>();
48     private boolean _open = true;
49     
50     public JarClassLoader(List JavaDoc<File JavaDoc> jarFiles, final ClassLoader JavaDoc parent) {
51         super(parent);
52         // Handle manifest classpath entries
53
_files = new LinkedHashSet JavaDoc<File JavaDoc>(jarFiles);
54         for (File JavaDoc f : jarFiles) {
55             _recursiveGetManifestJars(f, _files);
56         }
57         open();
58     }
59     
60     private void open() {
61         // Create all jar files
62
_jars = new ArrayList JavaDoc<JarFile JavaDoc>(_files.size());
63         for (File JavaDoc f : _files) {
64             try {
65                 JarFile JavaDoc jar = new JarFile JavaDoc(f);
66                 _jars.add(jar);
67             }
68             catch (IOException JavaDoc ioe) {
69                 AptPlugin.log(ioe, "Unable to create JarFile for file: " + f); //$NON-NLS-1$
70
}
71         }
72     }
73     
74     public synchronized void close() {
75         if (! _open) return;
76         _open = false;
77         
78         for (JarCLInputStream st : _openStreams) {
79             try {
80                 st.close();
81             }
82             catch (IOException JavaDoc ioe) {
83                 AptPlugin.log(ioe, "Failed to close stream"); //$NON-NLS-1$
84
}
85         }
86         _openStreams = null;
87
88         for (JarFile JavaDoc jar : _jars) {
89             try {
90                 jar.close();
91             }
92             catch (IOException JavaDoc ioe) {
93                 AptPlugin.log(ioe, "Failed to close jar: " + jar); //$NON-NLS-1$
94
}
95         }
96         _jars = null;
97     }
98     
99     private InputStream JavaDoc openInputStream(InputStream JavaDoc in) {
100         JarCLInputStream result = new JarCLInputStream(in);
101         _openStreams.add(result);
102         return in;
103     }
104     
105     private synchronized void closeInputStream(JarCLInputStream in) {
106         if (_open)
107             _openStreams.remove(in);
108     }
109     
110     @Override JavaDoc
111     protected synchronized Class JavaDoc<?> findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
112         if (!_open)
113             throw new ClassNotFoundException JavaDoc("Classloader closed: " + name); //$NON-NLS-1$
114

115         byte[] b = loadClassData(name);
116         if (b == null) {
117             throw new ClassNotFoundException JavaDoc("Could not find class " + name); //$NON-NLS-1$
118
}
119         Class JavaDoc<?> clazz = defineClass(name, b, 0, b.length);
120         // Define the package if necessary
121
String JavaDoc pkgName = getPackageName(name);
122         if (pkgName != null) {
123             Package JavaDoc pkg = getPackage(pkgName);
124             if (pkg == null) {
125                 definePackage(pkgName, null, null, null, null, null, null, null);
126             }
127         }
128         return clazz;
129     }
130     
131     private String JavaDoc getPackageName(String JavaDoc fullyQualifiedName) {
132         int index = fullyQualifiedName.lastIndexOf('.');
133         if (index != -1) {
134             return fullyQualifiedName.substring(0, index);
135         }
136         return null;
137     }
138     
139     // returns null if no class found
140
private byte[] loadClassData(String JavaDoc name) {
141         name = name.replace('.','/');
142         InputStream JavaDoc input = getResourceAsStream(name + ".class"); //$NON-NLS-1$
143
if (input == null)
144             return null;
145         try {
146             ByteArrayOutputStream JavaDoc baos = new ByteArrayOutputStream JavaDoc();
147             byte[] buf = new byte[1024];
148             int len;
149             while ((len = input.read(buf)) > 0) {
150                 baos.write(buf, 0, len);
151             }
152             baos.close();
153             return baos.toByteArray();
154         }
155         catch (IOException JavaDoc ioe) {
156             return null;
157         }
158         finally {
159             try {input.close();} catch (IOException JavaDoc ioe) {}
160         }
161     }
162     
163     @Override JavaDoc
164     public synchronized InputStream JavaDoc getResourceAsStream(String JavaDoc name) {
165         InputStream JavaDoc input = getParent().getResourceAsStream(name);
166         if (input != null)
167             return input;
168         
169         if (!_open)
170             return null;
171     
172         for (JarFile JavaDoc j : _jars) {
173             try {
174                 ZipEntry JavaDoc entry = j.getEntry(name);
175                 if (entry != null) {
176                     InputStream JavaDoc zipInput = j.getInputStream(entry);
177                     return openInputStream(zipInput);
178                 }
179             }
180             catch (IOException JavaDoc ioe) {
181                 AptPlugin.log(ioe, "Unable to get entry from jar: " + j); //$NON-NLS-1$
182
}
183         }
184         return null;
185     }
186     
187     /**
188      * This is difficult to implement and close out resources underneath.
189      * Delaying until someone actually requests this.
190      *
191      * If we actually contain the entry throw UnsupportedOperationException,
192      * else return null in case another classloader can handle this.
193      */

194     @Override JavaDoc
195     public URL JavaDoc getResource(String JavaDoc name) {
196         for (JarFile JavaDoc j : _jars) {
197             ZipEntry JavaDoc entry = j.getEntry(name);
198             if (entry != null) {
199                 throw new UnsupportedOperationException JavaDoc("getResource() not implemented: " + name); //$NON-NLS-1$
200
}
201         }
202         return null;
203     }
204
205     /**
206      * This is difficult to implement and close out resources underneath.
207      * Delaying until someone actually requests this.
208      */

209     @Override JavaDoc
210     public Enumeration JavaDoc<URL JavaDoc> getResources(String JavaDoc name) throws IOException JavaDoc {
211         throw new UnsupportedOperationException JavaDoc("getResources() not implemented"); //$NON-NLS-1$
212
}
213     
214     private class JarCLInputStream extends InputStream JavaDoc {
215         
216         private boolean _closed = false;
217         
218         private final InputStream JavaDoc _input;
219         
220         public JarCLInputStream(InputStream JavaDoc origInput) {
221             _input = origInput;
222         }
223
224         @Override JavaDoc
225         public void close() throws IOException JavaDoc {
226             if (_closed) {
227                 // NOOP
228
return;
229             }
230             try {
231                 super.close();
232                 _input.close();
233                 _closed = true;
234             }
235             finally {
236                 closeInputStream(this);
237             }
238         }
239
240         @Override JavaDoc
241         public int read() throws IOException JavaDoc {
242             return _input.read();
243         }
244
245         @Override JavaDoc
246         public int available() throws IOException JavaDoc {
247             return _input.available();
248         }
249
250         @Override JavaDoc
251         public synchronized void mark(int readlimit) {
252             _input.mark(readlimit);
253         }
254
255         @Override JavaDoc
256         public boolean markSupported() {
257             return _input.markSupported();
258         }
259
260         @Override JavaDoc
261         public int read(byte[] b, int off, int len) throws IOException JavaDoc {
262             return _input.read(b, off, len);
263         }
264
265         @Override JavaDoc
266         public int read(byte[] b) throws IOException JavaDoc {
267             return _input.read(b);
268         }
269
270         @Override JavaDoc
271         public synchronized void reset() throws IOException JavaDoc {
272             _input.reset();
273         }
274
275         @Override JavaDoc
276         public long skip(long n) throws IOException JavaDoc {
277             return _input.skip(n);
278         }
279     }
280     
281     /**
282      * Scan manifest classpath entries of a jar, adding all found jar files
283      * to the set of manifest jars.
284      */

285     private static void _recursiveGetManifestJars(File JavaDoc jarFile, Set JavaDoc<File JavaDoc> manifestJars) {
286         if (!jarFile.exists())
287             return;
288
289         JarFile JavaDoc jar = null;
290         try {
291             jar = new JarFile JavaDoc(jarFile);
292             Manifest JavaDoc mf = jar.getManifest();
293             if (mf == null)
294                 return;
295             String JavaDoc classpath = mf.getMainAttributes().getValue(Attributes.Name.CLASS_PATH);
296             if (classpath == null)
297                 return;
298
299             // We've got some entries
300
File JavaDoc parent = jarFile.getParentFile();
301
302             String JavaDoc[] rgPaths = classpath.split(" "); //$NON-NLS-1$
303
for (String JavaDoc path : rgPaths)
304             {
305                 if (path.length() == 0)
306                     continue;
307                 File JavaDoc file = new File JavaDoc(parent, path);
308                 // If we haven't seen this, we need to get its manifest jars as well
309
if (!manifestJars.contains(file) && file.exists()) {
310                     manifestJars.add(file);
311                     _recursiveGetManifestJars(file, manifestJars);
312                 }
313             }
314         }
315         catch (IOException JavaDoc ioe) {
316         }
317         finally {
318             if (jar != null) {
319                 try {jar.close();} catch (IOException JavaDoc ioe) {}
320             }
321         }
322     }
323 }
324
Popular Tags