KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > enterprise > loader > EJBClassLoader


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24
25 package com.sun.enterprise.loader;
26
27 import java.io.BufferedInputStream JavaDoc;
28 import java.io.ByteArrayOutputStream JavaDoc;
29 import java.io.File JavaDoc;
30 import java.io.FileInputStream JavaDoc;
31 import java.io.FilterInputStream JavaDoc;
32 import java.io.IOException JavaDoc;
33 import java.io.InputStream JavaDoc;
34
35 import java.lang.instrument.IllegalClassFormatException JavaDoc;
36 import java.net.JarURLConnection JavaDoc;
37
38 import java.net.MalformedURLException JavaDoc;
39 import java.net.URI JavaDoc;
40 import java.net.URISyntaxException JavaDoc;
41 import java.net.URL JavaDoc;
42 import java.net.URLClassLoader JavaDoc;
43 import java.net.URLConnection JavaDoc;
44 import java.net.URLStreamHandler JavaDoc;
45 import java.security.AccessController JavaDoc;
46 import java.security.CodeSource JavaDoc;
47 import java.security.PrivilegedAction JavaDoc;
48 import java.security.PrivilegedActionException JavaDoc;
49 import java.security.PrivilegedExceptionAction JavaDoc;
50 import java.security.ProtectionDomain JavaDoc;
51 import java.security.SecureClassLoader JavaDoc;
52 import java.security.cert.Certificate JavaDoc;
53 import java.text.MessageFormat JavaDoc;
54 import java.util.ArrayList JavaDoc;
55 import java.util.Collections JavaDoc;
56 import java.util.Date JavaDoc;
57 import java.util.HashMap JavaDoc;
58 import java.util.Hashtable JavaDoc;
59 import java.util.List JavaDoc;
60 import java.util.Map JavaDoc;
61 import java.util.StringTokenizer JavaDoc;
62 import java.util.jar.Attributes JavaDoc;
63 import java.util.jar.JarEntry JavaDoc;
64 import java.util.jar.JarFile JavaDoc;
65 import java.util.jar.Manifest JavaDoc;
66 import java.util.logging.Level JavaDoc;
67 import java.util.logging.Logger JavaDoc;
68 import java.util.Vector JavaDoc;
69 import java.util.Enumeration JavaDoc;
70 import java.util.Iterator JavaDoc;
71
72 import com.sun.appserv.server.util.PreprocessorUtil;
73 import com.sun.enterprise.util.Print;
74 import com.sun.logging.LogDomains;
75 import com.sun.enterprise.util.i18n.StringManager;
76
77 import javax.persistence.spi.ClassTransformer;
78 import java.util.zip.ZipEntry JavaDoc;
79
80 /**
81  * Class loader used by the ejbs of an application or stand alone module.
82  *
83  * This class loader also keeps cache of not found classes and resources.
84  * </xmp>
85  *
86  * @author Nazrul Islam
87  * @author Kenneth Saks
88  * @author Sivakumar Thyagarajan
89  * @since JDK 1.4
90  */

