KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > JarClassLoader


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans;
21
22 import java.io.File JavaDoc;
23 import java.io.FileInputStream JavaDoc;
24 import java.io.FileOutputStream JavaDoc;
25 import java.io.IOException JavaDoc;
26 import java.io.InputStream JavaDoc;
27 import java.io.OutputStream JavaDoc;
28 import java.net.MalformedURLException JavaDoc;
29 import java.net.URL JavaDoc;
30 import java.security.CodeSource JavaDoc;
31 import java.security.PermissionCollection JavaDoc;
32 import java.security.Policy JavaDoc;
33 import java.security.ProtectionDomain JavaDoc;
34 import java.security.cert.Certificate JavaDoc;
35 import java.util.ArrayList JavaDoc;
36 import java.util.Arrays JavaDoc;
37 import java.util.Enumeration JavaDoc;
38 import java.util.HashSet JavaDoc;
39 import java.util.List JavaDoc;
40 import java.util.Set JavaDoc;
41 import java.util.Vector JavaDoc;
42 import java.util.jar.Attributes JavaDoc;
43 import java.util.jar.Attributes.Name;
44 import java.util.jar.JarFile JavaDoc;
45 import java.util.jar.Manifest JavaDoc;
46 import java.util.logging.Level JavaDoc;
47 import java.util.logging.Logger JavaDoc;
48 import java.util.zip.ZipEntry JavaDoc;
49 import org.openide.util.Union2;
50
51 /**
52  * A ProxyClassLoader capable of loading classes from a set of jar files
53  * and local directories.
54  *
55  * @author Petr Nejedly
56  */

