KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > jdo > api > persistence > enhancer > EnhancerClassLoader


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  * EnhancerClassLoader.java
26  */

27
28 package com.sun.jdo.api.persistence.enhancer;
29
30 import java.lang.ref.WeakReference JavaDoc;
31
32 import java.io.InputStream JavaDoc;
33 import java.io.ByteArrayInputStream JavaDoc;
34 import java.io.ByteArrayOutputStream JavaDoc;
35 import java.io.IOException JavaDoc;
36 import java.io.PrintWriter JavaDoc;
37
38 import java.util.Properties JavaDoc;
39
40 import java.net.URLClassLoader JavaDoc;
41 import java.net.URL JavaDoc;
42
43 import sun.misc.Resource;
44 import sun.misc.URLClassPath;
45
46 import java.util.jar.Manifest JavaDoc;
47 import java.util.jar.Attributes JavaDoc;
48 import java.util.jar.Attributes.Name;
49
50 import java.security.AccessController JavaDoc;
51 import java.security.AccessControlContext JavaDoc;
52 import java.security.CodeSource JavaDoc;
53 import java.security.PrivilegedExceptionAction JavaDoc;
54 import java.security.PrivilegedActionException JavaDoc;
55 import java.security.cert.Certificate JavaDoc;
56
57 import com.sun.jdo.api.persistence.model.Model;
58
59 import com.sun.jdo.api.persistence.enhancer.meta.JDOMetaData;
60 import com.sun.jdo.api.persistence.enhancer.meta.JDOMetaDataPropertyImpl;
61 import com.sun.jdo.api.persistence.enhancer.meta.JDOMetaDataModelImpl;
62 import com.sun.jdo.api.persistence.enhancer.meta.JDOMetaDataTimer;
63
64 import com.sun.jdo.api.persistence.enhancer.util.Support;
65
66
67 //@lars: changes to reflect the new ByteCodeEnhancer interface
68

69
70 /**
71  * Implements a ClassLoader which automatically enchances the .class files
72  * according to the JDOMetaData information in the jar archive.
73  * @author Yury Kamen
74  * @version
75  */