91 public class EJBClassLoader
92         extends URLClassLoader JavaDoc
93         implements JasperAdapter, InstrumentableClassLoader {
94
95     /** logger for this class */
96     static Logger JavaDoc _logger=LogDomains.getLogger(LogDomains.LOADER_LOGGER);
97
98     /** list of url entries of this class loader */
99     private List JavaDoc<URLEntry> urlSet = Collections.synchronizedList(new ArrayList JavaDoc());
100
101     /** cache of not found resources */
102     private Map JavaDoc notFoundResources = new HashMap JavaDoc();
103
104     /** cache of not found classes */
105     private Map JavaDoc notFoundClasses = new HashMap JavaDoc();
106
107     /** state flag to track whether this instance has been shut off. */
108     private boolean doneCalled = false;
109     /** snapshot of classloader state at the time done was called */
110     private String JavaDoc doneSnapshot;
111
112     /** streams opened by this loader */
113     private Vector JavaDoc<SentinelInputStream> streams = null;
114
115     private ArrayList JavaDoc<ClassTransformer> transformers =
116             new ArrayList JavaDoc<ClassTransformer>(1);
117
118     private static StringManager sm =
119         StringManager.getManager(EJBClassLoader.class);
120
121     /**
122      * Constructor.
123      */

124     public EJBClassLoader() {
125         super(new URL JavaDoc[0]);
126
127         if (_logger.isLoggable(Level.FINE)) {
128             _logger.log(Level.FINE,
129                         "ClassLoader: " + this + " is getting created.");
130         }
131     }
132
133     /**
134      * Constructor.
135      *
136      * @param parent parent class loader
137      */

138     public EJBClassLoader(ClassLoader JavaDoc parent) {
139         super(new URL JavaDoc[0], parent);
140     }
141
142     public boolean isDone() {
143         return doneCalled;
144     }
145
146     /**
147      * This method should be called to free up the resources.
148      * It helps garbage collection.
149      */

150     public void done() {
151
152         if( doneCalled ) {
153             return;
154         }
155
156         // Capture the fact that the classloader is now effectively disabled.
157
// First create a snapshot of our state. This should be called
158
// before setting doneCalled = true.
159
doneSnapshot = "EJBClassLoader.done() called ON " + this.toString()
160             + "\n AT " + new Date JavaDoc() +
161             " \n BY :" + Print.printStackTraceToString();
162         doneCalled = true;
163
164         // closes the jar handles and sets the url entries to null
165
int i = 0;
166         while (i < this.urlSet.size()) {
167             URLEntry u = (URLEntry) this.urlSet.get(i);
168             if (u.zip != null) {
169                 try {
170                     u.zip.reallyClose();
171                 } catch (IOException JavaDoc ioe) {
172                     _logger.log(Level.INFO, formatMsg("loader.ejbclassloader_exc_closing_URLEntry", u.source),
173                                 ioe);
174                 }
175             }
176             if (u.table != null) {
177                 u.table.clear();
178                 u.table = null;
179             }
180             u = null;
181             i++;
182         }
183
184         closeOpenStreams();
185
186         // clears out the tables
187
if (this.urlSet != null) { this.urlSet.clear(); }
188         if (this.notFoundResources != null) { this.notFoundResources.clear(); }
189         if (this.notFoundClasses != null) { this.notFoundClasses.clear(); }
190
191         // sets all the objects to null
192
this.urlSet = null;
193         this.notFoundResources = null;
194         this.notFoundClasses = null;
195     }
196
197
198     /**
199      * Adds a URL to the search list, based on the specified File.
200      * <p>
201      * This variant of the method makes sure that the URL is valid, in particular
202      * encoding special characters (such as blanks) in the file path.
203      * @param file the File to use in creating the URL
204      * @throws IOException in case of errors converting the file to a URL
205      */

206     public synchronized void appendURL(File JavaDoc file) throws IOException JavaDoc {
207         try {
208             appendURL(file.toURI().toURL());
209         } catch (MalformedURLException JavaDoc mue) {
210             _logger.log(Level.SEVERE,
211                 "loader.ejbclassloader_bad_url_entry", file.toURI());
212
213             _logger.log(Level.SEVERE,
214                 "loader.ejbclassloader_malformed_url", mue);
215             IOException JavaDoc ioe = new IOException JavaDoc();
216             ioe.initCause(mue);
217             throw ioe;
218         }
219     }
220
221
222     /**
223      * Appends the specified URL to the list of URLs to search for
224      * classes and resources.
225      *
226      * @param url the URL to be added to the search path of URLs
227      */

228     public void addURL(URL JavaDoc url) {
229         appendURL(url);
230     }
231
232
233     /**
234      * Add a url to the list of urls we search for a class's bytecodes.
235      *
236      * @param url url to be added
237      */

238     public synchronized void appendURL(URL JavaDoc url) {
239
240         try {
241             if (url == null) {
242                 _logger.log(Level.INFO,
243                     "loader.ejbclassloader_bad_url_entry", url);
244                 return;
245             }
246
247             URLEntry entry = new URLEntry(url);
248
249             if ( !urlSet.contains(entry) ) {
250                 entry.init();
251                 // adds the url entry to the list
252
this.urlSet.add(entry);
253
254                 if (entry.isJar) {
255                     // checks the manifest if a jar
256
checkManifest(entry.zip, entry.file);
257                 }
258             } else {
259                 _logger.log(Level.FINE,
260                     "[EJB-CL] Ignoring duplicate URL: " + url);
261                 /*
262                  *Clean up the unused entry or it could hold open a jar file.
263                  */

264                 if (entry.zip != null) {
265                     try {
266                         entry.zip.reallyClose();
267                     } catch (IOException JavaDoc ioe) {
268                     _logger.log(Level.INFO, formatMsg("loader.ejbclassloader_exc_closing_dup_URLEntry", url),
269                                 ioe);
270                     }
271                 }
272             }
273
274             // clears the "not found" cache since we are adding a new url
275
clearNotFoundCaches();
276
277         } catch (IOException JavaDoc ioe) {
278
279             _logger.log(Level.SEVERE,
280                 "loader.ejbclassloader_bad_url_entry", url);
281
282             _logger.log(Level.SEVERE,
283                 "loader.ejbclassloader_malformed_url", ioe);
284         }
285     }
286
287     /**
288      * Returns the urls of this class loader.
289      *
290      * @return the urls of this class loader or an empty array
291      */

292     public synchronized URL JavaDoc[] getURLs() {
293
294         URL JavaDoc[] url = null;
295
296         if (this.urlSet != null) {
297             url = new URL JavaDoc[this.urlSet.size()];
298
299             for (int i=0; i<url.length; i++) {
300                 url[i] = ((URLEntry)this.urlSet.get(i)).source;
301             }
302         } else {
303             url = new URL JavaDoc[0];
304         }
305
306         return url;
307     }
308
309     /**
310      * Returns all the "file" protocol resources of this EJBClassLoader,
311      * concatenated to a classpath string.
312      *
313      * Notice that this method is called by the setClassPath() method of
314      * org.apache.catalina.loader.WebappLoader, since this EJBClassLoader does
315      * not extend off of URLClassLoader.
316      *
317      * @return Classpath string containing all the "file" protocol resources
318      * of this EJBClassLoader
319      */

320     public String JavaDoc getClasspath() {
321
322         StringBuffer JavaDoc strBuf = null;
323
324         URL JavaDoc[] urls = getURLs();
325         if (urls != null) {
326             for (int i=0; i<urls.length; i++) {
327                 if (urls[i].getProtocol().equals("file")) {
328                     if (strBuf == null) {
329                         strBuf = new StringBuffer JavaDoc();
330                     }
331                     if (i > 0) {
332                         strBuf.append(File.pathSeparator);
333                     }
334                     strBuf.append(urls[i].getFile());
335                 }
336             }
337         }
338
339         return (strBuf != null) ? strBuf.toString() : null;
340     }
341
342     /**
343      *Refreshes the memory of the class loader. This involves clearing the
344      *not-found cahces and recreating the hash tables for the URLEntries that
345      *record the files accessible for each.
346      *<p>
347      *Code that creates an EJBClassLoader and then adds files to a directory
348      *that is in the loader's classpath should invoke this method after the new
349      *file(s) have been added in order to update the class loader's data
350      *structures which optimize class and resource searches.
351      *@throws IOException in case of errors refreshing the cache
352      */

353     public synchronized void refresh() throws IOException JavaDoc {
354         clearNotFoundCaches();
355 // for (URLEntry entry : urlSet) {
356
// entry.cacheItems();
357
// }
358
}
359
360     public synchronized void addTransformer(ClassTransformer transformer) {
361         transformers.add(transformer);
362     }
363
364     /**
365      * Create a new instance of a sibling classloader
366      * @return a new instance of a class loader that has the same visibility
367      * as this class loader
368      */

369     public ClassLoader JavaDoc copy() {
370         return new DelegatingClassLoader(this);
371     }
372
373     /**
374      *Erases the memory of classes and resources that have been searched for
375      *but not found.
376      */

377      private void clearNotFoundCaches() {
378         this.notFoundResources.clear();
379         this.notFoundClasses.clear();
380     }
381
382     /**
383      * Internal implementation of find resource.
384      *
385      * @param res url resource entry
386      * @param name name of the resource
387      */

388     private URL JavaDoc findResource0(final URLEntry res,
389                               final String JavaDoc name) {
390
391         Object JavaDoc result =
392         AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
393             public Object JavaDoc run() {
394
395                 if (res.isJar) {
396
397                     try {
398                         JarEntry JavaDoc jarEntry = res.zip.getJarEntry(name);
399                         if (jarEntry != null) {
400                             /*
401                              *Use a custom URL with a special stream handler to
402                              *prevent the JDK's JarURLConnection caching from
403                              *locking the jar file until JVM exit.
404                              */

405                             InternalURLStreamHandler handler = new InternalURLStreamHandler(res, name);
406                             URI JavaDoc uri = new URI JavaDoc("jar", res.source + "!/" + name, null /* fragment */);
407                             URL JavaDoc ret = new URL JavaDoc(uri.toURL(), "" /* spec */, handler);
408                             handler.tieUrl(ret);
409                             return ret;
410                         }
411
412                     } catch (Throwable JavaDoc thr) {
413                         _logger.log(Level.INFO,
414                                     "loader.excep_in_ejbclassloader",thr);
415                     }
416                 } else { // directory
417
try {
418                         File JavaDoc resourceFile =
419                             new File JavaDoc(res.file.getCanonicalPath()
420                                         + File.separator + name);
421
422                         if (resourceFile.exists()) {
423                             // If we make it this far,
424
// the resource is in the directory.
425
return resourceFile.toURL();
426                         }
427
428                     } catch (IOException JavaDoc e) {
429                         _logger.log(Level.INFO,
430                                     "loader.excep_in_ejbclassloader",e);
431                     }
432                 }
433
434                 return null;
435
436             } // End for -- each URL in classpath.
437
});
438
439         return (URL JavaDoc) result;
440     }
441
442     public URL JavaDoc findResource(String JavaDoc name) {
443
444         if( doneCalled ) {
445             _logger.log(Level.WARNING,
446                     formatMsg("loader.ejbclassloader_find_resource_after_done", name, this.toString()),
447                     new Throwable JavaDoc());
448             return null;
449         }
450
451         // resource is in the not found list
452
String JavaDoc nf = (String JavaDoc) notFoundResources.get(name);
453         if (nf != null && nf.equals(name) ) {
454             return null;
455         }
456
457         int i = 0;
458         while (i < this.urlSet.size()) {
459
460             URLEntry u = (URLEntry) this.urlSet.get(i);
461
462             if (!u.hasItem(name)) {
463                 i++;
464                 continue;
465             }
466
467             URL JavaDoc url = findResource0(u, name);
468             if (url != null) return url;
469             i++;
470         }
471
472         // add resource to the not found list
473
notFoundResources.put(name, name);
474
475         return null;
476     }
477
478     /**
479      * Returns an enumeration of java.net.URL objects
480      * representing all the resources with the given name.
481      */

