View Javadoc
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