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: * @(#)ClassLoaderFactory.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.platform.PlatformContext;
032:
033: import java.io.File;
034: import java.net.URL;
035: import java.util.HashMap;
036: import java.util.Iterator;
037: import java.util.List;
038: import java.util.logging.Logger;
039:
040: import javax.jbi.JBIException;
041:
042: /**
043: * This is the implementation of the ClassLoaderFactory.
044: * It acts as a factory and cache to create and retrieve different types of
045: * classloaders and classloader chains for use in the JBI Framework. Examples
046: * include Component ClassLoaders, Bootstrap Classloaders, Shared Classloaders.
047: *
048: * @author Sun Microsystems, Inc.
049: */
050: public final class ClassLoaderFactory {
051: /**
052: * Component classloaders
053: */
054: private HashMap mComponentClassLoaderCache;
055:
056: /**
057: * Shared classloaders
058: */
059: private HashMap mSharedClassLoaderCache;
060:
061: /**
062: * Handle to StringTranslator for message translation
063: */
064: private StringTranslator mTranslator;
065:
066: /**
067: * Handle to the environment logger
068: */
069: private Logger mLogger;
070:
071: /**
072: * Classloader factory
073: */
074: private static ClassLoaderFactory sClassLoaderFactory;
075:
076: /**
077: * Constructor
078: * Private constructor to facilitate a singleton.
079: */
080: private ClassLoaderFactory() {
081: mComponentClassLoaderCache = new HashMap();
082: mSharedClassLoaderCache = new HashMap();
083: EnvironmentContext envCtx = EnvironmentContext.getInstance();
084: mTranslator = (StringTranslator) envCtx
085: .getStringTranslatorFor(this );
086: mLogger = (Logger) envCtx.getLogger();
087: }
088:
089: /**
090: * Returns a singleton instance of the ClassLoaderFactory class.
091: *
092: * @return The instance of the ClassLoaderFactory
093: */
094: public static ClassLoaderFactory getInstance() {
095: if (null == sClassLoaderFactory) {
096: sClassLoaderFactory = new ClassLoaderFactory();
097: }
098: return sClassLoaderFactory;
099: }
100:
101: /**
102: * Creates and returns the installation bootstrap classloader for a JBI
103: * component.
104: * This instance is not cached by the ClassLoaderFactory as these are
105: * created and used infrequently. The bootstrap classloader is used to
106: * bootstrap the installation of a binding/engine.
107: *
108: * @param component runtime representation of the component.
109: * @throws javax.jbi.JBIException if the classloader could not be created.
110: * @throws java.lang.IllegalArgumentException if a null or empty list of
111: * class path elements is found.
112: * @return An instance of the Bootstrap classloader.
113: */
114: public ClassLoader createBootstrapClassLoader(Component component)
115: throws JBIException {
116: ClassLoader cl;
117:
118: if (null == component) {
119: throw new IllegalArgumentException(mTranslator.getString(
120: LocalStringKeys.NULL_ARGUMENT, "component"));
121: }
122:
123: List bootCpElements = component.getBootstrapClassPathElements();
124:
125: if (null == bootCpElements) {
126: throw new IllegalArgumentException(mTranslator.getString(
127: LocalStringKeys.NULL_ARGUMENT, "paths"));
128: }
129: if (bootCpElements.isEmpty()) {
130: throw new IllegalArgumentException(mTranslator.getString(
131: LocalStringKeys.EMPTY_LIST_ARGUMENT, "paths"));
132: }
133:
134: try {
135: URL[] urls = list2URLArray(bootCpElements);
136:
137: // Set the "selfFirst" flag to what is set in the component.
138: boolean selfFirst = component
139: .isBootstrapClassLoaderSelfFirst();
140:
141: // The parent classloader is the JBI shared class loader that has
142: // access to all of the public JBI APIs and SPIs.
143:
144: cl = new CustomClassLoader(urls, getJBISystemClassLoader(),
145: selfFirst);
146:
147: mLogger.finest("Created bootstrap CustomClassLoader for "
148: + component.getComponentTypeAsString() + " "
149: + component.getName() + " with class path "
150: + bootCpElements);
151: return cl;
152: } catch (Throwable e) {
153: throw new JBIException(mTranslator.getString(
154: LocalStringKeys.CL_BOOTSTRAP_CREATE_FAILED, e
155: .toString()), e);
156: }
157: }
158:
159: /**
160: * Creates and returns the classloader for a Shared Library.
161: * This classloader is used to load classes belonging to libraries
162: * that are shared between two or more JBI components.
163: * @param sharedLib A SharedLibrary object representing the library.
164: * @throws javax.jbi.JBIException If the shared classloader cannot be
165: * successfully created.
166: * @throws java.lang.IllegalArgumentException If a null argument is
167: * received.
168: * @return The shared library classloader.
169: */
170: public ClassLoader createSharedClassLoader(SharedLibrary sharedLib)
171: throws JBIException {
172: ClassLoader cl;
173:
174: if (null == sharedLib) {
175: throw new IllegalArgumentException(mTranslator.getString(
176: LocalStringKeys.NULL_ARGUMENT, "sharedLib"));
177: }
178:
179: try {
180: cl = (ClassLoader) mSharedClassLoaderCache.get(sharedLib
181: .getName());
182: if (null == cl) {
183: List slPaths = sharedLib.getClassPathElements();
184: URL[] urls = list2URLArray(slPaths);
185:
186: // Set the "selfFirst" flag to what is set for the Shared
187: // Library.
188: boolean selfFirst = sharedLib.isClassLoaderSelfFirst();
189:
190: // The parent classloader is the JBI shared class loader that
191: // has access to all of the public JBI APIs and SPIs.
192:
193: cl = new CustomClassLoader(urls,
194: getJBISystemClassLoader(), selfFirst);
195: mSharedClassLoaderCache.put(sharedLib.getName(), cl);
196: }
197: mLogger.finest("Created shared CustomClassLoader for "
198: + sharedLib.getName() + " with class path "
199: + sharedLib.getClassPathElements());
200: return cl;
201: } catch (Throwable e) {
202: throw new JBIException(mTranslator.getString(
203: LocalStringKeys.CL_SHARED_CREATE_FAILED, e
204: .toString()), e);
205: }
206: }
207:
208: /**
209: * Destroys the classloader for a Shared Library.
210: * This method should be called only when the corresponding Shared
211: * Library is being destroyed. There should never be a condition where
212: * a shared classloader does not exist for an active Shared Library.
213: * @param sharedLibId Shared Library ID.
214: * @throws javax.jbi.JBIException If a shared classloader cannot be removed.
215: * @throws java.lang.IllegalArgumentException If a null argument is received.
216: */
217: public void removeSharedClassLoader(String sharedLibId)
218: throws JBIException {
219: CustomClassLoader cl;
220:
221: if (null == sharedLibId) {
222: throw new IllegalArgumentException(mTranslator.getString(
223: LocalStringKeys.NULL_ARGUMENT, "sharedLibId"));
224: }
225: if (sharedLibId.length() == 0) {
226: throw new IllegalArgumentException(mTranslator.getString(
227: LocalStringKeys.EMPTY_STRING_ARGUMENT,
228: "sharedLibId"));
229: }
230:
231: cl = (CustomClassLoader) mSharedClassLoaderCache
232: .remove(sharedLibId);
233:
234: if (null != cl) {
235: cl.releaseResources();
236: mLogger.finest("Removed shared CustomClassLoader for "
237: + sharedLibId);
238: } else {
239: throw new JBIException(mTranslator.getString(
240: LocalStringKeys.CL_SHARED_CLASSLOADER_NOT_FOUND,
241: sharedLibId));
242: }
243: }
244:
245: /**
246: * Returns the classloader for a Shared Library.
247: * This shared class loader is shared between two or more components
248: * installed in the JBI namespace.
249: * This method is typically called by components such as NMR after the
250: * initial class loader creation by the ComponentFramework. Hence there
251: * should always be a valid classloader for a valid Shared Library.
252: * If this method throws an exception,it probably means that the library
253: * was not installed correctly or that an invalid Shared Library ID was
254: * passed in.
255: *
256: * @param sharedLibId The id of the Shared Library.
257: * @throws JBIException If there is no class loader available for the
258: * Shared Library.
259: * @return The Shared Library classloader.
260: */
261: public ClassLoader getSharedClassLoader(String sharedLibId)
262: throws JBIException {
263: if (null == sharedLibId) {
264: throw new IllegalArgumentException(mTranslator.getString(
265: LocalStringKeys.NULL_ARGUMENT, "sharedLibId"));
266: }
267: if (sharedLibId.length() == 0) {
268: throw new IllegalArgumentException(mTranslator.getString(
269: LocalStringKeys.EMPTY_STRING_ARGUMENT,
270: "sharedLibId"));
271: }
272:
273: ClassLoader sharedLoader = (ClassLoader) mSharedClassLoaderCache
274: .get(sharedLibId);
275: if (null == sharedLoader) {
276: throw new JBIException(mTranslator.getString(
277: LocalStringKeys.CL_SHARED_CLASSLOADER_NOT_FOUND,
278: sharedLibId));
279: }
280:
281: return sharedLoader;
282: }
283:
284: /**
285: * Creates and returns the classloader for a JBI component.
286: * It is used to load classes in the classpath of a component installed
287: * in the JBI namespace. It is important that all shared libraries that
288: * are referenced by this component are already installed by the time
289: * a call to this method is made as it needs these to create the
290: * <code>DelegatingClassLoader</code> chain.
291: *
292: * @param component reference to the component object
293: * @throws java.lang.SecurityException If the current thread does not have
294: * permissions to create a class loader.
295: * @throws JBIException if class loader cannot be created.
296: * create a class loader.
297: * @throws IllegalArgumentException if a null component is passed in
298: * @return The component classloader.
299: */
300: public ClassLoader createComponentClassLoader(Component component)
301: throws JBIException {
302: ClassLoader compClassLoader;
303:
304: if (null == component) {
305: throw new IllegalArgumentException(mTranslator.getString(
306: LocalStringKeys.NULL_ARGUMENT, "component"));
307: }
308:
309: try {
310: compClassLoader = (ClassLoader) mComponentClassLoaderCache
311: .get(component.getName());
312: if (null == compClassLoader) {
313: List lifeCycleCpElements = component
314: .getClassPathElements();
315: URL[] cpURLs = list2URLArray(lifeCycleCpElements);
316:
317: // Set the "selfFirst" flag to what is set for the component.
318: boolean selfFirst = component.isClassLoaderSelfFirst();
319:
320: // Create a DelegatingClassLoader instance, which is the parent
321: // of the component classloader.
322: DelegatingClassLoader dcl = createDelegatingClassLoader(component);
323: compClassLoader = new CustomClassLoader(cpURLs, dcl,
324: selfFirst);
325: mComponentClassLoaderCache.put(component.getName(),
326: compClassLoader);
327: mLogger
328: .finest("Created life cycle CustomClassLoader for "
329: + component.getComponentTypeAsString()
330: + " "
331: + component.getName()
332: + " with class path "
333: + lifeCycleCpElements);
334: }
335: } catch (JBIException je) {
336: throw je;
337: } catch (Throwable e) {
338: throw new JBIException(mTranslator.getString(
339: LocalStringKeys.CL_COMPONENT_CREATE_FAILED, e
340: .toString()), e);
341: }
342: return compClassLoader;
343: }
344:
345: /**
346: * Removes the classloader of a JBI component.
347: * This should only be called when a component is being uninstalled.
348: *
349: * @param componentId The component ID
350: * @throws JBIException If the classloader cannot be removed.
351: * @throws IllegalArgumentException If a null component ID is passed in.
352: */
353: public void removeComponentClassLoader(String componentId)
354: throws JBIException {
355: CustomClassLoader compClassLoader;
356:
357: if (null == componentId) {
358: throw new IllegalArgumentException(mTranslator.getString(
359: LocalStringKeys.NULL_ARGUMENT, "componentId"));
360: }
361: if (componentId.length() == 0) {
362: throw new IllegalArgumentException(mTranslator.getString(
363: LocalStringKeys.EMPTY_STRING_ARGUMENT,
364: "componentId"));
365: }
366:
367: compClassLoader = (CustomClassLoader) mComponentClassLoaderCache
368: .remove(componentId);
369: if (null != compClassLoader) {
370: compClassLoader.releaseResources();
371: mLogger.finest("Removed life cycle CustomClassLoader for "
372: + componentId);
373: } else {
374: throw new JBIException(mTranslator.getString(
375: LocalStringKeys.CL_COMPONENT_CLASSLOADER_NOT_FOUND,
376: componentId));
377: }
378: }
379:
380: /**
381: * Returns the classloader of a JBI component (binding or engine).
382: * This method is typically called by components such as NMS after the
383: * initial class loader creation by the ComponentFramework. Hence there
384: * should always be a valid classloader for a valid component. If this
385: * method throws an exception,it probably means that the component was not
386: * installed correctly or that an invalid component id was passed in.
387: *
388: * @param componentId the id of the binding or engine
389: * @throws JBIException If there is no class loader available for the
390: * component.
391: * @return The classloader for the component.
392: */
393: public ClassLoader getComponentClassLoader(String componentId)
394: throws JBIException {
395: if (null == componentId) {
396: throw new IllegalArgumentException(mTranslator.getString(
397: LocalStringKeys.NULL_ARGUMENT, "componentId"));
398: }
399: if (componentId.length() == 0) {
400: throw new IllegalArgumentException(mTranslator.getString(
401: LocalStringKeys.EMPTY_STRING_ARGUMENT,
402: "componentId"));
403: }
404:
405: ClassLoader compLoader = (ClassLoader) mComponentClassLoaderCache
406: .get(componentId);
407: if (null == compLoader) {
408: throw new JBIException(mTranslator.getString(
409: LocalStringKeys.CL_COMPONENT_CLASSLOADER_NOT_FOUND,
410: componentId));
411: }
412: return compLoader;
413: }
414:
415: /**
416: * Private method to create a DelegatingClassLoader. This class loader has
417: * a chain of all Shared Library class loaders required by the component.
418: *
419: * @param comp the Component instance for the component.
420: * @return an instance of DelegatingClassLoader that has access to all
421: * the Shared Library class loaders required for the component.
422: * @throws Exception If the class loader creation is unsucessful.
423: */
424: private DelegatingClassLoader createDelegatingClassLoader(
425: Component comp) throws Exception {
426: mLogger.finest("Creating DelegatingClassLoader for "
427: + comp.getComponentTypeAsString() + " "
428: + comp.getName());
429:
430: DelegatingClassLoader dcl = new DelegatingClassLoader(
431: getJBISystemClassLoader());
432:
433: List sharedLibraries = comp.getSharedLibraryNames();
434:
435: // Add any Shared Library class loaders required by the component.
436:
437: if (null != sharedLibraries) {
438: for (Iterator slItr = sharedLibraries.listIterator(); slItr
439: .hasNext();) {
440: String slName = (String) slItr.next();
441: ClassLoader sharedLibLoader = getSharedClassLoader(slName);
442:
443: // set a reference to the SharedLibrary for this component
444: // into the Delegating ClassLoader
445:
446: dcl.addSharedClassLoader(sharedLibLoader);
447: mLogger.finest("Added shared CustomClassLoader for "
448: + slName + " to DelegatingClassLoader");
449: }
450: }
451: return dcl;
452: }
453:
454: /**
455: * Private method to convert a List into a URL array.
456: *
457: * @param paths list of String elements representing paths and JAR file
458: * names
459: * @return java.net.URL[] array representing the URLs corresponding to the
460: * paths, or null if the list is null or if there is an exception creating
461: * the array.
462: * @throws Exception If the array creation is unsucessful.
463: */
464: private URL[] list2URLArray(List paths) throws Exception {
465: File f;
466: URL[] urls = new URL[paths.size()];
467: int i = 0;
468: for (Iterator itr = paths.listIterator(); itr.hasNext();) {
469: String nextCPElement = (String) itr.next();
470: f = new File(nextCPElement);
471: urls[i++] = f.toURL();
472: }
473: return urls;
474: }
475:
476: /**
477: * Private method that returns the JBI "System class loader". This method
478: * delegates to the PlatformContext for the current platform to obtain
479: * the appropriate class loader.
480: *
481: * @return ClassLoader representing the JBI common class loader.
482: * @throws SecurityException If access to the class loader was denied.
483: */
484: private ClassLoader getJBISystemClassLoader() {
485: ClassLoader systemClassLoader = null;
486:
487: PlatformContext platform = EnvironmentContext.getInstance()
488: .getPlatformContext();
489: systemClassLoader = platform.getSystemClassLoader();
490:
491: // Special case for unit testing only
492: if (null != System.getProperty("junit.srcroot")) {
493: systemClassLoader = this.getClass().getClassLoader();
494: }
495:
496: return systemClassLoader;
497: }
498: }
|