482     public Enumeration JavaDoc<URL JavaDoc> findResources(String JavaDoc name) throws IOException JavaDoc {
483         if( doneCalled ) {
484             _logger.log(Level.WARNING,
485                         "loader.ejbclassloader_done_already_called",
486                         new Object JavaDoc[] { name, doneSnapshot });
487             return null;
488         }
489
490         // resource is in the not found list
491
String JavaDoc nf = (String JavaDoc) notFoundResources.get(name);
492         if (nf != null && nf.equals(name) ) {
493             return null;
494         }
495
496         List JavaDoc<URL JavaDoc> resourcesList = new ArrayList JavaDoc<URL JavaDoc>();
497
498         for (Iterator JavaDoc iter = this.urlSet.iterator(); iter.hasNext();) {
499                         URLEntry urlEntry = (URLEntry) iter.next();
500             URL JavaDoc url = findResource0(urlEntry, name);
501             if (url != null) {
502                 resourcesList.add(url);
503             }
504                 }
505
506         if (resourcesList.size() == 0) {
507             // add resource to the not found list
508
notFoundResources.put(name, name);
509         }
510
511         return (new Vector JavaDoc(resourcesList)).elements();
512     }
513
514
515
516     /**
517      * Checks the manifest of the given jar file.
518      *
519      * @param jar the jar file that may contain manifest class path
520      * @param file file pointer to the jar
521      *
522      * @throws IOException if an i/o error
523      */

