KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > geronimo > kernel > classloader > JarFileClassLoader


1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements. See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License. You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */

17 package org.apache.geronimo.kernel.classloader;
18
19 import java.io.IOException JavaDoc;
20 import java.io.File JavaDoc;
21 import java.net.URL JavaDoc;
22 import java.net.URI JavaDoc;
23 import java.net.URLClassLoader JavaDoc;
24 import java.security.AccessControlContext JavaDoc;
25 import java.security.AccessController JavaDoc;
26 import java.security.CodeSource JavaDoc;
27 import java.security.PrivilegedAction JavaDoc;
28 import java.security.PrivilegedExceptionAction JavaDoc;
29 import java.security.PrivilegedActionException JavaDoc;
30 import java.security.cert.Certificate JavaDoc;
31 import java.util.Collection JavaDoc;
32 import java.util.Enumeration JavaDoc;
33 import java.util.jar.Attributes JavaDoc;
34 import java.util.jar.Manifest JavaDoc;
35
36 import org.apache.geronimo.kernel.config.MultiParentClassLoader;
37 import org.apache.geronimo.kernel.repository.Artifact;
38
39 /**
40  * The JarFileClassLoader that loads classes and resources from a list of JarFiles. This method is simmilar to URLClassLoader
41  * except it properly closes JarFiles when the classloader is destroyed so that the file read lock will be released, and
42  * the jar file can be modified and deleted.
43  * <p>
44  * Note: This implementation currently does not work reliably on windows, since the jar URL handler included with the Sun JavaVM
45  * holds a read lock on the JarFile, and this lock is not released when the jar url is dereferenced. To fix this a
46  * replacement for the jar url handler must be written.
47  *
48  * @author Dain Sundstrom
49  * @version $Id: JarFileClassLoader.java 476049 2006-11-17 04:35:17Z kevan $
50  * @since 2.0
51  */

52 public class JarFileClassLoader extends MultiParentClassLoader {
53     private static final URL JavaDoc[] EMPTY_URLS = new URL JavaDoc[0];
54
55     private final UrlResourceFinder resourceFinder = new UrlResourceFinder();
56     private final AccessControlContext JavaDoc acc;
57
58     /**
59      * Creates a JarFileClassLoader that is a child of the system class loader.
60      * @param id the name of this class loader
61      * @param urls a list of URLs from which classes and resources should be loaded
62      */

63     public JarFileClassLoader(Artifact id, URL JavaDoc[] urls) {
64         super(id, EMPTY_URLS);
65         this.acc = AccessController.getContext();
66         addURLs(urls);
67     }
68
69     /**
70      * Creates a JarFileClassLoader that is a child of the specified class loader.
71      * @param id the name of this class loader
72      * @param urls a list of URLs from which classes and resources should be loaded
73      * @param parent the parent of this class loader
74      */

75     public JarFileClassLoader(Artifact id, URL JavaDoc[] urls, ClassLoader JavaDoc parent) {
76         super(id, EMPTY_URLS, parent);
77         this.acc = AccessController.getContext();
78         addURLs(urls);
79     }
80
81     public JarFileClassLoader(Artifact id, URL JavaDoc[] urls, ClassLoader JavaDoc parent, boolean inverseClassLoading, String JavaDoc[] hiddenClasses, String JavaDoc[] nonOverridableClasses) {
82         super(id, EMPTY_URLS, parent, inverseClassLoading, hiddenClasses, nonOverridableClasses);
83         this.acc = AccessController.getContext();
84         addURLs(urls);
85     }
86
87     /**
88      * Creates a named class loader as a child of the specified parents.
89      * @param id the name of this class loader
90      * @param urls the urls from which this class loader will classes and resources
91      * @param parents the parents of this class loader
92      */

93     public JarFileClassLoader(Artifact id, URL JavaDoc[] urls, ClassLoader JavaDoc[] parents) {
94         super(id, EMPTY_URLS, parents);
95         this.acc = AccessController.getContext();
96         addURLs(urls);
97     }
98
99     public JarFileClassLoader(Artifact id, URL JavaDoc[] urls, ClassLoader JavaDoc[] parents, boolean inverseClassLoading, Collection JavaDoc hiddenClasses, Collection JavaDoc nonOverridableClasses) {
100         super(id, EMPTY_URLS, parents, inverseClassLoading, hiddenClasses, nonOverridableClasses);
101         this.acc = AccessController.getContext();
102         addURLs(urls);
103     }
104
105     public JarFileClassLoader(Artifact id, URL JavaDoc[] urls, ClassLoader JavaDoc[] parents, boolean inverseClassLoading, String JavaDoc[] hiddenClasses, String JavaDoc[] nonOverridableClasses) {
106         super(id, EMPTY_URLS, parents, inverseClassLoading, hiddenClasses, nonOverridableClasses);
107         this.acc = AccessController.getContext();
108         addURLs(urls);
109     }
110
111     public JarFileClassLoader(JarFileClassLoader source) {
112         super(source);
113         this.acc = AccessController.getContext();
114         addURLs(source.getURLs());
115     }
116
117     public static ClassLoader JavaDoc copy(ClassLoader JavaDoc source) {
118         if (source instanceof JarFileClassLoader) {
119             return new JarFileClassLoader((JarFileClassLoader) source);
120         } else if (source instanceof MultiParentClassLoader) {
121             return new MultiParentClassLoader((MultiParentClassLoader) source);
122         } else if (source instanceof URLClassLoader JavaDoc) {
123             return new URLClassLoader JavaDoc(((URLClassLoader JavaDoc)source).getURLs(), source.getParent());
124         } else {
125             return new URLClassLoader JavaDoc(new URL JavaDoc[0], source);
126         }
127     }
128
129     ClassLoader JavaDoc copy() {
130         return JarFileClassLoader.copy(this);
131     }
132
133     /**
134      * {@inheritDoc}
135      */

136     public URL JavaDoc[] getURLs() {
137         return resourceFinder.getUrls();
138     }
139
140     /**
141      * {@inheritDoc}
142      */

143     public void addURL(final URL JavaDoc url) {
144         AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
145             public Object JavaDoc run() {
146                 resourceFinder.addUrl(url);
147                 return null;
148             }
149         }, acc);
150     }
151
152     /**
153      * Adds an array of urls to the end of this class loader.
154      * @param urls the URLs to add
155      */