76 public class EnhancerClassLoader extends URLClassLoader JavaDoc {
77
78     static public final String JavaDoc DO_SIMPLE_TIMING
79         = FilterEnhancer.DO_SIMPLE_TIMING;
80     static public final String JavaDoc VERBOSE_LEVEL
81         = FilterEnhancer.VERBOSE_LEVEL;
82     static public final String JavaDoc VERBOSE_LEVEL_QUIET
83         = FilterEnhancer.VERBOSE_LEVEL_QUIET;
84     static public final String JavaDoc VERBOSE_LEVEL_WARN
85         = FilterEnhancer.VERBOSE_LEVEL_WARN;
86     static public final String JavaDoc VERBOSE_LEVEL_VERBOSE
87         = FilterEnhancer.VERBOSE_LEVEL_VERBOSE;
88     static public final String JavaDoc VERBOSE_LEVEL_DEBUG
89         = FilterEnhancer.VERBOSE_LEVEL_DEBUG;
90
91     static public URL JavaDoc[] pathToURLs(String JavaDoc classpath) {
92         return URLClassPath.pathToURLs(classpath);
93     }
94
95     // misc
96
//@olsen: 4370739
97
private boolean debug = true;
98     private boolean doTiming = false;
99     private PrintWriter JavaDoc out = new PrintWriter JavaDoc(System.out, true);
100
101     private ByteCodeEnhancer enhancer;
102     private JDOMetaData metaData;
103     private Properties JavaDoc settings;
104     private WeakReference JavaDoc outByteCodeRef;
105
106     // The search path for classes and resources
107
private final URLClassPath ucp;
108
109     // The context to be used when loading classes and resources
110
private final AccessControlContext JavaDoc acc;
111
112     //@olsen: 4370739
113
private final void message() {
114         if (debug) {
115             out.println();
116         }
117     }
118
119     //@olsen: 4370739
120
private final void message(String JavaDoc s) {
121         if (debug) {
122             out.println(s);
123         }
124     }
125
126     //@olsen: 4370739
127
private final void message(Exception JavaDoc e) {
128         if (debug) {
129             final String JavaDoc msg = ("Exception caught: " + e);//NOI18N
130
out.println(msg);
131             e.printStackTrace(out);
132         }
133     }
134
135     /**
136      * Creates a new EnhancerClassLoader for the specified url.
137      *
138      * @param urls the classpath to search
139      */

140     protected EnhancerClassLoader(URL JavaDoc[] urls) {
141         super(urls);
142         acc = AccessController.getContext();
143         ucp = new URLClassPath(urls);
144         checkUCP(urls);
145     }
146
147     /**
148      * Creates a new EnhancerClassLoader for the specified url.
149      *
150      * @param urls the classpath to search
151      */

152     protected EnhancerClassLoader(URL JavaDoc[] urls,
153                                   ClassLoader JavaDoc loader) {
154         super(urls, loader);
155         acc = AccessController.getContext();
156         ucp = new URLClassPath(urls);
157         checkUCP(urls);
158     }
159
160     /**
161      * Creates a new EnhancerClassLoader for the specified url.
162      *
163      * @param classpath the classpath to search
164      */

165     public EnhancerClassLoader(String JavaDoc classpath,
166                                Properties JavaDoc settings,
167                                PrintWriter JavaDoc out) {
168         this(pathToURLs(classpath));
169         JDOMetaData metaData = new JDOMetaDataModelImpl(Model.ENHANCER, out);
170         init(metaData, settings, out);
171     }
172
173     /**
174      * Creates a new EnhancerClassLoader for the specified url.
175      *
176      * @param urls the classpath to search
177      */

178     public EnhancerClassLoader(URL JavaDoc[] urls,
179                                Properties JavaDoc settings,
180                                PrintWriter JavaDoc out) {
181         this(urls);
182         JDOMetaData metaData = new JDOMetaDataModelImpl(Model.ENHANCER, out);
183         init(metaData, settings, out);
184     }
185
186     /**
187      * Creates a new EnhancerClassLoader for the specified url.
188      *
189      * @param classpath the classpath to search
190      */

191     public EnhancerClassLoader(String JavaDoc classpath,
192                                JDOMetaData metaData,
193                                Properties JavaDoc settings,
194                                PrintWriter JavaDoc out) {
195         this(pathToURLs(classpath));
196         init(metaData, settings, out);
197     }
198
199     /**
200      * Creates a new EnhancerClassLoader for the specified url.
201      *
202      * @param urls the classpath to search
203      */

204     public EnhancerClassLoader(URL JavaDoc[] urls,
205                                JDOMetaData metaData,
206                                Properties JavaDoc settings,
207                                PrintWriter JavaDoc out) {
208         this(urls);
209         init(metaData, settings, out);
210     }
211
212     /**
213      * Creates a new EnhancerClassLoader for the specified url.
214      *
215      * @param url the url of the jar file
216      */

217 //@olsen: obsolete code
218
/*
219     public EnhancerClassLoader(URL url,
220                                Properties metaDataProperties,
221                                Properties settings,
222                                PrintWriter out) {
223         super(new URL[]{ url }); // , ClassLoader.getSystemClassLoader() ); //, new Factory() );
224         initUcp(url);
225         init(url, new JDOMetaDataPropertyImpl(metaDataProperties, out), settings, out);
226     }
227 */

228
229     /**
230      * Creates a new EnhancerClassLoader for the specified url.
231      *
232      * @param url the url of the jar file
233      */

234 //@olsen: obsolete code
235
/*
236     public EnhancerClassLoader(URL url,
237                                Properties settings,
238                                PrintWriter out)
239         throws IOException {
240         super(new URL[]{ url } ); //, ClassLoader.getSystemClassLoader() ); //, new Factory() );
241         initUcp(url);
242         Properties metaDataProperties = getJDOMetaDataProperties();
243         metaData = new JDOMetaDataPropertyImpl(metaDataProperties, out);
244         init(url, metaData, settings, out);
245     }
246 */

247
248     /**
249      * Creates a new EnhancerClassLoader for the specified url.
250      *
251      * @param url the url of the jar file
252      */

253 //@olsen: obsolete code
254
/*
255     public EnhancerClassLoader(URL url,
256                                ClassLoader loader,
257                                Properties settings,
258                                PrintWriter out)
259         throws IOException {
260         super(new URL[]{ url }, loader); //super(new URL[]{ url }, loader, new Factory() ); //, ClassLoader.getSystemClassLoader() ); //, new Factory() );
261         initUcp(url);
262         Properties metaDataProperties = getJDOMetaDataProperties();
263         metaData = new JDOMetaDataPropertyImpl(metaDataProperties, out);
264         init(url, metaData, settings, out);
265     }
266 */

267
268 //@olsen: obsolete code
269
/*
270     private String getJDOMetaDataPropertiesName()
271         throws IOException {
272         //message("url=" + url);
273         if (true)
274             return null;
275         URL u = new URL("jar", "", url + "!/");
276         JarURLConnection uc = (JarURLConnection)u.openConnection();
277         Attributes attr = uc.getMainAttributes();
278         String result = attr != null ? attr.getValue("JDOMetaData") : null;
279         //message("getJDOMetaDataPropertiesName() returned: " + result);
280         return result;
281     }
282
283     private Properties getJDOMetaDataProperties()
284         throws IOException {
285         return getJDOMetaDataProperties(getJDOMetaDataPropertiesName());
286     }
287
288     private static final String DEFAULT_JDO_PROPERTY_NAME = "all.jdo";
289
290     private Properties getJDOMetaDataProperties(String name)
291         throws IOException {
292         if (null == name) {
293             name = DEFAULT_JDO_PROPERTY_NAME;
294         }
295         Properties prop = new Properties();
296         message("---ucp=" + ucp + " name=" + name);
297         Resource res = ucp.getResource(name, false);
298         if (null == res) {
299             throw new IOException("Resource '" + name + "'" + " was not found");
300         }
301
302         byte[] b = res.getBytes();
303         if (null == b) {
304             throw new IOException("Resource '" + name + "'" + " has null content");
305         }
306
307         InputStream is = new ByteArrayInputStream(b);
308         prop.load(is);
309         return prop;
310     }
311 */

312
313     /**
314      * Appends the specified URL to the list of URLs to search for
315      * classes and resources.
316      *
317      * @param url the URL to be added to the search path of URLs
318      */

319     protected void addURL(URL JavaDoc url) {
320         throw new UnsupportedOperationException JavaDoc("Not implemented yet: EnhancerClassLoader.addURL(URL)");//NOI18N
321
//super.addURL(url);
322
//ucp.addURL(url);
323
}
324
325     private void checkUCP(URL JavaDoc[] urls) {
326         // ensure classpath is not empty
327
if (null == urls) {
328             throw new IllegalArgumentException JavaDoc("urls == null");//NOI18N
329
}
330         if (urls.length == 0) {
331             throw new IllegalArgumentException JavaDoc("urls.length == 0");//NOI18N
332
}
333
334         for (int i = 0; i < urls.length; i++) {
335             super.addURL(urls[i]);
336         }
337     }
338
339     /**
340      * Initialize the EnhancingClassLoader
341      */

342     private void init(JDOMetaData metaData,
343                       Properties JavaDoc settings,
344                       PrintWriter JavaDoc out) {
345         this.out = out;
346         final String JavaDoc verboseLevel
347             = (settings == null ? null
348                : settings.getProperty(FilterEnhancer.VERBOSE_LEVEL));
349         this.debug = FilterEnhancer.VERBOSE_LEVEL_DEBUG.equals(verboseLevel);
350         this.settings = settings;
351         this.metaData = metaData;
352         this.enhancer = null;
353
354         if (settings != null) {
355             final String JavaDoc timing
356                 = settings.getProperty(FilterEnhancer.DO_SIMPLE_TIMING);
357             this.doTiming = Boolean.valueOf(timing).booleanValue();
358         }
359         if (this.doTiming) {
360             // wrap with timing meta data object
361
this.metaData = new JDOMetaDataTimer(metaData);
362         }
363
364         message("EnhancerClassLoader: UCP = {");//NOI18N
365
final URL JavaDoc[] urls = getURLs();
366         for (int i = 0; i < urls.length; i++) {
367             message(" " + urls[i]);//NOI18N
368
}
369         message("}");//NOI18N
370

371         message("EnhancerClassLoader: jdoMetaData = " + metaData);//NOI18N
372
}
373
374     public synchronized Class JavaDoc loadClass(String JavaDoc name, boolean resolve)
375         throws ClassNotFoundException JavaDoc {
376         message();
377         message("EnhancerClassLoader: loading class: " + name);//NOI18N
378

379         try {
380             Class JavaDoc c = null;
381
382             final String JavaDoc classPath = name.replace('.', '/');
383             // At least these packages must be delegated to parent class
384
// loader:
385
// java/lang, (Object, ...)
386
// java/util, (Collection)
387
// java/io, (PrintWriter)
388
// javax/sql, (PMF->javax.sql.DataSource)
389
// javax/transaction (Tx->javax.transaction.Synchronization)
390
//
391
//@olsen: delegate loading of "safe" classes to parent
392
//if (metaData.isTransientClass(classPath)) {
393
//
394
//@olsen: only delegate loading of bootstrap classes to parent
395
//if (classPath.startsWith("java/lang/")) {
396
//
397
//@olsen: performance bug 4457471: delegate loading of F4J
398
// persistence classes to parent tp prevent passing these and
399
// other IDE classes plus database drivers etc. to the enhancer!
400
//if (classPath.startsWith("java/lang/")
401
// || classPath.startsWith("com/sun/jdo/")) {
402
//
403
//@olsen: bug 4480618: delegate loading of javax.{sql,transaction}
404
// classes to parent class loader to support user-defined
405
// DataSource and Synchronization objects to be passed to the
406
// TP runtime. By the same argument, java.{util,io} classes need
407
// also be loaded by the parent class loader. This has been
408
// the case since the EnhancerClassLoader will never find these
409
// bootstrap classes in the passed Classpath. However, for
410
// efficiency and clarity, this delegation should be expressed
411
// by testing for entire "java/" package in the check here.
412
if (classPath.startsWith("java/")//NOI18N
413
|| classPath.startsWith("javax/sql/")//NOI18N
414
|| classPath.startsWith("javax/transaction/")//NOI18N
415
|| classPath.startsWith("com/sun/jdo/")) {//NOI18N
416
message("EnhancerClassLoader: bootstrap class, using parent loader for class: " + name);//NOI18N
417
return super.loadClass(name, resolve);
418
419 //@olsen: dropped alternative approach
420
/*
421                 message("EnhancerClassLoader: transient class, skipping enhancing: " + name);//NOI18N
422
423                 // get a byte array output stream to collect byte code
424                 ByteArrayOutputStream outByteCode
425                     = ((null == outByteCodeRef)
426                        ? null : (ByteArrayOutputStream)outByteCodeRef.get());
427                 if (null == outByteCode) {
428                     outByteCode = new ByteArrayOutputStream(10000);
429                     outByteCodeRef = new WeakReference(outByteCode);
430                 }
431                 outByteCode.reset();
432
433                 // find byte code of class
434                 final InputStream is = getSystemResourceAsStream(name);
435                 //@olsen: (is == null) ?!
436
437                 // copy byte code of class into byte array
438                 final byte[] data;
439                 try {
440                     int b;
441                     while ((b = is.read()) >= 0) {
442                         outByteCode.write(b);
443                     }
444                     data = outByteCode.toByteArray();
445                 } catch (IOException e) {
446                     final String msg
447                         = ("Exception caught while loading class '"//NOI18N
448                            + name + "' : " + e);//NOI18N
449                     throw new ClassNotFoundException(msg, e);
450                 }
451
452                 // convert the byte code into class object
453                 c = defineClass(name, data, 0, data.length);
454 */

455             }
456
457             //@olsen: check if class has been loaded already
458
if (c == null) {
459                 c = findLoadedClass(name);
460                 if (c != null) {
461                     message("EnhancerClassLoader: class already loaded: " + name);//NOI18N
462
}
463             }
464
465             if (c == null) {
466                 c = findAndEnhanceClass(name);
467             }
468
469             // as a last resort, if the class couldn't be found, try
470
// loading class by parent class loader
471
if (c == null) {
472                 message("EnhancerClassLoader: class not found, using parent loader for class: " + name);//NOI18N
473
return super.loadClass(name, resolve);
474             }
475
476             message();
477             message("EnhancerClassLoader: loaded class: " + name);//NOI18N
478
if (resolve) {
479                 resolveClass(c);
480             }
481
482             message();
483             message("EnhancerClassLoader: loaded+resolved class: " + name);//NOI18N
484
return c;
485         } catch (RuntimeException JavaDoc e) {
486             // log exception only
487
message();
488             message("EnhancerClassLoader: EXCEPTION SEEN: " + e);//NOI18N
489
//e.printStackTrace(out);
490
throw e;
491         } catch (ClassNotFoundException JavaDoc e) {
492             // log exception only
493
message();
494             message("EnhancerClassLoader: EXCEPTION SEEN: " + e);//NOI18N
495
//e.printStackTrace(out);
496
throw e;
497         }
498     }
499
500     /**
501      * Finds and loads the class with the specified name from the URL search
502      * path. Any URLs referring to JAR files are loaded and opened as needed
503      * until the class is found.
504      *
505      * @param name the name of the class
506      * @return the resulting class
507      * @exception ClassNotFoundException if the class could not be found
508      */

509     private Class JavaDoc findAndEnhanceClass(final String JavaDoc name)
510         throws ClassNotFoundException JavaDoc
511     {
512         try {
513             if (doTiming) {
514                 Support.timer.push("EnhancerClassLoader.findAndEnhanceClass(String)",//NOI18N
515
"EnhancerClassLoader.findAndEnhanceClass(" + name + ")");//NOI18N
516
}
517             return (Class JavaDoc)
518             AccessController.doPrivileged(new PrivilegedExceptionAction JavaDoc() {
519                 public Object JavaDoc run() throws ClassNotFoundException JavaDoc {
520                     String JavaDoc path = name.replace('.', '/').concat(".class");//NOI18N
521
//message("path=" + path);
522
Resource res = ucp.getResource(path, false);
523                     if (res != null) {
524                         try {
525                             return defineClass(name, res);
526                         } catch (IOException JavaDoc e) {
527                             final String JavaDoc msg
528                                 = ("Exception caught while loading class '"//NOI18N
529
+ name + "' : " + e);//NOI18N
530
throw new ClassNotFoundException JavaDoc(msg, e);
531                         }
532                     } else {
533                         // ok if class resource not found (e.g. java.*)
534
//throw new ClassNotFoundException(name);
535
return null;
536                     }
537                 }
538             }, acc);
539         } catch (PrivilegedActionException JavaDoc pae) {
540             throw (ClassNotFoundException JavaDoc) pae.getException();
541         } finally {
542             if (doTiming) {
543                 Support.timer.pop();
544             }
545         }
546     }
547
548     /**
549      * Defines a Class using the class bytes obtained from the specified
550      * Resource. The resulting Class must be resolved before it can be
551      * used.
552      */

553     private Class JavaDoc defineClass(String JavaDoc name, Resource res)
554         throws IOException JavaDoc, ClassNotFoundException JavaDoc {
555         int i = name.lastIndexOf('.');
556         URL JavaDoc url = res.getCodeSourceURL();
557         if (i != -1) {
558             String JavaDoc pkgname = name.substring(0, i);
559             // Check if package already loaded.
560
Package JavaDoc pkg = getPackage(pkgname);
561             Manifest JavaDoc man = res.getManifest();
562             if (pkg != null) {
563                 // Package found, so check package sealing.
564
boolean ok;
565                 if (pkg.isSealed()) {
566                     // Verify that code source URL is the same.
567
ok = pkg.isSealed(url);
568                 } else {
569                     // Make sure we are not attempting to seal the package
570
// at this code source URL.
571
ok = (man == null) || !isSealed(pkgname, man);
572                 }
573                 if (!ok) {
574                     throw new SecurityException JavaDoc("sealing violation");//NOI18N
575
}
576             } else {
577                 if (man != null) {
578                     definePackage(pkgname, man, url);
579                 } else {
580                     definePackage(pkgname, null, null, null, null, null, null, null);
581                 }
582             }
583         }
584         // Now read the class bytes and define the class
585
byte[] b = res.getBytes();
586         Certificate JavaDoc[] certs = res.getCertificates();
587         CodeSource JavaDoc cs = new CodeSource JavaDoc(url, certs);
588
589         //@olsen: performance bug 4457471: circumvent enhancer for
590
// non-enhancable classes
591
final String JavaDoc classPath = name.replace('.', '/');
592         if (!metaData.isTransientClass(classPath)) {
593             // Add enhancement here
594
b = enhance(name, b, 0, b.length);
595         }
596
597         return defineClass(name, b, 0, b.length, cs);
598     }
599
600     private byte[] enhance(String JavaDoc name, byte[] data, int off, int len)
601         throws ClassNotFoundException JavaDoc {
602         //message("EnhancerClassLoader: enhance class: " + name);
603

604         final byte[] result;
605         try {
606             // create enhancer if not done yet
607
if (null == enhancer) {
608                 enhancer = new FilterEnhancer(metaData, settings, out, null);
609                 if (doTiming) {
610                     // wrap with timing filter enhancer object
611
enhancer = new ByteCodeEnhancerTimer(enhancer);
612                 }
613             }
614
615             // create input and output byte streams
616
ByteArrayInputStream JavaDoc inByteCode
617                 = new ByteArrayInputStream JavaDoc(data, off, len);
618             ByteArrayOutputStream JavaDoc outByteCode
619                 = ((null == outByteCodeRef)
620                    ? null : (ByteArrayOutputStream JavaDoc)outByteCodeRef.get());
621             if (null == outByteCode) {
622                 outByteCode = new ByteArrayOutputStream JavaDoc(10000);
623                 outByteCodeRef = new WeakReference JavaDoc(outByteCode);
624             }
625             outByteCode.reset();
626
627             // enhance class
628
boolean changed
629                 = enhancer.enhanceClassFile(inByteCode, outByteCode);
630
631             // check whether class has been enhanced
632
result = (changed ? outByteCode.toByteArray() : data);
633         } catch (EnhancerUserException e) {
634             //@olsen: 4370739
635
message(e);
636             final String JavaDoc msg = ("Exception caught while loading class '"//NOI18N
637
+ name + "' : " + e);//NOI18N
638
throw new ClassNotFoundException JavaDoc(msg, e);
639         } catch(EnhancerFatalError e) {
640             //@olsen: 4370739
641
message(e);
642             final String JavaDoc msg = ("Exception caught while loading class '"//NOI18N
643
+ name + "' : " + e);//NOI18N
644
// discard enhancer since it might have become inconsistent
645
enhancer = null;
646             throw new ClassNotFoundException JavaDoc(msg, e);
647         }
648         return result;
649     }
650
651     /**
652      * Returns true if the specified package name is sealed according to the
653      * given manifest.
654      */

655     private boolean isSealed(String JavaDoc name, Manifest JavaDoc man) {
656         String JavaDoc path = name.replace('.', '/').concat("/");//NOI18N
657
Attributes JavaDoc attr = man.getAttributes(path);
658         String JavaDoc sealed = null;
659         if (attr != null) {
660             sealed = attr.getValue(Name.SEALED);
661         }
662         if (sealed == null) {
663             if ((attr = man.getMainAttributes()) != null) {
664                 sealed = attr.getValue(Name.SEALED);
665             }
666         }
667         return "true".equalsIgnoreCase(sealed);//NOI18N
668
}
669 }
670
Popular Tags