524     private void checkManifest(JarFile JavaDoc jar, File JavaDoc file) throws IOException JavaDoc {
525
526         if ( (jar == null) || (file == null) ) return;
527
528         Manifest JavaDoc man = jar.getManifest();
529         if (man == null) return;
530
531         synchronized (this) {
532             String JavaDoc cp = man.getMainAttributes().getValue(
533                                         Attributes.Name.CLASS_PATH);
534             if (cp == null) return;
535
536             StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(cp, " ");
537
538             while (st.hasMoreTokens()) {
539                 String JavaDoc entry = st.nextToken();
540
541                 File JavaDoc newFile = new File JavaDoc(file.getParentFile(), entry);
542
543                 // add to class path of this class loader
544
try {
545                     appendURL(newFile);
546                 } catch (MalformedURLException JavaDoc ex) {
547                     _logger.log(Level.SEVERE,
548                         "loader.ejbclassloader_malformed_url",ex);
549                 }
550             }
551         }
552     }
553
554     /**
555      * Internal implementation of load class.
556      *
557      * @param res url resource entry
558      * @param entryName name of the class
559      */

560     private byte[] loadClassData0(final URLEntry res, final String JavaDoc entryName) {
561
562         Object JavaDoc result =
563         AccessController.doPrivileged(new PrivilegedAction JavaDoc() {
564             public Object JavaDoc run() {
565                 InputStream JavaDoc classStream = null;
566                 try {
567
568                     if (res.isJar) { // It is a jarfile..
569
JarFile JavaDoc zip = res.zip;
570                         JarEntry JavaDoc entry = zip.getJarEntry(entryName);
571                         if (entry != null) {
572                             classStream = zip.getInputStream(entry);
573                             byte[] classData = getClassData(classStream);
574                             res.setProtectionDomain(EJBClassLoader.this, entry.getCertificates());
575                             return classData;
576                         }
577                     } else { // Its a directory....
578
File JavaDoc classFile = new File JavaDoc (res.file,
579                                     entryName.replace('/', File.separatorChar));
580
581                         if (classFile.exists()) {
582                             try {
583                                 classStream = new FileInputStream JavaDoc(classFile);
584                                 byte[] classData = getClassData(classStream);
585                                 res.setProtectionDomain(EJBClassLoader.this, null);
586                                 return classData;
587                             } finally {
588                                 /*
589                                  *Close the stream only if this is a directory. The stream for
590                                  *a jar/zip file was opened elsewhere and should remain open after this
591                                  *method completes.
592                                  */

593                                 if (classStream != null) {
594                                     try {
595                                         classStream.close();
596                                     } catch (IOException JavaDoc closeIOE) {
597                                         _logger.log(Level.INFO, "loader.excep_in_ejbclassloader", closeIOE);
598                                     }
599                                 }
600                             }
601                         }
602                     }
603                 } catch (IOException JavaDoc ioe) {
604                     _logger.log(Level.INFO,
605                                 "loader.excep_in_ejbclassloader", ioe);
606                 }
607                 return null;
608             }
609         });
610         return (byte[]) result;
611     }
612
613     protected Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
614         ClassData classData = findClassData(name);
615         // Instruments the classes if the profiler's enabled
616
if (PreprocessorUtil.isPreprocessorEnabled()) {
617             // search thru the JARs for a file of the form java/lang/Object.class
618
String JavaDoc entryName = name.replace('.', '/') + ".class";
619             classData.classBytes = PreprocessorUtil.processClass(entryName, classData.classBytes);
620         }
621
622         // Define package information if necessary
623
int lastPackageSep = name.lastIndexOf('.');
624         if ( lastPackageSep != -1 ) {
625             String JavaDoc packageName = name.substring(0, lastPackageSep);
626             if( getPackage(packageName) == null ) {
627                 try {
628
629                     // There's a small chance that one of our parents
630
// could define the same package after getPackage
631
// returns null but before we call definePackage,
632
// since the parent classloader instances
633
// are not locked. So, just catch the exception
634
// that is thrown in that case and ignore it.
635
//
636
// It's unclear where we would get the info to
637
// set all spec and impl data for the package,
638
// so just use null. This is consistent will the
639
// JDK code that does the same.
640
definePackage(packageName, null, null, null,
641                                   null, null, null, null);
642                 } catch(IllegalArgumentException JavaDoc iae) {
643                     // duplicate attempt to define same package.
644
// safe to ignore.
645
_logger.log(Level.FINE, "duplicate package " +
646                         "definition attempt for " + packageName, iae);
647                 }
648             }
649         }
650
651         // Loop though the transformers here!!
652
try {
653             ArrayList JavaDoc<ClassTransformer> xformers = (ArrayList JavaDoc<ClassTransformer>) transformers.clone();
654             for (ClassTransformer transformer : xformers) {
655
656                 // see javadocs of transform().
657
// It expects class name as java/lang/Object
658
// as opposed to java.lang.Object
659
String JavaDoc internalClassName = name.replace('.','/');
660                 byte[] transformedBytes = transformer.transform(this, internalClassName, null,
661                         classData.pd, classData.classBytes);
662                 if(transformedBytes!=null){ // null indicates no transformation
663
_logger.logp(Level.INFO, "EJBClassLoader",
664                             "findClass", "{0} actually got transformed",
665                             name);
666                     classData.classBytes = transformedBytes;
667                 }
668             }
669         } catch (IllegalClassFormatException JavaDoc icfEx) {
670             throw new ClassNotFoundException JavaDoc(icfEx.toString(), icfEx);
671         }
672         Class JavaDoc clazz = null;
673         try {
674             clazz = defineClass(name, classData.classBytes, 0, classData.classBytes.length, classData.pd);
675             return clazz;
676         } catch (UnsupportedClassVersionError JavaDoc ucve) {
677         throw new UnsupportedClassVersionError JavaDoc(
678             sm.getString("ejbClassLoader.unsupportedVersion", name,
679                          System.getProperty("java.version")));
680         }
681     }
682
683     /**
684      * This method is responsible for locating the url from the class bytes
685      * have to be read and reading the bytes. It does not actually define
686      * the Class object.
687      * @param name class name in java.lang.Object format
688      * @return class bytes as well protection domain information
689      * @throws ClassNotFoundException
690      */