156     protected void addURLs(final URL JavaDoc[] urls) {
157         AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
158             public Object JavaDoc run() {
159                 resourceFinder.addUrls(urls);
160                 return null;
161             }
162         }, acc);
163     }
164
165     /**
166      * {@inheritDoc}
167      */

168     public void destroy() {
169         resourceFinder.destroy();
170         super.destroy();
171     }
172
173     /**
174      * {@inheritDoc}
175      */

176     public URL JavaDoc findResource(final String JavaDoc resourceName) {
177         return (URL JavaDoc) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
178             public Object JavaDoc run() {
179                 return resourceFinder.findResource(resourceName);
180             }
181         }, acc);
182     }
183
184     /**
185      * {@inheritDoc}
186      */

187     public Enumeration JavaDoc findResources(final String JavaDoc resourceName) throws IOException JavaDoc {
188         // todo this is not right
189
// first get the resources from the parent classloaders
190
Enumeration JavaDoc parentResources = super.findResources(resourceName);
191
192         // get the classes from my urls
193
Enumeration JavaDoc myResources = (Enumeration JavaDoc) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
194             public Object JavaDoc run() {
195                 return resourceFinder.findResources(resourceName);
196             }
197         }, acc);
198
199         // join the two together
200
Enumeration JavaDoc resources = new UnionEnumeration(parentResources, myResources);
201         return resources;
202     }
203
204     /**
205      * {@inheritDoc}
206      */

207     protected String JavaDoc findLibrary(String JavaDoc libraryName) {
208         // if the libraryName is actually a directory it is invalid
209
int pathEnd = libraryName.lastIndexOf('/');
210         if (pathEnd == libraryName.length() - 1) {
211             throw new IllegalArgumentException JavaDoc("libraryName ends with a '/' character: " + libraryName);
212         }
213
214         // get the name if the library file
215
final String JavaDoc resourceName;
216         if (pathEnd < 0) {
217             resourceName = System.mapLibraryName(libraryName);
218         } else {
219             String JavaDoc path = libraryName.substring(0, pathEnd + 1);
220             String JavaDoc file = libraryName.substring(pathEnd + 1);
221             resourceName = path + System.mapLibraryName(file);
222         }
223
224         // get a resource handle to the library
225
ResourceHandle resourceHandle = (ResourceHandle) AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
226             public Object JavaDoc run() {
227                 return resourceFinder.getResource(resourceName);
228             }
229         }, acc);
230
231         if (resourceHandle == null) {
232             return null;
233         }
234
235         // the library must be accessable on the file system
236
URL JavaDoc url = resourceHandle.getUrl();
237         if (!"file".equals(url.getProtocol())) {
238             return null;
239         }
240
241         String JavaDoc path = new File JavaDoc(URI.create(url.toString())).getPath();
242         return path;
243     }
244
245     /**
246      * {@inheritDoc}
247      */

