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