You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by rs...@apache.org on 2002/06/28 23:28:13 UTC
cvs commit: jakarta-commons-sandbox/discovery/src/java/org/apache/commons/service/discovery ClassLoaderUtils.java SPIContext.java ClassFinder.java ServiceFinder.java
rsitze 2002/06/28 14:28:12
Modified: discovery/src/java/org/apache/commons/service/discovery
SPIContext.java ClassFinder.java ServiceFinder.java
Added: discovery/src/java/org/apache/commons/service/discovery
ClassLoaderUtils.java
Log:
In most cases, after class NAME is found, then a number of attempts
are made to load the class using different class loaders, in the following
sequence:
- Thread Context Class Loader
- Caller's Class Loader
- SPI's Class Loader
- ServiceFinder's (this class) Class Loader
- System Class Loader
The default implementation is loaded using:
- ServiceFinder's (this class) Class Loader
- System Class Loader
Revision Changes Path
1.2 +65 -2 jakarta-commons-sandbox/discovery/src/java/org/apache/commons/service/discovery/SPIContext.java
Index: SPIContext.java
===================================================================
RCS file: /home/cvs/jakarta-commons-sandbox/discovery/src/java/org/apache/commons/service/discovery/SPIContext.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- SPIContext.java 26 Jun 2002 15:50:15 -0000 1.1
+++ SPIContext.java 28 Jun 2002 21:28:12 -0000 1.2
@@ -83,10 +83,16 @@
/**
* Thread context class loader or null if not available (JDK 1.1).
*/
- private ClassLoader threadContextClassLoader =
+ private final ClassLoader threadContextClassLoader =
findThreadContextClassLoader();
/**
+ * System class loader or null if not available (JDK 1.1).
+ */
+ private final ClassLoader systemClassLoader =
+ findSystemClassLoader();
+
+ /**
* The service programming interface: intended to be
* an interface or abstract class, but not limited
* to those two.
@@ -101,6 +107,10 @@
return threadContextClassLoader;
}
+ public ClassLoader getSystemClassLoader() {
+ return systemClassLoader;
+ }
+
public Class getSPI() {
return spi;
}
@@ -112,7 +122,7 @@
* The thread context class loader is available for JDK 1.2
* or later, if certain security conditions are met.
*
- * @exception SecurityException if a suitable class loader
+ * @exception ServiceException if a suitable class loader
* cannot be identified.
*/
private static ClassLoader findThreadContextClassLoader()
@@ -148,6 +158,59 @@
* the logic below, but other calls elsewhere (to obtain
* a class loader) will re-trigger this exception where
* we can make a distinction.
+ */
+ if (e.getTargetException() instanceof SecurityException) {
+ classLoader = null; // ignore
+ } else {
+ // Capture 'e.getTargetException()' exception for details
+ // alternate: log 'e.getTargetException()', and pass back 'e'.
+ throw new ServiceException
+ ("Unexpected InvocationTargetException",
+ e.getTargetException());
+ }
+ }
+ } catch (NoSuchMethodException e) {
+ // Assume we are running on JDK 1.1
+ classLoader = null;
+ }
+
+ // Return the selected class loader
+ return classLoader;
+ }
+
+ /**
+ * Return the system class loader if available.
+ * Otherwise return null.
+ *
+ * The system class loader is available for JDK 1.2
+ * or later, if certain security conditions are met.
+ *
+ * @exception ServiceException if a suitable class loader
+ * cannot be identified.
+ */
+ private static ClassLoader findSystemClassLoader()
+ throws ServiceException
+ {
+ ClassLoader classLoader = null;
+
+ try {
+ // Are we running on a JDK 1.2 or later system?
+ Method method = ClassLoader.class.getMethod("getSystemClassLoader", null);
+
+ // Get the system class loader (if there is one)
+ try {
+ classLoader =
+ (ClassLoader)method.invoke(null, null);
+ } catch (IllegalAccessException e) {
+ throw new ServiceException("Unexpected IllegalAccessException", e);
+ } catch (InvocationTargetException e) {
+ /**
+ * InvocationTargetException is thrown by 'invoke' when
+ * the method being invoked (ClassLoader.getSystemClassLoader)
+ * throws an exception.
+ *
+ * ClassLoader.getSystemClassLoader() throws SecurityException
+ * if security permissions are restricted.
*/
if (e.getTargetException() instanceof SecurityException) {
classLoader = null; // ignore
1.2 +50 -72 jakarta-commons-sandbox/discovery/src/java/org/apache/commons/service/discovery/ClassFinder.java
Index: ClassFinder.java
===================================================================
RCS file: /home/cvs/jakarta-commons-sandbox/discovery/src/java/org/apache/commons/service/discovery/ClassFinder.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ClassFinder.java 26 Jun 2002 15:50:15 -0000 1.1
+++ ClassFinder.java 28 Jun 2002 21:28:12 -0000 1.2
@@ -90,79 +90,27 @@
* looking for an implementation of.
*/
private final SPIContext spiContext;
+ private final Class finderClass;
- public ClassFinder(SPIContext spiContext) {
+ private final ClassLoader[] localLoaders;
+ private final ClassLoader[] allLoaders;
+
+ public ClassFinder(SPIContext spiContext, Class finderClass) {
this.spiContext = spiContext;
+ this.finderClass = finderClass;
+ this.localLoaders = getLocalLoaders(spiContext, finderClass);
+ this.allLoaders = getAllLoaders(spiContext, finderClass);
+
//System.out.println("Finding '" + spiContext.getSPI().getName() + "'");
}
- public ClassFinder(Class spi) {
+ public ClassFinder(Class spi, Class finderClass) {
this.spiContext = new SPIContext(spi);
- //System.out.println("Finding '" + spi.getName() + "'");
- }
-
- public ClassLoader getThreadContextClassLoader() {
- return spiContext.getThreadContextClassLoader();
- }
-
- /**
- * Load the class serviceImplName.
- * First try to load the class with the thread context class loader,
- * and failing that try the local class loader.
- *
- * @param serviceImplName The name of the class to load.
- */
- public static Class load(String serviceImplName,
- ClassLoader threadContextClassLoader,
- ClassLoader localClassLoader)
- throws ServiceException
- {
- //System.out.println("Loading '" + serviceImplName + "'");
+ this.finderClass = finderClass;
+ this.localLoaders = getLocalLoaders(spiContext, finderClass);
+ this.allLoaders = getAllLoaders(spiContext, finderClass);
- Class clazz = null;
-
- if (serviceImplName != null && serviceImplName.length() > 0) {
- if (threadContextClassLoader != null) {
- try {
- // first the thread class loader
- clazz = threadContextClassLoader.loadClass(serviceImplName);
- } catch (ClassNotFoundException e) {
- clazz = null;
- }
- }
-
- if (clazz == null &&
- localClassLoader != null &&
- localClassLoader != threadContextClassLoader) {
- // if threadContextClassLoder was null or if it failed
- // (i.e. no implementation is found in the webapp), try
- // the local loader if we haven't already...
- try {
- // clazz = spi.getClassLoader().loadClass(serviceImplName);
- clazz = localClassLoader.loadClass(serviceImplName);
- } catch (ClassNotFoundException e) {
- clazz = null;
- }
- }
- }
-
- return clazz;
- }
-
- /**
- * Load the class serviceImplName.
- * First try to load the class with the SPI context's
- * thread context class loader, and failing that try
- * the class loader that loaded the SPI.
- *
- * @param serviceImplName The name of the class to load.
- */
- public static Class load(String serviceImplName, SPIContext spiContext)
- throws ServiceException
- {
- return load(serviceImplName,
- spiContext.getThreadContextClassLoader(),
- spiContext.getSPI().getClassLoader());
+ //System.out.println("Finding '" + spi.getName() + "'");
}
/**
@@ -181,9 +129,8 @@
public Class find(String serviceImplName, boolean localOnly)
throws ServiceException
{
- Class clazz = load(serviceImplName,
- localOnly ? null : getThreadContextClassLoader(),
- spiContext.getSPI().getClassLoader());
+ Class clazz = ClassLoaderUtils.loadUniq(serviceImplName,
+ localOnly ? localLoaders : allLoaders);
if (clazz != null && !spiContext.getSPI().isAssignableFrom(clazz)) {
throw new ServiceException("Class " + serviceImplName +
@@ -260,10 +207,13 @@
// Name of J2EE application file that identifies the service implementation.
String servicePropertyFile = SERVICE_HOME + spiContext.getSPI().getName();
+
+ ClassLoader contextLoader = spiContext.getThreadContextClassLoader();
- InputStream is = (getThreadContextClassLoader() == null
+ InputStream is = (contextLoader == null
? ClassLoader.getSystemResourceAsStream(servicePropertyFile)
- : getThreadContextClassLoader().getResourceAsStream(servicePropertyFile));
+ : contextLoader.getResourceAsStream(servicePropertyFile));
+
if( is != null ) {
try {
try {
@@ -277,7 +227,6 @@
rd = new BufferedReader(new InputStreamReader(is));
}
-
try {
serviceImplName = rd.readLine();
} finally {
@@ -292,5 +241,34 @@
}
return serviceImplName;
+ }
+
+ /**
+ * MAGIC: as in, I don't have a clue how to go about this...
+ * SecurityManager.getClassContext() is out of reach at best,
+ * and getting to it in a J2EE environment is likely to be beyond
+ * hopeless....
+ */
+ private static final ClassLoader getCallerClassLoader(Class finderClass) {
+ return null;
+ }
+
+ private static final ClassLoader[] getLocalLoaders(SPIContext spiContext,
+ Class finderClass) {
+ return ClassLoaderUtils.compactUniq(new ClassLoader[] {
+ finderClass.getClassLoader(),
+ spiContext.getSystemClassLoader()
+ });
+ }
+
+ private static final ClassLoader[] getAllLoaders(SPIContext spiContext,
+ Class finderClass) {
+ return ClassLoaderUtils.compactUniq(new ClassLoader[] {
+ spiContext.getThreadContextClassLoader(),
+ getCallerClassLoader(finderClass),
+ spiContext.getSPI().getClassLoader(),
+ finderClass.getClassLoader(),
+ spiContext.getSystemClassLoader()
+ });
}
}
1.2 +108 -7 jakarta-commons-sandbox/discovery/src/java/org/apache/commons/service/discovery/ServiceFinder.java
Index: ServiceFinder.java
===================================================================
RCS file: /home/cvs/jakarta-commons-sandbox/discovery/src/java/org/apache/commons/service/discovery/ServiceFinder.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ServiceFinder.java 26 Jun 2002 15:50:15 -0000 1.1
+++ ServiceFinder.java 28 Jun 2002 21:28:12 -0000 1.2
@@ -93,7 +93,7 @@
/**
* <p>Locate and instantiate a service. The service implementation
- * class is located using the following ordered lookup procedure:</p>
+ * class is located using the following ordered lookup:</p>
* <ul>
* <li>Try to load a class with the name obtained from the system
* property, having the same name as the spi class:
@@ -111,9 +111,28 @@
*
* <li>Fall back to a default implementation class, as specified by
* non-null <code>defaultImplName</code>.</li>
+ * </ul>
*
+ * <p>In most cases, after class NAME is found, then a
+ * number of attempts are made to load the class using different
+ * class loaders, in the following sequence:
+ * <ul>
+ * <li>Thread Context Class Loader</li>
+ * <li>Caller's Class Loader</li>
+ * <li>SPI's Class Loader</li>
+ * <li>ServiceFinder's (this class) Class Loader</li>
+ * <li>System Class Loader</li>
+ * </ul>
+ *
+ * <p>The default implementation is loaded using:
+ * <ul>
+ * <li>ServiceFinder's (this class) Class Loader</li>
+ * <li>System Class Loader</li>
* </ul>
*
+ * @param finderClass The root finder class, which may not
+ * be 'ServiceFinder' if a wrapper class is used.
+ *
* @param spiContext The SPI Context identifies the SPI and the
* thread context class loader.
* <code>spiContext.getSPI().getName()</code> id's the (property)
@@ -135,21 +154,22 @@
* cannot be instantiated,
* or is not an instance of <code>spi</code>.
*/
- public static Object find(SPIContext spiContext,
+ public static Object find(Class finderClass,
+ SPIContext spiContext,
Properties properties,
String defaultImplName)
throws ServiceException
{
// thread context can change on each call,
// so establish context for this one call.
- ClassFinder classFinder = new ClassFinder(spiContext);
+ ClassFinder classFinder = new ClassFinder(spiContext, finderClass);
/**
* Return previously registered service object (not class)
* for this spi. Try contextClassLoader first, and if that
* fails then try the local class loader.
*/
- Object service = (classFinder.getThreadContextClassLoader() == null)
+ Object service = (spiContext.getThreadContextClassLoader() == null)
? null
: get(spiContext.getSPI().getName(), spiContext.getThreadContextClassLoader());
@@ -204,21 +224,47 @@
/**
* Equivalent to
+ * <code>find(ServiceFinder.class, spiContext, properties, defaultImplName)</code>.
+ */
+ public static Object find(SPIContext spiContext,
+ Properties properties,
+ String defaultImplName)
+ throws ServiceException
+ {
+ return find(ServiceFinder.class, spiContext, properties, defaultImplName);
+ }
+
+ /**
+ * Equivalent to
* <code>find(new SPIContext(spi), properties, defaultImplName)</code>.
*/
+ public static Object find(Class finderClass,
+ Class spi,
+ Properties properties,
+ String defaultImplName)
+ throws ServiceException
+ {
+ return find(finderClass, new SPIContext(spi), properties, defaultImplName);
+ }
+
+ /**
+ * Equivalent to
+ * <code>find(ServiceFinder.class, spi, properties, defaultImplName)</code>.
+ */
public static Object find(Class spi,
Properties properties,
String defaultImplName)
throws ServiceException
{
- return find(new SPIContext(spi), properties, defaultImplName);
+ return find(ServiceFinder.class, spi, properties, defaultImplName);
}
/**
* Load properties file, and call
* <code>find(spiContext, properties, defaultImplName)</code>.
*/
- public static Object find(SPIContext spiContext,
+ public static Object find(Class finderClass,
+ SPIContext spiContext,
String propertiesFileName,
String defaultImplName)
throws ServiceException
@@ -243,10 +289,35 @@
}
}
- return find(spiContext, properties, defaultImplName);
+ return find(finderClass, spiContext, properties, defaultImplName);
}
/**
+ * Load properties file, and call
+ * <code>find(ServiceFinder.class, spiContext, propertiesFileName, defaultImplName)</code>.
+ */
+ public static Object find(SPIContext spiContext,
+ String propertiesFileName,
+ String defaultImplName)
+ throws ServiceException
+ {
+ return find(ServiceFinder.class, spiContext, propertiesFileName, defaultImplName);
+ }
+
+ /**
+ * Equivalent to
+ * <code>find(finderClass, new SPIContext(spi), propertiesFileName, defaultImplName)</code>.
+ */
+ public static Object find(Class finderClass,
+ Class spi,
+ String propertiesFileName,
+ String defaultImplName)
+ throws ServiceException
+ {
+ return find(finderClass, new SPIContext(spi), propertiesFileName, defaultImplName);
+ }
+
+ /**
* Equivalent to
* <code>find(new SPIContext(spi), propertiesFileName, defaultImplName)</code>.
*/
@@ -260,6 +331,16 @@
/**
* Find implementation of SPI.
+ * Equivalent to find(finderClass, spi, (Properties)null, defaultImplName);
+ */
+ public static Object find(Class finderClass, Class spi, String defaultImplName)
+ throws ServiceException
+ {
+ return find(finderClass, spi, (Properties)null, defaultImplName);
+ }
+
+ /**
+ * Find implementation of SPI.
* Equivalent to find(spi, (Properties)null, defaultImplName);
*/
public static Object find(Class spi, String defaultImplName)
@@ -270,12 +351,32 @@
/**
* Find implementation of SPI.
+ * Equivalent to find(finderClass, spi, properties, null);
+ */
+ public static Object find(Class finderClass, Class spi, Properties properties)
+ throws ServiceException
+ {
+ return find(finderClass, spi, properties, null);
+ }
+
+ /**
+ * Find implementation of SPI.
* Equivalent to find(spi, properties, null);
*/
public static Object find(Class spi, Properties properties)
throws ServiceException
{
return find(spi, properties, null);
+ }
+
+ /**
+ * Find implementation of SPI.
+ * Equivalent to find(finderClass, spi, (Properties)null, null);
+ */
+ public static Object find(Class finderClass, Class spi)
+ throws ServiceException
+ {
+ return find(finderClass, spi, (Properties)null, null);
}
/**
1.1 jakarta-commons-sandbox/discovery/src/java/org/apache/commons/service/discovery/ClassLoaderUtils.java
Index: ClassLoaderUtils.java
===================================================================
/*
* $Header$
* $Revision$
* $Date$
*
* ====================================================================
*
* The Apache Software License, Version 1.1
*
* Copyright (c) 1999-2002 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution, if
* any, must include the following acknowlegement:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowlegement may appear in the software itself,
* if and wherever such third-party acknowlegements normally appear.
*
* 4. The names "The Jakarta Project", "Commons", and "Apache Software
* Foundation" must not be used to endorse or promote products derived
* from this software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache"
* nor may "Apache" appear in their names without prior written
* permission of the Apache Group.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
*/
package org.apache.commons.service.discovery;
import java.io.InputStream;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.Properties;
import java.lang.reflect.Array;
/**
* Mechanisms to locate and load a class.
* The load methods locate a class only.
* The find methods locate a class and verify that the
* class implements an given interface or extends a given class.
*
* @author Richard A. Sitze
* @author Craig R. McClanahan
* @author Costin Manolache
*/
public class ClassLoaderUtils {
private static final boolean debug = false;
/**
* Load the class serviceImplName, no safety checking
*
* @param serviceImplName The name of the class to load.
*/
private static Class rawLoad(String serviceImplName, ClassLoader loader)
throws ServiceException
{
Class clazz = null;
try {
// first the thread class loader
clazz = loader.loadClass(serviceImplName);
} catch (ClassNotFoundException e) {
clazz = null;
}
return clazz;
}
/**
* Load the class serviceImplName.
*
* @param serviceImplName The name of the class to load.
*/
public static Class load(String serviceImplName, ClassLoader loader)
throws ServiceException
{
if (debug)
System.out.println("Loading '" + serviceImplName + "'");
return (loader != null &&
serviceImplName != null &&
serviceImplName.length() > 0
)
? rawLoad(serviceImplName, loader)
: null;
}
/**
* Load the class serviceImplName.
* Try each classloader in succession,
* until first succeeds, or all fail.
*
* @param serviceImplName The name of the class to load.
*/
public static Class loadUniq(String serviceImplName, ClassLoader[] uniqLoaders, int length)
throws ServiceException
{
if (debug)
System.out.println("Loading '" + serviceImplName + "'");
Class clazz = null;
if (serviceImplName != null && serviceImplName.length() > 0) {
for (int i = 0; i < length && clazz == null; i++)
{
clazz = (uniqLoaders[i] != null)
? rawLoad(serviceImplName, uniqLoaders[i])
: null;
}
}
return clazz;
}
/**
* Load the class serviceImplName.
* Try each classloader in succession,
* until first succeeds, or all fail.
*
* @param serviceImplName The name of the class to load.
*/
public static Class loadUniq(String serviceImplName, ClassLoader[] uniqLoaders)
throws ServiceException
{
return loadUniq(serviceImplName, uniqLoaders, uniqLoaders.length);
}
/**
* Load the class serviceImplName.
* Try each classloader in succession,
* until first succeeds, or all fail.
*
* @param serviceImplName The name of the class to load.
*/
public static Class load(String serviceImplName, ClassLoader[] loaders)
throws ServiceException
{
ClassLoader[] uniqLoaders = new ClassLoader[loaders.length];
int length = uniq(loaders, uniqLoaders);
return loadUniq(serviceImplName, uniqLoaders, length);
}
/**
* Load the class serviceImplName.
*
* @param serviceImplName The name of the class to load.
*/
public static Class load(String serviceImplName,
ClassLoader loaderA,
ClassLoader loaderB)
throws ServiceException
{
return load(serviceImplName,
new ClassLoader[] { loaderA, loaderB });
}
/**
* Load the class serviceImplName.
* First try to load the class with the SPI context's
* thread context class loader, and failing that try
* the class loader that loaded the SPI.
*
* @param serviceImplName The name of the class to load.
*/
public static Class load(String serviceImplName, SPIContext spiContext)
throws ServiceException
{
return load(serviceImplName,
spiContext.getThreadContextClassLoader(),
spiContext.getSPI().getClassLoader());
}
/***
* Remove duplicate Objects (as opposed to equivalent) from
* array. Also checks previous class loader parents..
*
* Assumes that array is short, so (n^2)*m isn't a problem...
*
* This is exposed to allow a unique array to be computed once,
* and passed in for different tasks...
*/
public static int uniq(ClassLoader[] array, ClassLoader[] uneek) {
int len = 0;
for (int lookForward = 0; lookForward < array.length; lookForward++) {
ClassLoader fore = array[lookForward];
if (fore != null) {
boolean seen = false;
for (int lookBack = 0; !seen && lookBack < len; lookBack++) {
ClassLoader back = uneek[lookBack];
/**
* Look to see if the current ClassLoader (fore)
* is already in the list. Also review parent
* class loaders of those already in the list.
*/
while (!seen && back != null) {
if (back == fore) {
seen = true;
}
back = back.getParent();
}
}
if (!seen) {
uneek[len++] = fore;
}
}
}
return len;
}
public static final ClassLoader[] compactUniq(ClassLoader[] array) {
ClassLoader[] uniqLoaders = new ClassLoader[array.length];
int length = ClassLoaderUtils.uniq(array, uniqLoaders);
ClassLoader[] loaders = new ClassLoader[length];
System.arraycopy(uniqLoaders, 0, loaders, 0, length);
return loaders;
}
}
--
To unsubscribe, e-mail: <ma...@jakarta.apache.org>
For additional commands, e-mail: <ma...@jakarta.apache.org>