691     protected ClassData findClassData(String JavaDoc name) throws ClassNotFoundException JavaDoc {
692
693         if( doneCalled ) {
694             _logger.log(Level.WARNING,
695                         formatMsg("loader.ejbclassloader_find_class_after_done", name, this.toString()), new Throwable JavaDoc());
696             throw new ClassNotFoundException JavaDoc(name);
697         }
698
699         String JavaDoc nf = (String JavaDoc) notFoundClasses.get(name);
700         if (nf != null && nf.equals(name) ) {
701             throw new ClassNotFoundException JavaDoc(name);
702         }
703
704         // search thru the JARs for a file of the form java/lang/Object.class
705
String JavaDoc entryName = name.replace('.', '/') + ".class";
706
707         int i = 0;
708         while (i < urlSet.size()) {
709             URLEntry u = (URLEntry) this.urlSet.get(i);
710
711             if (!u.hasItem(entryName)) {
712                 i++;
713                 continue;
714             }
715
716             byte[] result = loadClassData0(u, entryName);
717             if (result != null) return new ClassData(result, u.pd);
718             i++;
719         }
720
721         // add to the not found classes list
722
notFoundClasses.put(name, name);
723
724         throw new ClassNotFoundException JavaDoc(name);
725     }
726
727     /**
728      * Returns the byte array from the given input stream.
729      *
730      * @param istream input stream to the class or resource
731      *
732      * @throws IOException if an i/o error
733      */

734     private byte[] getClassData(InputStream JavaDoc istream) throws IOException JavaDoc {
735
736         BufferedInputStream JavaDoc bstream = new BufferedInputStream JavaDoc(istream);;
737         byte[] buf = new byte[4096];
738         ByteArrayOutputStream JavaDoc bout = new ByteArrayOutputStream JavaDoc();
739         int num = 0;
740         try {
741             while( (num = bstream.read(buf)) != -1) {
742                 bout.write(buf, 0, num);
743             }
744         } finally {
745             if (bstream != null) {
746                 try {
747                     bstream.close();
748                 } catch (IOException JavaDoc closeIOE) {
749                     EJBClassLoader._logger.log(Level.INFO, "loader.excep_in_ejbclassloader", closeIOE);
750                 }
751             }
752         }
753
754         return bout.toByteArray();
755     }
756
757     /**
758      * Returns a string representation of this class loader.
759      *
760      * @return a string representation of this class loader
761      */

762     public String JavaDoc toString() {
763
764         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
765
766         buffer.append("EJBClassLoader : \n");
767         if( doneCalled ) {
768             buffer.append("doneCalled = true" + "\n");
769             if( doneSnapshot != null ) {
770                 buffer.append("doneSnapshot = " + doneSnapshot);
771             }
772         } else {
773             buffer.append("urlSet = " + this.urlSet + "\n");
774             buffer.append("doneCalled = false " + "\n");
775         }
776         buffer.append(" Parent -> " + getParent() + "\n");
777
778         return buffer.toString();
779     }
780
781     public InputStream JavaDoc getResourceAsStream(final String JavaDoc name) {
782         InputStream JavaDoc stream = super.getResourceAsStream(name);
783         /*
784          *Make sure not to wrap the stream if it already is a wrapper.
785          */

786         if (stream != null) {
787             if (! (stream instanceof SentinelInputStream)) {
788                 stream = new SentinelInputStream(stream);
789             }
790         }
791         return stream;
792     }
793
794     /**
795      *Looks up the key in the logger's resource bundle and substitutes any
796      *arguments provided into the looked-up string.
797      *@param key the key to look up in the resource bundle
798      *@param args... optional arguments to plug into the string found in the bundle
799      *@return the formatted string
800      */

801     private static String JavaDoc formatMsg(String JavaDoc key, Object JavaDoc... args) {
802         String JavaDoc fmt = _logger.getResourceBundle().getString(key);
803         return MessageFormat.format(fmt, args);
804     }
805
806     /**
807      * The JarFile objects loaded in the classloader may get exposed to the
808      * application code (e.g. EJBs) through calls of
809      * ((JarURLConnection) getResource().openConnection()).getJarFile().
810      *
811      * This class protects the jar file from being closed by such an application.
812      *
813      * @author fkieviet
814      */

815     private static class ProtectedJarFile extends JarFile JavaDoc {
816         /**
817          * Constructor
818          *
819          * @param file File
820          * @throws IOException from parent
821          */

822         public ProtectedJarFile(File JavaDoc file) throws IOException JavaDoc {
823             super(file);
824         }
825
826         /**
827          * Do nothing
828          *
829          * @see java.util.zip.ZipFile#close()
830          */

831         public void close() {
832             // nothing
833
_logger.log(Level.WARNING, "Illegal call to close() detected", new Throwable JavaDoc());
834         }
835
836         /**
837          * Really close the jar file
838          *
839          * @throws IOException from parent
840          */

841         public void reallyClose() throws IOException JavaDoc {
842             super.close();
843         }
844
845         /**
846          * @see java.lang.Object#finalize()
847          */

848         protected void finalize() throws IOException JavaDoc {
849             reallyClose();
850         }
851     }
852
853     /**
854      * URL entry - keeps track of the url resources.
855      */