57 public class JarClassLoader extends ProxyClassLoader {
58
59     private static final Logger JavaDoc LOGGER = Logger.getLogger(JarClassLoader.class.getName());
60
61     private Source JavaDoc[] sources;
62     /** temp copy JARs which ought to be deleted */
63     private Set JavaDoc<JarFile JavaDoc> deadJars = null;
64     
65     /** Creates new JarClassLoader.
66      * Gives transitive flag as true.
67      */

68     public JarClassLoader(List JavaDoc<Union2<File JavaDoc,JarFile JavaDoc>> files, ClassLoader JavaDoc[] parents) {
69         this(files, parents, true);
70     }
71     
72     /** Creates new JarClassLoader.
73      * @since org.netbeans.core/1 > 1.6
74      * @see ProxyClassLoader#ProxyClassLoader(ClassLoader[],boolean)
75      */

76     public JarClassLoader(List JavaDoc<Union2<File JavaDoc,JarFile JavaDoc>> files, ClassLoader JavaDoc[] parents, boolean transitive) {
77         super(parents, transitive);
78
79         sources = new Source JavaDoc[files.size()];
80         try {
81             int i=0;
82             for (Union2<File JavaDoc,JarFile JavaDoc> file : files) {
83                 if (file.hasFirst()) {
84                     sources[i++] = new DirSource(file.first(), this);
85                 } else {
86                     sources[i++] = new JarSource(file.second(), this);
87                 }
88             }
89         } catch (MalformedURLException JavaDoc exc) {
90             throw new IllegalArgumentException JavaDoc(exc.getMessage());
91         }
92             
93     }
94     
95     /** Boot classloader needs to add entries for netbeans.user later.
96      */

97     final void addSources (List JavaDoc<Union2<File JavaDoc,JarFile JavaDoc>> newSources) {
98         ArrayList JavaDoc<Source JavaDoc> l = new ArrayList JavaDoc<Source JavaDoc> (sources.length + newSources.size ());
99         l.addAll (Arrays.asList (sources));
100         try {
101             for (Union2<File JavaDoc,JarFile JavaDoc> file : newSources) {
102                 if (file.hasFirst()) {
103                     l.add(new DirSource(file.first(), this));
104                 } else {
105                     l.add(new JarSource(file.second(), this));
106                 }
107             }
108         } catch (MalformedURLException JavaDoc exc) {
109             throw new IllegalArgumentException JavaDoc(exc.getMessage());
110         }
111         sources = l.toArray (sources);
112     }
113
114     /** Allows to specify the right permissions, OneModuleClassLoader does it differently.
115      */

116     protected PermissionCollection JavaDoc getPermissions( CodeSource JavaDoc cs ) {
117         return Policy.getPolicy().getPermissions(cs);
118     }
119     
120     
121     protected Package JavaDoc definePackage(String JavaDoc name, Manifest JavaDoc man, URL JavaDoc url)
122     throws IllegalArgumentException JavaDoc
123     {
124         if (man == null ) {
125             return definePackage(name, null, null, null, null, null, null, null);
126         }
127         
128     String JavaDoc path = name.replace('.', '/').concat("/"); // NOI18N
129
Attributes JavaDoc spec = man.getAttributes(path);
130         Attributes JavaDoc main = man.getMainAttributes();
131     
132         String JavaDoc specTitle = getAttr(spec, main, Name.SPECIFICATION_TITLE);
133         String JavaDoc implTitle = getAttr(spec, main, Name.IMPLEMENTATION_TITLE);
134         String JavaDoc specVersion = getAttr(spec, main, Name.SPECIFICATION_VERSION);
135         String JavaDoc implVersion = getAttr(spec, main, Name.IMPLEMENTATION_VERSION);
136         String JavaDoc specVendor = getAttr(spec, main, Name.SPECIFICATION_VENDOR);
137         String JavaDoc implVendor = getAttr(spec, main, Name.IMPLEMENTATION_VENDOR);
138         String JavaDoc sealed = getAttr(spec, main, Name.SEALED);
139
140         URL JavaDoc sealBase = "true".equalsIgnoreCase(sealed) ? url : null; // NOI18N
141
return definePackage(name, specTitle, specVersion, specVendor,
142                  implTitle, implVersion, implVendor, sealBase);
143     }
144
145     private static String JavaDoc getAttr(Attributes JavaDoc spec, Attributes JavaDoc main, Name name) {
146         String JavaDoc val = null;
147         if (spec != null) val = spec.getValue (name);
148         if (val == null && main != null) val = main.getValue (name);
149         return val;
150     }
151
152     protected Class JavaDoc simpleFindClass(String JavaDoc name, String JavaDoc path, String JavaDoc pkgnameSlashes) {
153         // look up the Sources and return a class based on their content
154
for( int i=0; i<sources.length; i++ ) {
155             Source JavaDoc src = sources[i];
156             byte[] data = src.getClassData(name, path);
157             if (data == null) continue;
158             
159             // do the enhancing
160
byte[] d = PatchByteCode.patch (data, name);
161             data = d;
162             
163             int j = name.lastIndexOf('.');
164             String JavaDoc pkgName = name.substring(0, j);
165             // Note that we assume that if we are defining a class in this package,
166
// we should also define the package! Thus recurse==false.
167
// However special packages might be defined in a parent and then we want
168
// to have the same Package object, proper sealing check, etc.; so be safe,
169
// overhead is probably small (check in parents, nope, check super which
170
// delegates to system loaders).
171
Package JavaDoc pkg = getPackageFast(pkgName, pkgnameSlashes, isSpecialResource(pkgnameSlashes));
172             if (pkg != null) {
173                 // XXX full sealing check, URLClassLoader does something more
174
if (pkg.isSealed() && !pkg.isSealed(src.getURL())) throw new SecurityException JavaDoc("sealing violation"); // NOI18N
175
} else {
176                 Manifest JavaDoc man = src.getManifest();
177                 definePackage (pkgName, man, src.getURL());
178             }
179
180             return defineClass (name, data, 0, data.length, src.getProtectionDomain());
181         }
182         return null;
183     }
184     // look up the jars and return a resource based on a content of jars
185
protected URL JavaDoc findResource(String JavaDoc name) {
186         for( int i=0; i<sources.length; i++ ) {
187             URL JavaDoc item = sources[i].getResource(name);
188             if (item != null) return item;
189         }
190     return null;
191     }
192
193     protected Enumeration JavaDoc<URL JavaDoc> simpleFindResources(String JavaDoc name) {
194         Vector JavaDoc<URL JavaDoc> v = new Vector JavaDoc<URL JavaDoc>(3);
195         // look up the jars and return a resource based on a content of jars
196

197         for( int i=0; i<sources.length; i++ ) {
198             URL JavaDoc item = sources[i].getResource(name);
199             if (item != null) v.add(item);
200         }
201         return v.elements();
202     }
203     
204     public void destroy() {
205         super.destroy ();
206         
207         // #21114 : try to release any JAR locks held by this classloader
208
assert deadJars == null : "Already had dead JARs: " + deadJars;
209         deadJars = new HashSet JavaDoc<JarFile JavaDoc>();
210         try {
211             for (int i = 0; i < sources.length; i++) {
212                 if (sources[i] instanceof JarSource) {
213                     JarFile JavaDoc origJar = ((JarSource)sources[i]).getJarFile();
214                     File JavaDoc orig = new File JavaDoc(origJar.getName());
215                     if (!orig.isFile()) {
216                         // Can happen when a test module is deleted:
217
// the physical JAR has already been deleted
218
// when the module was disabled. In this case it
219
// is possible that a classloader request for something
220
// in the JAR could still come in. Does it matter?
221
// See comment in Module.cleanup.
222
continue;
223                     }
224                     String JavaDoc name = orig.getName();
225                     String JavaDoc prefix, suffix;
226                     int idx = name.lastIndexOf('.');
227                     if (idx == -1) {
228                         prefix = name;
229                         suffix = null;
230                     } else {
231                         prefix = name.substring(0, idx);
232                         suffix = name.substring(idx);
233                     }
234                     while (prefix.length() < 3) prefix += "x"; // NOI18N
235
File JavaDoc temp = File.createTempFile(prefix, suffix);
236                     temp.deleteOnExit();
237                     // XXX should use NIO for speed
238
InputStream JavaDoc is = new FileInputStream JavaDoc(orig);
239                     try {
240                         OutputStream JavaDoc os = new FileOutputStream JavaDoc(temp);
241                         try {
242                             byte[] buf = new byte[4096];
243                             int j;
244                             while ((j = is.read(buf)) != -1) {
245                                 os.write(buf, 0, j);
246                             }
247                         } finally {
248                             os.close();
249                         }
250                     } finally {
251                         is.close();
252                     }
253                     // Don't use OPEN_DELETE even though it sounds like a good idea.
254
// Can cause real problems under 1.4; see Module.java.
255
JarFile JavaDoc tempJar = new JarFile JavaDoc(temp);
256                     origJar.close();
257                     deadJars.add(tempJar);
258                     sources[i] = new JarSource(tempJar, this);
259                     LOGGER.log(Level.FINE, "#21114: replacing {0} with {1}", new Object JavaDoc[] {orig, temp});
260                 }
261             }
262         } catch (IOException JavaDoc ioe) {
263             LOGGER.log(Level.WARNING, null, ioe);
264         }
265     }
266
267     /** Delete any temporary JARs we were holding on to.
268      * Also close any other JARs in our list.
269      */

270     protected void finalize() throws Throwable JavaDoc {
271         super.finalize();
272         for (int i = 0; i < sources.length; i++) {
273             if (sources[i] instanceof JarSource) {
274                 JarFile JavaDoc j = ((JarSource)sources[i]).getJarFile();
275                 File JavaDoc f = new File JavaDoc(j.getName());
276                 j.close();
277                 if (deadJars != null && deadJars.contains(j)) {
278                     LOGGER.log(Level.FINE, "#21114: closing and deleting temporary JAR {0}", f);
279                     if (f.isFile() && !f.delete()) {
280                         LOGGER.log(Level.FINE, "(but failed to delete {0})", f);
281                     }
282                 }
283             }
284         }
285     }
286
287     static abstract class Source {
288         private URL JavaDoc url;
289         private ProtectionDomain JavaDoc pd;
290         protected JarClassLoader jcl;
291         
292         public Source(URL JavaDoc url, JarClassLoader jcl) {
293             this.url = url;
294             this.jcl = jcl;
295         }
296         
297         public final URL JavaDoc getURL() {
298             return url;
299         }
300         
301         public final ProtectionDomain JavaDoc getProtectionDomain() {
302             if (pd == null) {
303                 CodeSource JavaDoc cs = new CodeSource JavaDoc(url, new Certificate JavaDoc[0]);
304                 pd = new ProtectionDomain JavaDoc(cs, jcl.getPermissions(cs));
305             }
306             return pd;
307         }
308   
309         public final URL JavaDoc getResource(String JavaDoc name) {
310             try {
311                 return doGetResource(name);
312             } catch (Exception JavaDoc e) {
313                 // can't get the resource. E.g. already closed JarFile
314
LOGGER.log(Level.FINE, null, e);
315             }
316             return null;
317         }
318         
319         protected abstract URL JavaDoc doGetResource(String JavaDoc name) throws MalformedURLException JavaDoc;
320         
321         public final byte[] getClassData(String JavaDoc name, String JavaDoc path) {
322             try {
323                 return readClass(name, path);
324             } catch (IOException JavaDoc e) {
325                 LOGGER.log(Level.FINE, null, e);
326             }
327             return null;
328         }
329
330         protected abstract byte[] readClass(String JavaDoc name, String JavaDoc path) throws IOException JavaDoc;
331
332         public Manifest JavaDoc getManifest() {
333             return null;
334         }
335     }
336
337     static class JarSource extends Source JavaDoc {
338         JarFile JavaDoc src;
339         private String JavaDoc resPrefix;
340         
341         public JarSource(JarFile JavaDoc file, JarClassLoader jcl) throws MalformedURLException JavaDoc {
342             super(new File JavaDoc(file.getName()).toURI().toURL(), jcl);
343             src = file;
344             resPrefix = "jar:" + new File JavaDoc(src.getName()).toURI() + "!/"; // NOI18N;
345
}
346
347         public Manifest JavaDoc getManifest() {
348             try {
349                 return src.getManifest();
350             } catch (IOException JavaDoc e) {
351                 return null;
352             }
353         }
354         
355         JarFile JavaDoc getJarFile() {
356             return src;
357         }
358         
359         protected URL JavaDoc doGetResource(String JavaDoc name) throws MalformedURLException JavaDoc {
360             ZipEntry JavaDoc ze;
361             try {
362                 ze = src.getEntry(name);
363             } catch (IllegalStateException JavaDoc ex) {
364                 // this exception occurs in org/netbeans/core/lookup/* tests
365
// without this catch statement the tests fail
366
return null;
367             }
368             if (ze == null) {
369                 return null;
370             }
371             LOGGER.log(Level.FINER, "Loading {0} from {1}", new Object JavaDoc[] {name, src.getName()});
372             return new URL JavaDoc(resPrefix + ze.getName());
373         }
374         
375         protected byte[] readClass(String JavaDoc name, String JavaDoc path) throws IOException JavaDoc {
376             ZipEntry JavaDoc ze;
377             try {
378                 ze = src.getEntry(path);
379             } catch (IllegalStateException JavaDoc ex) {
380                 // this exception occurs in org/netbeans/core/lookup/* tests
381
// without this catch statement the tests fail
382
return null;
383             }
384             if (ze == null) {
385                 return null;
386             }
387             LOGGER.log(Level.FINER, "Loading {0} from {1}", new Object JavaDoc[] {path, src.getName()});
388             
389             int len = (int)ze.getSize();
390             byte[] data = new byte[len];
391             InputStream JavaDoc is = src.getInputStream(ze);
392             int count = 0;
393             while (count < len) {
394                 count += is.read(data, count, len-count);
395             }
396             return data;
397         }
398     }
399
400     static class DirSource extends Source JavaDoc {
401         File JavaDoc dir;
402         
403         public DirSource(File JavaDoc file, JarClassLoader jcl) throws MalformedURLException JavaDoc {
404             super(file.toURI().toURL(), jcl);
405             dir = file;
406         }
407
408         protected URL JavaDoc doGetResource(String JavaDoc name) throws MalformedURLException JavaDoc {
409             File JavaDoc resFile = new File JavaDoc(dir, name);
410             return resFile.exists() ? resFile.toURI().toURL() : null;
411         }
412         
413         protected byte[] readClass(String JavaDoc name, String JavaDoc path) throws IOException JavaDoc {
414             File JavaDoc clsFile = new File JavaDoc(dir, path.replace('/', File.separatorChar));
415             if (!clsFile.exists()) return null;
416             
417             int len = (int)clsFile.length();
418             byte[] data = new byte[len];
419             InputStream JavaDoc is = new FileInputStream JavaDoc(clsFile);
420             int count = 0;
421             while (count < len) {
422                 count += is.read(data, count, len-count);
423             }
424             return data;
425         }
426         
427     }
428     
429 }
430
Popular Tags