1 /*****************************************************************************/ 2 /* Software Testing Automation Framework (STAF) */ 3 /* (C) Copyright IBM Corp. 2001 */ 4 /* */ 5 /* This software is licensed under the Eclipse Public License (EPL) V1.0. */ 6 /*****************************************************************************/ 7 8 package com.ibm.staf.service; 9 10 import com.ibm.staf.*; 11 import java.util.jar.*; 12 import java.util.jar.Attributes.*; 13 import java.io.IOException; 14 import java.io.InputStream; 15 import java.util.List; 16 import java.util.ArrayList; 17 import java.util.Enumeration; 18 import java.util.Map; 19 import java.util.HashMap; 20 import java.util.StringTokenizer; 21 import java.util.Iterator; 22 import java.util.Vector; 23 import java.io.FileOutputStream; 24 import java.net.URL; 25 26 27 public class STAFServiceJarClassLoader extends ClassLoader 28 { 29 static public final boolean DEBUG = false; 30 STAFServiceJarClassLoader(JarFile jarFile, String jarLoc)31 STAFServiceJarClassLoader(JarFile jarFile, String jarLoc) 32 { 33 this.jarFile = jarFile; 34 35 manifest = null; 36 37 try 38 { 39 manifest = jarFile.getManifest(); 40 } 41 catch (IOException e) 42 { 43 // If we can't get the manifest, that's ok. We shouldn't actually 44 // get here, as STAFServiceHelper will generate an error before 45 // instantiating us. 46 } 47 48 if (manifest == null) return; 49 50 Attributes attrs; 51 52 if (manifest.getEntries().containsKey(STAFServiceHelper.STAF_ENTRY3)) 53 { 54 attrs = manifest.getAttributes(STAFServiceHelper.STAF_ENTRY3); 55 } 56 else if (manifest.getEntries().containsKey(STAFServiceHelper.STAF_ENTRY)) 57 { 58 attrs = manifest.getAttributes(STAFServiceHelper.STAF_ENTRY); 59 } 60 else 61 { 62 return; 63 } 64 65 if (!attrs.containsKey(new Attributes.Name( 66 STAFServiceHelper.STAF_SERVICE_JARS))) 67 { 68 return; 69 } 70 71 String theJars = attrs.getValue(STAFServiceHelper.STAF_SERVICE_JARS); 72 StringTokenizer jarTokenizer = new StringTokenizer(theJars); 73 74 while (jarTokenizer.hasMoreTokens()) 75 { 76 String thisJar = jarTokenizer.nextToken(); 77 78 if (!thisJar.toLowerCase().endsWith(".jar")) 79 thisJar = thisJar + ".jar"; 80 81 JarEntry thisEntry = jarFile.getJarEntry("STAF-INF/jars/" + 82 thisJar); 83 84 if (thisEntry != null) 85 { 86 try 87 { 88 InputStream jarStream = jarFile.getInputStream(thisEntry); 89 byte [] buffer = new byte[1024]; 90 FileOutputStream fileStream = 91 new FileOutputStream(jarLoc + "/" + thisJar); 92 93 for (int bytesRead = 0; bytesRead != -1;) 94 { 95 bytesRead = jarStream.read(buffer, 0, 1024); 96 97 if (bytesRead != -1) 98 fileStream.write(buffer, 0, bytesRead); 99 } 100 101 fileStream.close(); 102 jarStream.close(); 103 104 JarFile thisJarFile = new JarFile(jarLoc + "/" + thisJar); 105 106 subordinateJars.add(thisJarFile); 107 } 108 catch (IOException e) 109 { 110 if (DEBUG) 111 { 112 System.out.println("Caught IOException processing " + 113 "subordinate jar file:"); 114 e.printStackTrace(); 115 } 116 } 117 } 118 } 119 } 120 findClass(String name)121 public Class findClass(String name) throws ClassNotFoundException 122 { 123 if (DEBUG) System.out.println("Finding class: " + name); 124 125 Class theClass = null; 126 127 try 128 { 129 JarEntry classEntry = jarFile.getJarEntry("STAF-INF/classes/" + 130 name.replace('.', '/') + 131 ".class"); 132 if (classEntry != null) 133 { 134 InputStream classStream = jarFile.getInputStream(classEntry); 135 byte [] classData = new byte[(int)classEntry.getSize()]; 136 int bytesToRead = classData.length; 137 138 for (int bytesRead = 0; bytesToRead > 0; 139 bytesToRead -= bytesRead) 140 { 141 bytesRead = classStream.read(classData, 142 classData.length - bytesToRead, 143 bytesToRead); 144 } 145 146 classStream.close(); 147 148 definePackage(name, manifest); 149 150 theClass = defineClass(name, classData, 0, classData.length); 151 } 152 } 153 catch (IOException e) 154 { 155 if (DEBUG) 156 { 157 System.out.println("Caught IOException reading from " + 158 "master jar file:"); 159 e.printStackTrace(); 160 } 161 } 162 163 Iterator iter = subordinateJars.iterator(); 164 165 while ((theClass == null) && iter.hasNext()) 166 { 167 JarFile subJarFile = (JarFile)iter.next(); 168 169 if (DEBUG) 170 { 171 System.out.println("Trying subordinate jar: " + 172 subJarFile.getName()); 173 } 174 175 Manifest subJarManifest = null; 176 177 try 178 { 179 subJarManifest = subJarFile.getManifest(); 180 } 181 catch (IOException e) 182 { 183 // If we can't get the manifest, that's ok. 184 } 185 186 try 187 { 188 JarEntry classEntry = subJarFile.getJarEntry( 189 name.replace('.', '/') + ".class"); 190 191 if (classEntry != null) 192 { 193 InputStream classStream = 194 subJarFile.getInputStream(classEntry); 195 byte [] classData = new byte[(int)classEntry.getSize()]; 196 int bytesToRead = classData.length; 197 198 for (int bytesRead = 0; bytesToRead > 0; 199 bytesToRead -= bytesRead) 200 { 201 bytesRead = classStream.read(classData, 202 classData.length - 203 bytesToRead, 204 bytesToRead); 205 } 206 207 classStream.close(); 208 209 definePackage(name, subJarManifest); 210 211 theClass = defineClass(name, classData, 0, classData.length); 212 } 213 } 214 catch (IOException e) 215 { 216 if (DEBUG) 217 { 218 System.out.println("Caught IOException reading from " + 219 "subordinate jar file:"); 220 e.printStackTrace(); 221 } 222 } 223 } 224 225 if (theClass == null) throw new ClassNotFoundException(name); 226 227 return theClass; 228 } 229 230 /** 231 * Finds the resource with the given name. Class loader implementations 232 * should override this method to specify where to find resources. 233 * 234 * @param name the resource name 235 * 236 * @return a URL for reading the resource, or null if the resource could 237 * not be found 238 */ findResource(String name)239 protected URL findResource(String name) 240 { 241 if (DEBUG) System.out.println("Finding Resource: " + name); 242 243 JarEntry propEntry = jarFile.getJarEntry("STAF-INF/classes/" + name); 244 245 if (propEntry != null) 246 { 247 try 248 { 249 return new URL("jar:file:///" + 250 jarFile.getName().replace('\\', '/') + 251 "!/STAF-INF/classes/" + name); 252 } 253 catch (java.net.MalformedURLException e) 254 { 255 // Ignore this 256 } 257 } 258 259 Iterator iter = subordinateJars.iterator(); 260 261 while (iter.hasNext()) 262 { 263 JarFile subJarFile = (JarFile)iter.next(); 264 265 if (DEBUG) 266 { 267 System.out.println("Trying subordinate jar: " + 268 subJarFile.getName()); 269 } 270 271 propEntry = subJarFile.getJarEntry(name); 272 273 if (propEntry != null) 274 { 275 try 276 { 277 return new URL("jar:file:///" + 278 subJarFile.getName().replace('\\', '/') + 279 "!/" + name); 280 } 281 catch (java.net.MalformedURLException e) 282 { 283 // Ignore this 284 } 285 } 286 } 287 288 return null; 289 } 290 findResources(String name)291 protected Enumeration findResources(String name) 292 { 293 if (DEBUG) System.out.println("Finding Resource: " + name); 294 295 Vector resources = new Vector(); 296 297 JarEntry propEntry = jarFile.getJarEntry("STAF-INF/classes/" + name); 298 299 if (propEntry != null) 300 { 301 try 302 { 303 resources.add(new URL("jar:file:///" + 304 jarFile.getName().replace('\\', '/') + 305 "!/STAF-INF/classes/" + name)); 306 } 307 catch (java.net.MalformedURLException e) 308 { 309 // Ignore this 310 } 311 } 312 313 Iterator iter = subordinateJars.iterator(); 314 315 while (iter.hasNext()) 316 { 317 JarFile subJarFile = (JarFile)iter.next(); 318 319 if (DEBUG) 320 { 321 System.out.println("Trying subordinate jar: " + 322 subJarFile.getName()); 323 } 324 325 propEntry = subJarFile.getJarEntry(name); 326 327 if (propEntry != null) 328 { 329 try 330 { 331 resources.add(new URL("jar:file:///" + 332 subJarFile.getName().replace('\\', '/') + 333 "!/" + name)); 334 } 335 catch (java.net.MalformedURLException e) 336 { 337 // Ignore this 338 } 339 } 340 } 341 342 return resources.elements(); 343 } 344 345 /** 346 * Defines a Package object associated with the given class name if it has 347 * not already been defined. The manifest indicates the title, version and 348 * vendor information of the specification and implementation, and 349 * whether the package is sealed. 350 * 351 * @param className a class name 352 * @param manifest the Manifest for the jar file 353 */ definePackage(String className, Manifest manifest)354 private void definePackage(String className, Manifest manifest) 355 { 356 // Get the package name from the class name 357 358 int lastDotIndex = className.lastIndexOf('.'); 359 360 if (lastDotIndex == -1) 361 return; // The class is not in a package 362 363 String packageName = className.substring(0, lastDotIndex); 364 365 if (getPackage(packageName) != null) 366 return; // The package has already been defined 367 368 // Need a manifest to get the package information 369 370 if (manifest == null) 371 { 372 if (DEBUG) 373 System.out.println("Cannot define package " + packageName + 374 " because manifest is null"); 375 376 definePackage(packageName, null, null, null, 377 null, null, null, null); 378 return; 379 } 380 381 // First, read the manifest to get the attributes for the 382 // package, if any 383 384 String packagePath = packageName.replace('.', '/').concat("/"); 385 String sealed = null; 386 387 Attributes attrs = manifest.getAttributes(packagePath); 388 389 if (attrs != null) 390 { 391 specTitle = attrs.getValue(Name.SPECIFICATION_TITLE); 392 specVersion = attrs.getValue(Name.SPECIFICATION_VERSION); 393 specVendor = attrs.getValue(Name.SPECIFICATION_VENDOR); 394 implTitle = attrs.getValue(Name.IMPLEMENTATION_TITLE); 395 implVersion = attrs.getValue(Name.IMPLEMENTATION_VERSION); 396 implVendor = attrs.getValue(Name.IMPLEMENTATION_VENDOR); 397 sealed = attrs.getValue(Name.SEALED); 398 } 399 400 // Second, read the manifest to get the main attributes, and 401 // assign their value if not overridden by a package attribute 402 403 attrs = manifest.getMainAttributes(); 404 405 if (attrs != null) 406 { 407 if (specTitle == null) 408 specTitle = attrs.getValue(Name.SPECIFICATION_TITLE); 409 410 if (specVersion == null) 411 specVersion = attrs.getValue(Name.SPECIFICATION_VERSION); 412 413 if (specVendor == null) 414 specVendor = attrs.getValue(Name.SPECIFICATION_VENDOR); 415 416 if (implTitle == null) 417 implTitle = attrs.getValue(Name.IMPLEMENTATION_TITLE); 418 419 if (implVersion == null) 420 implVersion = attrs.getValue(Name.IMPLEMENTATION_VERSION); 421 422 if (implVendor == null) 423 implVendor = attrs.getValue(Name.IMPLEMENTATION_VENDOR); 424 if (sealed == null) 425 sealed = attrs.getValue(Name.SEALED); 426 } 427 428 URL sealBase = null; // No sealing 429 430 /* XXX: How do we get the codeSourceURL to assign to sealBase if the 431 package is sealed? 432 433 if ((sealed != null) && sealed.equalsIgnoreCase("true")) 434 { 435 // Package is sealed 436 sealBase = codeSourceURL; 437 } 438 */ 439 440 definePackage(packageName, specTitle, specVersion, specVendor, 441 implTitle, implVersion, implVendor, sealBase); 442 443 if (DEBUG) 444 { 445 System.out.println( 446 "Defined package " + packageName + " with" + 447 " specTitle=" + specTitle + " specVersion=" + specVersion + 448 " specVendor=" + specVendor + " implTitle=" + implTitle + 449 " implVersion=" + implVersion + " implVendor=" + implVendor + 450 " sealBase=" + sealBase); 451 } 452 } 453 454 JarFile jarFile = null; 455 List subordinateJars = new ArrayList(); 456 457 private Manifest manifest = null; 458 459 private String specTitle = null; 460 private String specVersion = null; 461 private String specVendor = null; 462 private String implTitle = null; 463 private String implVersion = null; 464 private String implVendor = null; 465 } 466 467