856     protected static class URLEntry {
857
858         /** the url */
859         URL JavaDoc source = null;
860
861         /** file of the url */
862         File JavaDoc file = null;
863
864         /** jar file if url is a jar else null */
865         ProtectedJarFile zip = null;
866
867         /** true if url is a jar */
868         boolean isJar = false;
869
870         Hashtable JavaDoc<String JavaDoc,String JavaDoc> table = null;
871
872         /** ProtectionDomain with signers if jar is signed */
873         ProtectionDomain JavaDoc pd = null;
874
875         URLEntry(URL JavaDoc url) {
876             source = url;
877         }
878
879         void init() throws IOException JavaDoc {
880             try {
881                 file = new File JavaDoc(source.toURI());
882                 isJar = file.isFile();
883
884                 if (isJar) {
885                     zip = new ProtectedJarFile(file);
886                 }
887
888                 table = new Hashtable JavaDoc<String JavaDoc,String JavaDoc>();
889 // cacheItems();
890
} catch (URISyntaxException JavaDoc use) {
891                 IOException JavaDoc ioe= new IOException JavaDoc();
892                 ioe.initCause(use);
893                 throw ioe;
894             }
895         }
896
897         private void cacheItems() throws IOException JavaDoc {
898             if (isJar) {
899                 // cache entry names from jar file
900
for (Enumeration JavaDoc e = zip.entries(); e.hasMoreElements(); ) {
901                     ZipEntry JavaDoc curEntry = (ZipEntry JavaDoc) e.nextElement();
902                     table.put(curEntry.getName(), curEntry.getName());
903                 }
904
905             } else {
906                 // cache entry names from directory
907
if (file.exists()) {
908                     fillTable(file, table, "");
909                 }
910             }
911         }
912
913         private void fillTable(File JavaDoc f, Hashtable JavaDoc t, String JavaDoc parent) throws IOException JavaDoc {
914
915             String JavaDoc localName = (parent.equals("")) ? "" : parent + "/";
916
917             File JavaDoc[] children = f.listFiles();
918             for (int i = 0; i < children.length; i++) {
919                 processFile(children[i], t, localName);
920             }
921         }
922
923         /**
924          *Adds a file (or, if a directory, the directory's contents) to the table
925          *of files this loader knows about.
926          *<p>
927          *Invokes fillTable for subdirectories which in turn invokes processFile
928          *recursively.
929          *@param fileToProcess the File to be processed
930          *@param t the Hashtable that holds the files the loader knows about
931          *@param parentLocalName prefix to be used for the full path; should be
932          *non-empty only for recursive invocations
933          *@throws IOException in case of errors working with the fileToProcess
934          */

935         private void processFile(File JavaDoc fileToProcess, Hashtable JavaDoc t, String JavaDoc parentLocalName) throws IOException JavaDoc {
936             String JavaDoc key = parentLocalName + fileToProcess.getName();
937             if (fileToProcess.isFile()) {
938                 t.put(key, key);
939             } else if (fileToProcess.isDirectory()) {
940                 fillTable(fileToProcess, t, key);
941             }
942         }
943
944
945         boolean hasItem(String JavaDoc item) {
946             // in the case of ejbc stub compilation, ejbclassloader is created before stubs
947
// gets generated, thus we need to return true for this case.
948
if (table.size() == 0) {
949                 return true;
950             }
951
952             /*
953              *Even with the previous special handling, a file could be created
954              *in a directory after the loader was created and its table of
955              *URLEntry names populated. So check the table first and, if
956              *the target item is not there and this URLEntry is for a directory, look for
957              *the file. If the file is now present but was not when the loader
958              *was created, add an entry for the file in the table.
959              */

960             boolean result = false;
961             String JavaDoc target = item;
962             // special handling
963
if (item.startsWith("./")) {
964                 target = item.substring(2, item.length());
965             }
966
967             result = table.containsKey(target);
968             if ( ! result && ! isJar) {
969                 /*
970                  *If the file exists now then it has been added to the directory since the
971                  *loader was created. Add it to the table of files we
972                  *know about.
973                  */

974                 File JavaDoc targetFile = privilegedCheckForFile(target);
975                 if (targetFile != null) {
976                     try {
977                         processFile(targetFile, table, "");
978                         result = true;
979                     } catch (IOException JavaDoc ioe) {
980                         _logger.log(Level.SEVERE, formatMsg("loader.ejbclassloader_error_processing_file", target, file.getAbsolutePath()), ioe);
981                         return false;
982                     }
983                 }
984             }
985             return result;
986         }
987
988         /**
989          *Returns a File object for the requested path within the URLEntry.
990          *<p>
991          *Runs privileged because user code could trigger invocations of this
992          *method.
993          *@param targetPath the relative path to look for
994          *@return File object for the requested file; null if it does not exist or
995          *in case of error
996          */

997         private File JavaDoc privilegedCheckForFile(final String JavaDoc targetPath) {
998             /*
999              *Check for the file existence with privs, because this code can
1000             *be invoked from user code which may not otherwise have access
1001             *to the directories of interest.
1002             */

1003            try {
1004                File JavaDoc result = (File JavaDoc) AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc() {
1005                        public Object JavaDoc run() throws Exception JavaDoc {
1006
1007                            File JavaDoc targetFile = new File JavaDoc(file, targetPath);
1008                            if ( ! targetFile.exists()) {
1009                                targetFile = null;
1010                            }
1011                            return targetFile;
1012                        }
1013                    });
1014
1015                return result;
1016
1017            } catch (PrivilegedActionException JavaDoc pae) {
1018                /*
1019                 *Log any exception and return false.
1020                 */

1021                _logger.log(Level.SEVERE, formatMsg("loader.ejbclassloader_error_checking_existence", targetPath, file.getAbsolutePath()), pae.getCause());
1022                return null;
1023            }
1024        }
1025
1026          /**
1027           * Sets ProtectionDomain with CodeSource including Signers in
1028           * Entry for use in call to defineClass.
1029           * @param signers the array of signer certs or null
1030           */

