001: /*
002: * BEGIN_HEADER - DO NOT EDIT
003: *
004: * The contents of this file are subject to the terms
005: * of the Common Development and Distribution License
006: * (the "License"). You may not use this file except
007: * in compliance with the License.
008: *
009: * You can obtain a copy of the license at
010: * https://open-esb.dev.java.net/public/CDDLv1.0.html.
011: * See the License for the specific language governing
012: * permissions and limitations under the License.
013: *
014: * When distributing Covered Code, include this CDDL
015: * HEADER in each file and include the License file at
016: * https://open-esb.dev.java.net/public/CDDLv1.0.html.
017: * If applicable add the following below this CDDL HEADER,
018: * with the fields enclosed by brackets "[]" replaced with
019: * your own identifying information: Portions Copyright
020: * [year] [name of copyright owner]
021: */
022:
023: /*
024: * @(#)CustomClassLoader.java
025: * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
026: *
027: * END_HEADER - DO NOT EDIT
028: */
029: package com.sun.jbi.framework;
030:
031: import com.sun.jbi.JBIProvider;
032:
033: import java.net.URL;
034: import java.io.File;
035: import java.io.InputStream;
036: import java.io.IOException;
037: import java.util.logging.Level;
038: import java.util.logging.Logger;
039:
040: /**
041: * Implementation of a custom class loader.
042: * There is one instance of this classloader per component lifecycle
043: * /bootstrap/shared library.
044: * This classloader subclasses java.net.URLClassLoader and implements
045: * functionality to force 'local' loading of classes at the component
046: * level before delegating it to the parent classloader .
047: * This is to allow the component/SL to load specific versions of libraries
048: * such as Xerces rather than be forced to use the JVM/Application Server
049: * version of the same .
050: *
051: * @author Sun Microsystems, Inc.
052: */
053: public final class CustomClassLoader extends java.net.URLClassLoader {
054:
055: /**
056: * String Translator
057: */
058: private StringTranslator mTranslator;
059:
060: /**
061: * If this flag is <code>true<code>, the class loader will first
062: * ask its parent to try and load the class/resource . If not,
063: * the class loader will try and load the class/resource itself
064: * before delegating to the parent class loader .
065: */
066: private boolean mSelfFirst = false;
067:
068: /**
069: * handle to system classloader flag
070: */
071: private ClassLoader mSystemLoader;
072:
073: /**
074: * handle to parent of this class loader
075: */
076: private ClassLoader mParentLoader;
077:
078: /**
079: * list of filtered classes
080: */
081: private String[] mFilteredPkgs = { "java.", // core Java pkgs
082: "javax.", // Java extensions
083: "sun.", // Sun specific JDK
084: };
085: /**
086: * handle to system logger
087: */
088: private Logger mLogger;
089:
090: /**
091: * Used to load resources safely (without a dangling JarFile ref).
092: */
093: private JarCache mJarCache;
094:
095: /**
096: * Constructor.
097: * @param urls the urls to construct this class loader with
098: * @param parentCL the parent of the component class loader
099: * @param selfFirst flag indicating whether the component
100: * should try loading libraries itself first or delegate to
101: * its parent .
102: */
103: public CustomClassLoader(URL[] urls, ClassLoader parentCL,
104: boolean selfFirst) {
105: super (urls, parentCL);
106: mSelfFirst = selfFirst;
107: mSystemLoader = getSystemClassLoader();
108: mParentLoader = (parentCL != null) ? parentCL : mSystemLoader;
109:
110: EnvironmentContext ec = EnvironmentContext.getInstance();
111: mTranslator = (StringTranslator) ec
112: .getStringTranslatorFor(this );
113: mLogger = (Logger) ec.getLogger();
114:
115: initJarCache(urls);
116: }
117:
118: /**
119: * Overloaded loadClass method that works similarly to the
120: * <code>loadClass(String, boolean)</code> method with <code>
121: * false</code> asthe second argument.
122: *
123: * @param name The name of class to load.
124: * @return The loaded class.
125: * @throws ClassNotFoundException If the class cannot be loaded by this
126: * classloader.
127: */
128: public Class loadClass(String name) throws ClassNotFoundException {
129: return loadClass(name, false);
130: }
131:
132: /**
133: * Overloaded loadClass method that implements the 'selfFirst=true/false'
134: * functionality . We need to overload loadClass instead of findClass
135: * since we want to interfere with the normal Java2 class loading
136: * sequence . By the time the findClass() method is called, the parent
137: * class loader has already been asked to try and load the class - this
138: * is exactly what we are trying to circumvent .
139: *
140: * @see java.lang.ClassLoader#loadClass(String, boolean)
141: * @see java.lang.ClassLoader#findClass(String)
142: *
143: *
144: * The sequence of looking for a class is as follows -
145: * <ul>
146: * <li>call findLoadedClass(String) to check if the class has
147: * already been loaded.
148: * <li>if 'selfFirst=false' hand off the loading to the parent i.e.
149: * the Delegating ClassLoader for this classloader. If parent is null
150: * the class loader built into the VM is used instead.
151: * <li>call findClass() to try loading the class locally, if it
152: * succeeds , hand back the class
153: * <li> finally hand control to parent and let the normal delegation
154: * class loading continue.
155: * <li> if class was not found in above sequence, throw a
156: * ClassNotFoundException
157: * <li> resolveClass is called at each stage before returning the
158: * loaded class
159: * </ul>
160: *
161: * @param name The name of class to load.
162: * @param resolve whether or not to reolve the loaded class.
163: * @return The loaded class.
164: * @throws ClassNotFoundException If the class cannot be loaded by this
165: * classloader.
166: */
167:
168: protected Class loadClass(String name, boolean resolve)
169: throws ClassNotFoundException {
170: Class componentClass = null;
171:
172: mLogger.finest("Custom ClassLoader - loadClass: " + name);
173:
174: // 1. call findLoadedClass, return if found
175: //
176: componentClass = findLoadedClass(name);
177:
178: if (componentClass != null) {
179: if (resolve) {
180: resolveClass(componentClass);
181: }
182: mLogger.finest("Class " + name
183: + " will be loaded from cache");
184: return componentClass;
185: }
186:
187: // if the class is 'filtered' i.e. java/javax etc
188: // 1. first hand it off to the parent .
189: // 2. if the parent fails to load it, have the component/SL
190: // attempt to load it .
191: boolean isFiltered = false;
192:
193: if (isFilteredClassOrResource(name)) {
194: isFiltered = true;
195: try {
196: componentClass = mParentLoader.loadClass(name);
197: return componentClass;
198: } catch (ClassNotFoundException cnfe) {
199: ; // ignore and allow self-first loading
200: }
201: }
202:
203: // 2. if 'selfFirst = false' , send it to the parent classloader
204: // if the class is a filtered class, we have already tried the parent
205: // so there is no need to do that again.
206: try {
207: if (!mSelfFirst && !isFiltered) {
208: componentClass = mParentLoader.loadClass(name);
209: if (resolve) {
210: resolveClass(componentClass);
211: }
212: mLogger.finest("Class " + name
213: + " will be loaded using "
214: + mParentLoader.toString());
215: return componentClass;
216: }
217: } catch (ClassNotFoundException cnfe) {
218: ; // ignore
219: }
220:
221: // if 'selfFirst = 'true' , try loading it locally first
222: //
223: try {
224: componentClass = findClass(name);
225: if (componentClass != null) {
226: if (resolve) {
227: resolveClass(componentClass);
228: }
229:
230: mLogger.finest("Class " + name
231: + " will be loaded using " + this .toString());
232: return componentClass;
233: }
234: } catch (ClassNotFoundException cnfe) {
235: ; // ignore
236: }
237:
238: // finally delegate to the parent if not
239: // found locally . we do this only if the
240: // selfFirst flag is OFF and the class is not filtered
241: // because otherwise, the parent classloader would have
242: // already been searched and we dont want to do this again .
243: //
244: if (mSelfFirst && !isFiltered) {
245: try {
246: componentClass = mParentLoader.loadClass(name);
247: if (resolve) {
248: resolveClass(componentClass);
249: }
250:
251: mLogger.finest("Class " + name
252: + " will be loaded using "
253: + mParentLoader.toString());
254: return componentClass;
255: } catch (ClassNotFoundException cnfe) {
256: ; // ignore
257: }
258: }
259:
260: // all efforts are in vain - class was not found
261: throw new ClassNotFoundException(name);
262: }
263:
264: /**
265: * Overloaded getResource method that implements the 'selfFirst'
266: * functionality . We need to overload getResource instead of findResource
267: * since we want to interfere with the normal Java2 class loading
268: * sequence . By the time the findResource() method is called, the parent
269: * class loader has already been asked to try and load the class - this
270: * is exactly what we are trying to circumvent .
271: *
272: * @see java.lang.ClassLoader#getResource(String)
273: *
274: * The sequence of looking for a resource is as follows -
275: * <ul>
276: * <li>if 'selfFirst=false' hand off the loading to the parent i.e.
277: * the Delegating ClassLoader for this classloader. If parent is null
278: * the class loader built into the VM is used instead.
279: * <li>call findResource() to try loading the resource locally, if it
280: * succeeds , hand back the resource ,
281: * <li> finally hand control to parent and let the
282: * normal delegation class loading continue.
283: * <li> if the resource was not found in above sequence, return NULL
284: * </ul>
285: *
286: * @param name The name of resource to load.
287: * @return The loaded resource.
288: */
289: public URL getResource(String name) {
290: URL retURL = null;
291:
292: // if the resource is 'filtered' i.e. java/javax etc
293: // 1. first hand it off to the parent .
294: // 2. if the parent fails to load it, have the component/SL
295: // attempt to load it .
296: boolean isFiltered = false;
297:
298: if (isFilteredClassOrResource(name)) {
299: isFiltered = true;
300: retURL = mParentLoader.getResource(name);
301: if (retURL != null) {
302: return retURL;
303: }
304: }
305:
306: // 1. if 'selfFirst = false' , send it to the parent classloader
307: // if the resource is filtered, then dont bother since we have
308: // already tried the parent classloader.
309: if (!mSelfFirst && !isFiltered) {
310: retURL = mParentLoader.getResource(name);
311: if (retURL != null) {
312: return retURL;
313: }
314: }
315:
316: // If 'selfFirst = 'true' , try loading it locally first
317: // Check the jar cache first
318: retURL = mJarCache.findResource(name);
319: if (retURL == null) {
320: // jar cache didn't pick it up -- try the URL Classloader just
321: // in case it's included as a file and not a jar.
322: retURL = findResource(name);
323: }
324:
325: if (retURL != null) {
326: return retURL;
327: }
328:
329: // finally delegate to the parent if not
330: // found locally . we do this only if the
331: // selfFirst flag is ON because otherwise ,
332: // the parent classloader would have already been
333: // searched and we dont want to do this again .
334: //
335: if (mSelfFirst && !isFiltered) {
336: retURL = mParentLoader.getResource(name);
337: if (retURL != null) {
338: return retURL;
339: }
340: }
341:
342: // all efforts are in vain - resource was not found
343: return null;
344: }
345:
346: /**
347: * Overloaded getResourceAsStream method for the 'selfFirst'
348: * functionality . We need to overload getResourceAsStream
349: * since we want to interfere with the normal Java2 class loading
350: * sequence .
351: *
352: * @see java.lang.ClassLoader#getResourceAsStream(String)
353: *
354: *
355: * The sequence of looking for a resource is as follows -
356: * <ul>
357: * <li>if 'selfFirst=false' hand off the loading to the parent i.e.
358: * the Delegating ClassLoader for this classloader. If parent is null
359: * the class loader built into the VM is used instead.
360: * <li>call findResourceAsStream() to try loading the resource locally,
361: * if it succeeds , hand back the resource ,
362: * <li> finally hand control to parent and let the
363: * normal delegation class loading continue.
364: * <li> if the resource was not found in above sequence, return NULL
365: * </ul>
366: *
367: * @param name The name of resource to load.
368: * @return The loaded resource.
369: */
370: public InputStream getResourceAsStream(String name) {
371: InputStream retStream = null;
372:
373: // if the resource is 'filtered' i.e. java/javax etc
374: // 1. first hand it off to the parent .
375: // 2. if the parent fails to load it, have the component/SL
376: // attempt to load it .
377: boolean isFiltered = false;
378:
379: if (isFilteredClassOrResource(name)) {
380: isFiltered = true;
381: retStream = mParentLoader.getResourceAsStream(name);
382: if (retStream != null) {
383: return retStream;
384: }
385: }
386:
387: // 1. if 'selfFirst = false' , send it to the parent classloader
388: // if the resource is filtered, then dont bother since we have
389: // already tried the parent classloader.
390: //
391: if (!mSelfFirst && !isFiltered) {
392: retStream = mParentLoader.getResourceAsStream(name);
393: if (retStream != null) {
394: return retStream;
395: }
396: }
397:
398: // If 'selfFirst = 'true' , try loading it locally first.
399: // Check the jar cache for the resource
400: URL resURL = mJarCache.findResource(name);
401: if (resURL == null) {
402: // jar cache didn't pick it up -- try the URL Classloader just
403: // in case it's included as a file and not a jar.
404: resURL = findResource(name);
405: }
406:
407: if (resURL != null) {
408: try {
409: return resURL.openStream();
410: } catch (IOException ioe) {
411: return null;
412: }
413: }
414:
415: // finally delegate to the parent if not
416: // found locally . we do this only if the
417: // selfFirst flag is ON because otherwise ,
418: // the parent classloader would have already been
419: // searched and we dont want to do this again .
420: // dont bother about filtered classes since the
421: // parent classloader has already tried loading it
422: if (mSelfFirst && !isFiltered) {
423: retStream = mParentLoader.getResourceAsStream(name);
424: if (retStream != null) {
425: return retStream;
426: }
427: }
428:
429: // all efforts are in vain - resource was not found
430: return null;
431: }
432:
433: /**
434: * This method checks if a class name is among the filtered classes.
435: * Typical examples of this are the "java.X , javax.X , sun.X" classes
436: * of resource.
437: *
438: * @param name The fully qualified name of class/resource to filter.
439: * @return true if the class/resource is in the filtered list , false
440: * otherwise.
441: */
442: private boolean isFilteredClassOrResource(String name) {
443: boolean isFilteredPkg = false;
444: String pkgPrefix = name.substring(0, name.indexOf(".") + 1);
445:
446: for (int i = 0; i < mFilteredPkgs.length; i++) {
447: String nextPkg = (String) mFilteredPkgs[i];
448: if (nextPkg.equalsIgnoreCase(pkgPrefix)) {
449: isFilteredPkg = true;
450: break;
451: }
452: }
453: return isFilteredPkg;
454: }
455:
456: /** Releases all resources currently held in this classloader's cache. */
457: public void releaseResources() {
458: if (EnvironmentContext.getInstance().getProvider() == JBIProvider.SUNAS) {
459: // Extra clean-up available on Glassfish
460: com.sun.appserv.ClassLoaderUtil
461: .releaseLoader((java.net.URLClassLoader) this );
462: }
463:
464: mJarCache.close();
465: }
466:
467: /** Initialize the JAR cache by adding entries for each jar in this
468: * classloader's path.
469: */
470: private void initJarCache(URL[] urls) {
471: mJarCache = new JarCache();
472:
473: // iterate through each URL in the classpath
474: for (URL url : urls) {
475: try {
476: // make sure the entry exists and that it's a jar
477: File file = new File(url.getFile());
478: if (file.exists() && file.getName().endsWith(".jar")) {
479: mJarCache.addJar(file.getPath());
480: }
481: } catch (java.io.IOException ioEx) {
482: mLogger.log(Level.FINE, mTranslator.getString(
483: LocalStringKeys.CU_CL_CREATE_IO_ERROR, url
484: .getFile()), ioEx);
485: }
486: }
487: }
488: }
|