1 package org.codehaus.classworlds;
2
3 /*
4 $Id: UberJarRealmClassLoader.java,v 1.2 2003/09/23 14:09:46 jvanzyl Exp $
5
6 Copyright 2002 (C) The Werken Company. All Rights Reserved.
7
8 Redistribution and use of this software and associated documentation
9 ("Software"), with or without modification, are permitted provided
10 that the following conditions are met:
11
12 1. Redistributions of source code must retain copyright
13 statements and notices. Redistributions must also contain a
14 copy of this document.
15
16 2. Redistributions in binary form must reproduce the
17 above copyright notice, this list of conditions and the
18 following disclaimer in the documentation and/or other
19 materials provided with the distribution.
20
21 3. The name "classworlds" must not be used to endorse or promote
22 products derived from this Software without prior written
23 permission of The Werken Company. For written permission,
24 please contact bob@werken.com.
25
26 4. Products derived from this Software may not be called "classworlds"
27 nor may "classworlds" appear in their names without prior written
28 permission of The Werken Company. "classworlds" is a registered
29 trademark of The Werken Company.
30
31 5. Due credit should be given to The Werken Company.
32 (http://classworlds.werken.com/).
33
34 THIS SOFTWARE IS PROVIDED BY THE WERKEN COMPANY AND CONTRIBUTORS
35 ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
36 NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
37 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
38 THE WERKEN COMPANY OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
39 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
40 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
41 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
42 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
43 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
44 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
45 OF THE POSSIBILITY OF SUCH DAMAGE.
46
47 */
48
49 import java.net.URL;
50 import java.io.ByteArrayOutputStream;
51 import java.io.InputStream;
52 import java.io.IOException;
53 import java.util.List;
54 import java.util.ArrayList;
55 import java.util.Map;
56 import java.util.HashMap;
57 import java.util.Iterator;
58 import java.util.jar.JarInputStream;
59 import java.util.jar.JarEntry;
60
61 /*** Classloader for <code>ClassRealm</code>s.
62 *
63 * Loads classes from an "uberjar".
64 *
65 * @author <a href="mailto:bob@eng.werken.com">bob mcwhirter</a>
66 *
67 * @version $Id: UberJarRealmClassLoader.java,v 1.2 2003/09/23 14:09:46 jvanzyl Exp $
68 */
69 class UberJarRealmClassLoader
70 extends RealmClassLoader
71 {
72 // ------------------------------------------------------------
73 // Instance members
74 // ------------------------------------------------------------
75
76 /*** Classes, indexed by path. */
77 private Map classIndex;
78
79 /*** Urls. */
80 private List urls;
81
82 /***
83 * Indexes for constituent jars. Contains a Map for each constituent which
84 * is a jar, keyed by url. The second map contains urls to each entry of
85 * the jar keyed by path.
86 */
87 private Map jarIndexes;
88
89 // ------------------------------------------------------------
90 // Constructors
91 // ------------------------------------------------------------
92
93 /*** Construct.
94 *
95 * @param realm The realm for which this loads.
96 */
97 UberJarRealmClassLoader( DefaultClassRealm realm )
98 {
99 super( realm );
100
101 this.urls = new ArrayList();
102
103 this.classIndex = new HashMap();
104
105 this.jarIndexes = new HashMap();
106 }
107
108 // ------------------------------------------------------------
109 // Instance methods
110 // ------------------------------------------------------------
111
112 /*** Retrieve the realm.
113 *
114 * @return The realm.
115 */
116 DefaultClassRealm getRealm()
117 {
118 return this.realm;
119 }
120
121 /*** Add a constituent to this realm for locating classes.
122 *
123 * @param constituent URL to contituent jar or directory.
124 */
125 void addConstituent( URL constituent )
126 {
127 // If the constituent is a jar, build an index for it.
128 if ( "jar".equals( constituent.getProtocol() )
129 || constituent.toExternalForm().endsWith( ".jar" ) )
130 {
131 buildIndexForJar( constituent );
132 }
133
134 // Add the constituent to the urls collection
135
136 this.urls.add( constituent );
137
138 super.addConstituent( constituent );
139 }
140
141 /***
142 * Builds an index for an incoming jar.
143 *
144 * @param inUrl
145 */
146 private void buildIndexForJar( URL inUrl )
147 {
148 HashMap index = new HashMap();
149
150 String urlText = null;
151
152 if ( inUrl.getProtocol().equals( "jar" ) )
153 {
154 urlText = inUrl.toExternalForm();
155 }
156 else
157 {
158 urlText = "jar:" + inUrl.toExternalForm();
159 }
160
161 String resourceName;
162 URL resourceUrl = null;
163
164 try
165 {
166 JarInputStream in = new JarInputStream( inUrl.openStream() );
167
168 try
169 {
170 JarEntry entry = null;
171
172 while ( ( entry = in.getNextJarEntry() ) != null )
173 {
174 resourceName = entry.getName();
175
176 resourceUrl = new URL( urlText + "!/" + resourceName );
177
178 index.put( resourceName, resourceUrl );
179 }
180 }
181 finally
182 {
183 in.close();
184 }
185 }
186 catch ( IOException e )
187 {
188 // swallow
189 }
190
191 jarIndexes.put( inUrl, index );
192 }
193
194 /*** Load a class directly from this classloader without
195 * defering through any other <code>ClassRealm</code>.
196 *
197 * @param className The name of the class to load.
198 *
199 * @return The loaded class.
200 *
201 * @throws ClassNotFoundException If the class could not be found.
202 */
203 Class loadClassDirect( String className ) throws ClassNotFoundException
204 {
205 String classPath = className.replace( '.',
206 '/' ) + ".class";
207
208 if ( this.classIndex.containsKey( classPath ) )
209 {
210 return (Class) this.classIndex.get( classPath );
211 }
212
213 Iterator urlIter = this.urls.iterator();
214 URL eachUrl = null;
215
216 byte[] classBytes = null;
217
218 while ( ( classBytes == null )
219 &&
220 ( urlIter.hasNext() ) )
221 {
222 eachUrl = (URL) urlIter.next();
223
224 if ( "jar".equals( eachUrl.getProtocol() )
225 ||
226 eachUrl.toExternalForm().endsWith( ".jar" ) )
227 {
228 classBytes = findClassInJarStream( eachUrl,
229 classPath );
230 }
231 else
232 {
233 classBytes = findClassInDirectoryUrl( eachUrl,
234 classPath );
235 }
236 }
237
238 if ( classBytes == null )
239 {
240 return super.loadClassDirect( className );
241 }
242 else
243 {
244 Class cls = defineClass( className,
245 classBytes,
246 0,
247 classBytes.length );
248
249 this.classIndex.put( classPath,
250 cls );
251
252 return cls;
253 }
254
255 // return super.loadClassDirect( name );
256 }
257
258 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
259 // java.lang.ClassLoader
260 // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
261
262 /*** @see ClassLoader
263 */
264 public URL getResource( String name )
265 {
266 return getRealm().getResource( name );
267 }
268
269 /*** @see ClassLoader
270 */
271 public URL findResource( String name )
272 {
273 URL resourceUrl = null;
274
275 Iterator urlIter = this.urls.iterator();
276 URL eachUrl = null;
277
278 while ( urlIter.hasNext() )
279 {
280 eachUrl = (URL) urlIter.next();
281
282 if ( "jar".equals( eachUrl.getProtocol() )
283 ||
284 eachUrl.toExternalForm().endsWith( ".jar" ) )
285 {
286 resourceUrl = findResourceInJarStream( eachUrl,
287 name );
288 }
289 else
290 {
291 resourceUrl = findResourceInDirectoryUrl( eachUrl,
292 name );
293 }
294
295 if ( resourceUrl != null )
296 {
297 return resourceUrl;
298 }
299 }
300
301 return null;
302 }
303
304 /*** Find a resource that potentially exists within a JAR stream.
305 *
306 * @param inUrl The jar stream URL.
307 * @param path The resource path to find.
308 *
309 * @return The resource URL or <code>null</code> if none found.
310 */
311 protected URL findResourceInJarStream( URL inUrl,
312 String path )
313 {
314 return (URL) ( (Map) jarIndexes.get( inUrl ) ).get( path );
315 }
316
317 /*** Find a resource that potentially exists within a directory.
318 *
319 * @param inUrl The directory URL.
320 * @param path The resource path to find.
321 *
322 * @return The resource URL or <code>null</code> if none found.
323 */
324 protected URL findResourceInDirectoryUrl( URL inUrl,
325 String path )
326 {
327 return null;
328 }
329
330 /*** Attempt to load the bytes of a class from a JAR <code>URL</code>.
331 *
332 * @param inUrl The base url.
333 * @param path The path to the desired class.
334 *
335 * @return The class bytes or <code>null</code> if not available
336 * via the base url.
337 */
338 protected byte[] findClassInJarStream( URL inUrl,
339 String path )
340 {
341 URL classUrl = (URL) ( (Map) jarIndexes.get( inUrl ) ).get( path );
342
343 if ( classUrl != null )
344 {
345 try
346 {
347 return readStream( classUrl.openStream() );
348 }
349 catch ( IOException e )
350 {
351 // Swallow
352 }
353 }
354
355 return null;
356 }
357
358 /*** Attempt to load the bytes of a class from a directory <code>URL</code>.
359 *
360 * @param url The directory url.
361 * @param path The path to the desired class.
362 *
363 * @return The class bytes or <code>null</code> if not available
364 * via the base url.
365 */
366 protected byte[] findClassInDirectoryUrl( URL url,
367 String path )
368 {
369 try
370 {
371 URL classUrl = new URL( url,
372 path );
373 }
374 catch ( IOException e )
375 {
376 // swallow
377 }
378
379 return null;
380 }
381
382 /*** Load a class.
383 *
384 * @param name The name of the class to load.
385 * @param resolve If <code>true</code> then resolve the class.
386 *
387 * @return The loaded class.
388 *
389 * @throws ClassNotFoundException If the class cannot be found.
390 */
391 protected Class loadClass( String name,
392 boolean resolve ) throws ClassNotFoundException
393 {
394 return getRealm().loadClass( name );
395 }
396
397 /***
398 * Read the contents of the provided input stream and return the contents
399 * as a byte array.
400 */
401 private byte[] readStream( InputStream in ) throws IOException
402 {
403 ByteArrayOutputStream out = new ByteArrayOutputStream();
404
405 try
406 {
407 byte[] buffer = new byte[2048];
408
409 int read = 0;
410
411 while ( in.available() > 0 )
412 {
413 read = in.read( buffer, 0, buffer.length );
414
415 if ( read < 0 )
416 {
417 break;
418 }
419
420 out.write( buffer, 0, read );
421 }
422
423 return out.toByteArray();
424 }
425 finally
426 {
427 out.close();
428 }
429 }
430 }
This page was automatically generated by Maven