248     protected Class JavaDoc findClass(final String JavaDoc className) throws ClassNotFoundException JavaDoc {
249         try {
250             return (Class JavaDoc) AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc() {
251                 public Object JavaDoc run() throws ClassNotFoundException JavaDoc {
252                     // first think check if we are allowed to define the package
253
SecurityManager JavaDoc securityManager = System.getSecurityManager();
254                     if (securityManager != null) {
255                         String JavaDoc packageName;
256                         int packageEnd = className.lastIndexOf('.');
257                         if (packageEnd >= 0) {
258                             packageName = className.substring(0, packageEnd);
259                             securityManager.checkPackageDefinition(packageName);
260                         }
261                     }
262
263
264                     // convert the class name to a file name
265
String JavaDoc resourceName = className.replace('.', '/') + ".class";
266
267                     // find the class file resource
268
ResourceHandle resourceHandle = resourceFinder.getResource(resourceName);
269                     if (resourceHandle == null) {
270                         throw new ClassNotFoundException JavaDoc(className);
271                     }
272
273                     byte[] bytes;
274                     Manifest JavaDoc manifest;
275                     try {
276                         // get the bytes from the class file
277
bytes = resourceHandle.getBytes();
278
279                         // get the manifest for defining the packages
280
manifest = resourceHandle.getManifest();
281                     } catch (IOException JavaDoc e) {
282                         throw new ClassNotFoundException JavaDoc(className, e);
283                     }
284
285                     // get the certificates for the code source
286
Certificate JavaDoc[] certificates = resourceHandle.getCertificates();
287
288                     // the code source url is used to define the package and as the security context for the class
289
URL JavaDoc codeSourceUrl = resourceHandle.getCodeSourceUrl();
290
291                     // define the package (required for security)
292
definePackage(className, codeSourceUrl, manifest);
293
294                     // this is the security context of the class
295
CodeSource JavaDoc codeSource = new CodeSource JavaDoc(codeSourceUrl, certificates);
296
297                     // load the class into the vm
298
Class JavaDoc clazz = defineClass(className, bytes, 0, bytes.length, codeSource);
299                     return clazz;
300                 }
301             }, acc);
302         } catch (PrivilegedActionException JavaDoc e) {
303             throw (ClassNotFoundException JavaDoc) e.getException();
304         }
305     }
306
307     private void definePackage(String JavaDoc className, URL JavaDoc jarUrl, Manifest JavaDoc manifest) {
308         int packageEnd = className.lastIndexOf('.');
309         if (packageEnd < 0) {
310             return;
311         }
312
313         String JavaDoc packageName = className.substring(0, packageEnd);
314         String JavaDoc packagePath = packageName.replace('.', '/') + "/";
315
316         Attributes JavaDoc packageAttributes = null;
317         Attributes JavaDoc mainAttributes = null;
318         if (manifest != null) {
319             packageAttributes = manifest.getAttributes(packagePath);
320             mainAttributes = manifest.getMainAttributes();
321         }
322         Package JavaDoc pkg = getPackage(packageName);
323         if (pkg != null) {
324             if (pkg.isSealed()) {
325                 if (!pkg.isSealed(jarUrl)) {
326                     throw new SecurityException JavaDoc("Package was already sealed with another URL: package=" + packageName + ", url=" + jarUrl);
327                 }
328             } else {
329                 if (isSealed(packageAttributes, mainAttributes)) {
330                     throw new SecurityException JavaDoc("Package was already been loaded and not sealed: package=" + packageName + ", url=" + jarUrl);
331                 }
332             }
333         } else {
334             String JavaDoc specTitle = getAttribute(Attributes.Name.SPECIFICATION_TITLE, packageAttributes, mainAttributes);
335             String JavaDoc specVendor = getAttribute(Attributes.Name.SPECIFICATION_VENDOR, packageAttributes, mainAttributes);
336             String JavaDoc specVersion = getAttribute(Attributes.Name.SPECIFICATION_VERSION, packageAttributes, mainAttributes);
337             String JavaDoc implTitle = getAttribute(Attributes.Name.IMPLEMENTATION_TITLE, packageAttributes, mainAttributes);
338             String JavaDoc implVendor = getAttribute(Attributes.Name.IMPLEMENTATION_VENDOR, packageAttributes, mainAttributes);
339             String JavaDoc implVersion = getAttribute(Attributes.Name.IMPLEMENTATION_VERSION, packageAttributes, mainAttributes);
340
341             URL JavaDoc sealBase = null;
342             if (isSealed(packageAttributes, mainAttributes)) {
343                 sealBase = jarUrl;
344             }
345
346             definePackage(packageName, specTitle, specVersion, specVendor, implTitle, implVersion, implVendor, sealBase);
347         }
348     }
349
350     private String JavaDoc getAttribute(Attributes.Name JavaDoc name, Attributes JavaDoc packageAttributes, Attributes JavaDoc mainAttributes) {
351         if (packageAttributes != null) {
352             String JavaDoc value = packageAttributes.getValue(name);
353             if (value != null) {
354                 return value;
355             }
356         }
357         if (mainAttributes != null) {
358             return mainAttributes.getValue(name);
359         }
360         return null;
361     }
362
363     private boolean isSealed(Attributes JavaDoc packageAttributes, Attributes JavaDoc mainAttributes) {
364         String JavaDoc sealed = getAttribute(Attributes.Name.SEALED, packageAttributes, mainAttributes);
365         if (sealed == null) {
366             return false;
367         }
368         return "true".equalsIgnoreCase(sealed);
369     }
370 }
Popular Tags