1031         public void setProtectionDomain (ClassLoader JavaDoc ejbClassLoader, Certificate JavaDoc[] signers) throws MalformedURLException JavaDoc {
1032             if (pd == null) {
1033                 pd = new ProtectionDomain JavaDoc(new CodeSource JavaDoc(file.toURL(),signers),null, ejbClassLoader, null );
1034             }
1035         }
1036
1037        public String JavaDoc toString() {
1038            return "URLEntry : " + source.toString();
1039        }
1040
1041        /**
1042         * Returns true if two URL entries has equal URLs.
1043         *
1044         * @param obj URLEntry to compare against
1045         * @return true if both entry has equal URL
1046         */

1047        public boolean equals(Object JavaDoc obj) {
1048
1049            boolean tf = false;
1050
1051            if (obj instanceof URLEntry) {
1052                URLEntry e = (URLEntry) obj;
1053                if (source.equals(e.source)) {
1054                    tf = true;
1055                }
1056            }
1057
1058            return tf;
1059        }
1060
1061        /**
1062         * Since equals is overridden, we need to override hashCode as well.
1063         */

1064        public int hashCode() {
1065            return source.hashCode();
1066        }
1067
1068    }
1069
1070    /**
1071     *Returns the vector of open streams; creates it if needed.
1072     *@return Vector<SentinelInputStream> holding open streams
1073     */

1074    private Vector JavaDoc<SentinelInputStream> getStreams() {
1075        if (streams == null) {
1076            streams = new Vector JavaDoc<SentinelInputStream>();
1077        }
1078        return streams;
1079    }
1080
1081    /**
1082     *Closes any streams that remain open, logging a warning for each.
1083     *<p>
1084     *This method should be invoked when the loader will no longer be used
1085     *and the app will no longer explicitly close any streams it may have opened.
1086     */

1087    private void closeOpenStreams() {
1088        if (streams != null) {
1089
1090            SentinelInputStream[] toClose = streams.toArray(new SentinelInputStream[streams.size()]);
1091            for (SentinelInputStream s : toClose) {
1092                try {
1093                    s.closeWithWarning();
1094                } catch (IOException JavaDoc ioe) {
1095                    _logger.log(Level.WARNING, "loader.ejbclassloader_error_closing_stream", ioe);
1096                }
1097            }
1098            streams.clear();
1099            streams = null;
1100        }
1101    }
1102
1103    /**
1104     * Wraps all InputStreams returned by this class loader to report when
1105     * a finalizer is run before the stream has been closed. This helps
1106     * to identify where locked files could arise.
1107     * @author vtsyganok
1108     * @author tjquinn
1109     */

1110    protected class SentinelInputStream extends FilterInputStream JavaDoc {
1111        private boolean closed;// = false;
1112
private final Throwable JavaDoc throwable;
1113
1114        /**
1115         * Constructs new FilteredInputStream which reports InputStreams not closed properly.
1116         * When the garbage collector runs the finalizer. If the stream is still open this class will
1117         * report a stack trace showing where the stream was opened.
1118         *
1119         * @param in - InputStream to be wrapped
1120         */

1121        protected SentinelInputStream(final InputStream JavaDoc in) {
1122            super(in);
1123            throwable = new Throwable JavaDoc();
1124            getStreams().add(this);
1125        }
1126
1127        /**
1128         * Closes underlying input stream.
1129         */

1130        public void close() throws IOException JavaDoc {
1131            _close();
1132        }
1133
1134        /**
1135         * Invoked by Garbage Collector. If underlying InputStream was not closed properly,
1136         * the stack trace of the constructor will be logged!
1137         */

1138        protected void finalize() throws Throwable JavaDoc {
1139            if (!closed && this.in != null){
1140                try {
1141                    in.close();
1142                }
1143                catch (IOException JavaDoc ignored){
1144                    //Cannot do anything here.
1145
}
1146                //Well, give them a stack trace!
1147
report();
1148            }
1149            super.finalize();
1150        }
1151
1152        private void _close() throws IOException JavaDoc {
1153            closed = true;
1154            getStreams().remove(this);
1155            super.close();
1156        }
1157
1158        private void closeWithWarning() throws IOException JavaDoc {
1159            _close();
1160            report();
1161        }
1162
1163        /**
1164         * Report "left-overs"!
1165         */

1166        private void report(){
1167            _logger.log(Level.WARNING, "Input stream has been finalized or forced closed without being explicitly closed; stream instantiation reported in following stack trace", this.throwable);
1168        }
1169    }
1170    /**
1171     * To properly close streams obtained through URL.getResource().getStream():
1172     * this opens the input stream on a JarFile that is already open as part
1173     * of the classloader, and returns a sentinel stream on it.
1174     *
1175     * @author fkieviet
1176     */

1177    private class InternalJarURLConnection extends JarURLConnection JavaDoc {
1178        private URL JavaDoc mURL;
1179        private URLEntry mRes;
1180        private String JavaDoc mName;
1181
1182        /**
1183         * Constructor
1184         *
1185         * @param url the URL that is a stream for
1186         * @param res URLEntry
1187         * @param name String
1188         * @throws MalformedURLException from super class
1189         */

1190        public InternalJarURLConnection(URL JavaDoc url, URLEntry res, String JavaDoc name)
1191            throws MalformedURLException JavaDoc {
1192            super(url);
1193            mRes = res;
1194            mName = name;
1195        }
1196
1197        /**
1198         * @see java.net.JarURLConnection#getJarFile()
1199         */

1200        public JarFile JavaDoc getJarFile() throws IOException JavaDoc {
1201            return mRes.zip;
1202        }
1203
1204        /**
1205         * @see java.net.URLConnection#connect()
1206         */

1207        public void connect() throws IOException JavaDoc {
1208            // Nothing
1209
}
1210
1211        /**
1212         * @see java.net.URLConnection#getInputStream()
1213         */

1214        public InputStream JavaDoc getInputStream() throws IOException JavaDoc {
1215            ZipEntry JavaDoc entry = mRes.zip.getEntry(mName);
1216            return new SentinelInputStream(mRes.zip.getInputStream(entry));
1217        }
1218    }
1219
1220    /**
1221     * To properly close streams obtained through URL.getResource().getStream():
1222     * an instance of this class is instantiated for each and every URL object
1223     * created by this classloader. It provides a custom JarURLConnection
1224     * (InternalJarURLConnection) so that the stream can be obtained from an already
1225     * open jar file.
1226     *
1227     * @author fkieviet
1228     */

1229    private class InternalURLStreamHandler extends URLStreamHandler JavaDoc {
1230        private URL JavaDoc mURL;
1231        private URLEntry mRes;
1232        private String JavaDoc mName;
1233
1234        /**
1235         * Constructor
1236         *
1237         * @param res URLEntry
1238         * @param name String
1239         */

1240        public InternalURLStreamHandler(URLEntry res, String JavaDoc name) {
1241            mRes = res;
1242            mName = name;
1243        }
1244
1245        /**
1246         * @see java.net.URLStreamHandler#openConnection(java.net.URL)
1247         */

1248        protected URLConnection JavaDoc openConnection(URL JavaDoc u) throws IOException JavaDoc {
1249            if (u != mURL) { // ref compare on purpose
1250
// This should never happen
1251
throw new IOException JavaDoc("Cannot open a foreign URL; this.url=" + mURL
1252                    + "; foreign.url=" + u);
1253            }
1254            return new InternalJarURLConnection(u, mRes, mName);
1255        }
1256
1257        /**
1258         * Ties the URL that this handler is associated with to the handler, so
1259         * that it can be asserted that somehow no other URLs are mangled in (this
1260         * is theoretically impossible)
1261         *
1262         * @param url URL
1263         */

1264        public void tieUrl(URL JavaDoc url) {
1265            mURL = url;
1266        }
1267    }
1268
1269    /**
1270     * This class is used as return value of findClassIntenal method to return
1271     * both class bytes and protection domain.
1272     */

1273    private static class ClassData {
1274        protected byte[] classBytes;
1275        protected ProtectionDomain JavaDoc pd;
1276
1277        ClassData(byte[] classData, ProtectionDomain JavaDoc pd) {
1278            this.classBytes = classData;
1279            this.pd = pd;
1280        }
1281    }
1282
1283    /**
1284     * This class loader only provides a new class loading namespace
1285     * so that persistence provider can load classes in that separate
1286     * namespace while scanning annotations.
1287     * This class loader delegates all stream handling (i.e. reading
1288     * actual class/resource data) operations to the application class loader.
1289     * It only defines the Class using the byte codes.
1290     * Motivation behind this class is discussed at
1291     * https://glassfish.dev.java.net/issues/show_bug.cgi?id=237.
1292     */

1293    private static class DelegatingClassLoader extends SecureClassLoader JavaDoc {
1294
1295        /**
1296         * The application class loader which is used to read class data.
1297         */

1298        private EJBClassLoader delegate = null;
1299
1300        /**
1301         * Create a new instance.
1302         * @param applicationCL is the original class loader associated
1303         * with this application. The new class loader uses it to delegate
1304         * stream handling operations. The new class loader also uses
1305         * applicationCL's parent as its own parent.
1306         */

1307        DelegatingClassLoader(EJBClassLoader applicationCL) {
1308            super(applicationCL.getParent()); // normal class loading delegation
1309
this.delegate = applicationCL;
1310        }
1311
1312        /**
1313         * This method uses the delegate to use class bytes and then defines
1314         * the class using this class loader
1315         */

1316        protected Class JavaDoc findClass(String JavaDoc name) throws ClassNotFoundException JavaDoc {
1317            ClassData classData = delegate.findClassData(name);
1318            // Define package information if necessary
1319
int lastPackageSep = name.lastIndexOf('.');
1320            if ( lastPackageSep != -1 ) {
1321                String JavaDoc packageName = name.substring(0, lastPackageSep);
1322                if( getPackage(packageName) == null ) {
1323                    try {
1324                        // There's a small chance that one of our parents
1325
// could define the same package after getPackage
1326
// returns null but before we call definePackage,
1327
// since the parent classloader instances
1328
// are not locked. So, just catch the exception
1329
// that is thrown in that case and ignore it.
1330
//
1331
// It's unclear where we would get the info to
1332
// set all spec and impl data for the package,
1333
// so just use null. This is consistent will the
1334
// JDK code that does the same.
1335
definePackage(packageName, null, null, null,
1336                                      null, null, null, null);
1337                    } catch(IllegalArgumentException JavaDoc iae) {
1338                        // duplicate attempt to define same package.
1339
// safe to ignore.
1340
_logger.log(Level.FINE, "duplicate package " +
1341                            "definition attempt for " + packageName, iae);
1342                    }
1343                }
1344            }
1345            Class JavaDoc clazz = null;
1346            try {
1347                clazz = defineClass(name, classData.classBytes, 0, classData.classBytes.length, classData.pd);
1348                return clazz;
1349            } catch (UnsupportedClassVersionError JavaDoc ucve) {
1350            throw new UnsupportedClassVersionError JavaDoc(
1351                sm.getString("ejbClassLoader.unsupportedVersion", name,
1352                             System.getProperty("java.version")));
1353            }
1354        }
1355
1356        protected URL JavaDoc findResource(String JavaDoc name) {
1357            return delegate.findResource(name);
1358        }
1359
1360        protected Enumeration JavaDoc<URL JavaDoc> findResources(String JavaDoc name) throws IOException JavaDoc {
1361            return delegate.findResources(name);
1362        }
1363
1364    }
1365
1366
1367}
1368
Popular Tags