org.apache.commons.discovery.tools.DiscoverClass Maven / Gradle / Ivy
Show all versions of commons-discovery Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.commons.discovery.tools;
import java.lang.reflect.InvocationTargetException;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;
import org.apache.commons.discovery.DiscoveryException;
import org.apache.commons.discovery.ResourceClass;
import org.apache.commons.discovery.ResourceClassIterator;
import org.apache.commons.discovery.ResourceNameIterator;
import org.apache.commons.discovery.defaults.Defaults;
import org.apache.commons.discovery.defaults.MyLogFactory;
import org.apache.commons.discovery.resource.ClassLoaders;
import org.apache.commons.discovery.resource.classes.DiscoverClasses;
import org.apache.commons.discovery.resource.names.DiscoverServiceNames;
/**
* Discover class that implements a given service interface,
* with discovery and configuration features similar to that employed
* by standard Java APIs such as JAXP.
*
*
* In the context of this package, a service interface is defined by a
* Service Provider Interface (SPI). The SPI is expressed as a Java interface,
* abstract class, or (base) class that defines an expected programming
* interface.
*
*
* DiscoverClass provides the find
methods for locating a
* class that implements a service interface (SPI). Each form of
* find
varies slightly, but they all perform the same basic
* function.
*
* The DiscoverClass.find
methods proceed as follows:
*
*
* -
* Get the name of an implementation class. The name is the first
* non-null value obtained from the following resources:
*
* -
* The value of the (scoped) system property whose name is the same as
* the SPI's fully qualified class name (as given by SPI.class.getName()).
* The
ScopedProperties
class provides a way to bind
* properties by classloader, in a secure hierarchy similar in concept
* to the way classloader find class and resource files.
* See ScopedProperties
for more details.
* If the ScopedProperties are not set by users, then behaviour
* is equivalent to System.getProperty()
.
*
*
* -
* The value of a
Properties properties
property, if provided
* as a parameter, whose name is the same as the SPI's fully qualifed class
* name (as given by SPI.class.getName()).
*
* -
* The value obtained using the JDK1.3+ 'Service Provider' specification
* (http://java.sun.com/j2se/1.3/docs/guide/jar/jar.html) to locate a
* service named
SPI.class.getName()
. This is implemented
* internally, so there is not a dependency on JDK 1.3+.
*
*
*
* -
* If the name of the implementation class is non-null, load that class.
* The class loaded is the first class loaded by the following sequence
* of class loaders:
*
* - Thread Context Class Loader
* - DiscoverSingleton's Caller's Class Loader
* - SPI's Class Loader
* - DiscoverSingleton's (this class or wrapper) Class Loader
* - System Class Loader
*
* An exception is thrown if the class cannot be loaded.
*
* -
* If the name of the implementation class is null, AND the default
* implementation class name (
defaultImpl
) is null,
* then an exception is thrown.
*
* -
* If the name of the implementation class is null, AND the default
* implementation class (
defaultImpl
) is non-null,
* then load the default implementation class. The class loaded is the
* first class loaded by the following sequence of class loaders:
*
* - SPI's Class Loader
* - DiscoverSingleton's (this class or wrapper) Class Loader
* - System Class Loader
*
*
* This limits the scope in which the default class loader can be found
* to the SPI, DiscoverSingleton, and System class loaders. The assumption here
* is that the default implementation is closely associated with the SPI
* or system, and is not defined in the user's application space.
*
*
* An exception is thrown if the class cannot be loaded.
*
*
* -
* Verify that the loaded class implements the SPI: an exception is thrown
* if the loaded class does not implement the SPI.
*
*
*
*
* IMPLEMENTATION NOTE - This implementation is modelled
* after the SAXParserFactory and DocumentBuilderFactory implementations
* (corresponding to the JAXP pluggability APIs) found in Apache Xerces.
*
*
* @version $Revision: 1090010 $ $Date: 2011-04-07 23:05:58 +0200 (Thu, 07 Apr 2011) $
*/
public class DiscoverClass {
/**
* Readable placeholder for a null value.
*/
public static final PropertiesHolder nullProperties = null;
/**
* The class loaders holder.
*/
private final ClassLoaders classLoaders;
/**
* Create a class instance with dynamic environment
* (thread context class loader is determined on each call).
*
* Dynamically construct class loaders on each call.
*/
public DiscoverClass() {
this(null);
}
/**
* Create a class instance with dynamic environment
* (thread context class loader is determined on each call).
*
* Cache static list of class loaders for each call.
*
* @param classLoaders The class loaders holder
*/
public DiscoverClass(ClassLoaders classLoaders) {
this.classLoaders = classLoaders;
}
/**
* Return the class loaders holder for the given SPI.
*
* @param spiClass The SPI type
* @return The class loaders holder for the given SPI
*/
public ClassLoaders getClassLoaders(@SuppressWarnings("unused") Class> spiClass) {
return classLoaders;
}
/**
* Find class implementing SPI.
*
* @param The SPI type
* @param Any class extending T
* @param spiClass Service Provider Interface Class.
* @return Class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded, or if
* the resulting class does not implement (or extend) the SPI.
*/
public Class find(Class spiClass) throws DiscoveryException {
return find(getClassLoaders(spiClass),
new SPInterface(spiClass),
nullProperties,
(DefaultClassHolder) null);
}
/**
* Find class implementing SPI.
*
* @param The SPI type
* @param Any class extending T
* @param spiClass Service Provider Interface Class.
* @param properties Used to determine name of SPI implementation.
* @return Class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded, or if
* the resulting class does not implement (or extend) the SPI.
*/
public Class find(Class spiClass, Properties properties) throws DiscoveryException {
return find(getClassLoaders(spiClass),
new SPInterface(spiClass),
new PropertiesHolder(properties),
(DefaultClassHolder) null);
}
/**
* Find class implementing SPI.
*
* @param The SPI type
* @param Any class extending T
* @param spiClass Service Provider Interface Class.
* @param defaultImpl Default implementation name.
* @return Class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded, or if
* the resulting class does not implement (or extend) the SPI.
*/
public Class find(Class spiClass, String defaultImpl) throws DiscoveryException {
return find(getClassLoaders(spiClass),
new SPInterface(spiClass),
nullProperties,
new DefaultClassHolder(defaultImpl));
}
/**
* Find class implementing SPI.
*
* @param The SPI type
* @param Any class extending T
* @param spiClass Service Provider Interface Class.
* @param properties Used to determine name of SPI implementation,.
* @param defaultImpl Default implementation class.
* @return Class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded, or if
* the resulting class does not implement (or extend) the SPI.
*/
public Class find(Class spiClass, Properties properties, String defaultImpl)
throws DiscoveryException {
return find(getClassLoaders(spiClass),
new SPInterface(spiClass),
new PropertiesHolder(properties),
new DefaultClassHolder(defaultImpl));
}
/**
* Find class implementing SPI.
*
* @param The SPI type
* @param Any class extending T
* @param spiClass Service Provider Interface Class.
* @param propertiesFileName Used to determine name of SPI implementation,.
* @param defaultImpl Default implementation class.
* @return Class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded, or if
* the resulting class does not implement (or extend) the SPI.
*/
public Class find(Class spiClass, String propertiesFileName, String defaultImpl)
throws DiscoveryException {
return find(getClassLoaders(spiClass),
new SPInterface(spiClass),
new PropertiesHolder(propertiesFileName),
new DefaultClassHolder(defaultImpl));
}
/**
* Find class implementing SPI.
*
* @param The SPI type
* @param Any class extending T
* @param loaders The class loaders holder
* @param spi Service Provider Interface Class.
* @param properties Used to determine name of SPI implementation,.
* @param defaultImpl Default implementation class.
* @return Class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded, or if
* the resulting class does not implement (or extend) the SPI.
*/
public static Class find(ClassLoaders loaders,
SPInterface spi,
PropertiesHolder properties,
DefaultClassHolder defaultImpl) throws DiscoveryException {
if (loaders == null) {
loaders = ClassLoaders.getLibLoaders(spi.getSPClass(),
DiscoverClass.class,
true);
}
Properties props = (properties == null)
? null
: properties.getProperties(spi, loaders);
String[] classNames = discoverClassNames(spi, props);
Exception error = null;
if (classNames.length > 0) {
DiscoverClasses classDiscovery = new DiscoverClasses(loaders);
for (String className : classNames) {
ResourceClassIterator classes =
classDiscovery.findResourceClasses(className);
// If it's set as a property.. it had better be there!
if (classes.hasNext()) {
ResourceClass info = classes.nextResourceClass();
try {
return info.loadClass();
} catch (Exception e) {
error = e;
}
}
}
} else {
ResourceNameIterator classIter =
(new DiscoverServiceNames(loaders)).findResourceNames(spi.getSPName());
ResourceClassIterator classes =
(new DiscoverClasses(loaders)).findResourceClasses(classIter);
if (!classes.hasNext() && defaultImpl != null) {
Class> impl = Defaults.instance().findClass(spi.getSPName());
if (impl != null) return (Class) impl;
return defaultImpl.getDefaultClass(spi, loaders);
}
// Services we iterate through until we find one that loads..
while (classes.hasNext()) {
ResourceClass info = classes.nextResourceClass();
try {
return info.loadClass();
} catch (Exception e) {
error = e;
}
}
}
Class> impl = Defaults.instance().findClass(spi.getSPName());
if (impl != null) return (Class) impl;
throw new DiscoveryException("No implementation defined for " + spi.getSPName(), error);
// return null;
}
/**
* Create new instance of class implementing SPI.
*
* @param The SPI type
* @param spiClass Service Provider Interface Class.
* @return Instance of a class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded and
* instantiated, or if the resulting class does not implement
* (or extend) the SPI.
* @throws InstantiationException see {@link Class#newInstance()}
* @throws IllegalAccessException see {@link Class#newInstance()}
* @throws NoSuchMethodException see {@link Class#newInstance()}
* @throws InvocationTargetException see {@link Class#newInstance()}
*/
public T newInstance(Class spiClass)
throws DiscoveryException,
InstantiationException,
IllegalAccessException,
NoSuchMethodException,
InvocationTargetException {
return newInstance(getClassLoaders(spiClass),
new SPInterface(spiClass),
nullProperties,
(DefaultClassHolder) null);
}
/**
* Create new instance of class implementing SPI.
*
* @param The SPI type
* @param spiClass Service Provider Interface Class.
* @param properties Used to determine name of SPI implementation,
* and passed to implementation.init() method if
* implementation implements Service interface.
* @return Instance of a class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded and
* instantiated, or if the resulting class does not implement
* (or extend) the SPI.
* @throws InstantiationException see {@link Class#newInstance()}
* @throws IllegalAccessException see {@link Class#newInstance()}
* @throws NoSuchMethodException see {@link Class#newInstance()}
* @throws InvocationTargetException see {@link Class#newInstance()}
*/
public T newInstance(Class spiClass, Properties properties) throws DiscoveryException,
InstantiationException,
IllegalAccessException,
NoSuchMethodException,
InvocationTargetException {
return newInstance(getClassLoaders(spiClass),
new SPInterface(spiClass),
new PropertiesHolder(properties),
(DefaultClassHolder) null);
}
/**
* Create new instance of class implementing SPI.
*
* @param The SPI type
* @param spiClass Service Provider Interface Class.
* @param defaultImpl Default implementation.
* @return Instance of a class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded and
* instantiated, or if the resulting class does not implement
* (or extend) the SPI.
* @throws InstantiationException see {@link Class#newInstance()}
* @throws IllegalAccessException see {@link Class#newInstance()}
* @throws NoSuchMethodException see {@link Class#newInstance()}
* @throws InvocationTargetException see {@link Class#newInstance()}
*/
public T newInstance(Class spiClass, String defaultImpl) throws DiscoveryException,
InstantiationException,
IllegalAccessException,
NoSuchMethodException,
InvocationTargetException {
return newInstance(getClassLoaders(spiClass),
new SPInterface(spiClass),
nullProperties,
new DefaultClassHolder(defaultImpl));
}
/**
* Create new instance of class implementing SPI.
*
* @param The SPI type
* @param spiClass Service Provider Interface Class.
* @param properties Used to determine name of SPI implementation,
* and passed to implementation.init() method if
* implementation implements Service interface.
* @param defaultImpl Default implementation.
* @return Instance of a class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded and
* instantiated, or if the resulting class does not implement
* (or extend) the SPI.
* @throws InstantiationException see {@link Class#newInstance()}
* @throws IllegalAccessException see {@link Class#newInstance()}
* @throws NoSuchMethodException see {@link Class#newInstance()}
* @throws InvocationTargetException see {@link Class#newInstance()}
*/
public T newInstance(Class spiClass, Properties properties, String defaultImpl) throws DiscoveryException,
InstantiationException,
IllegalAccessException,
NoSuchMethodException,
InvocationTargetException {
return newInstance(getClassLoaders(spiClass),
new SPInterface(spiClass),
new PropertiesHolder(properties),
new DefaultClassHolder(defaultImpl));
}
/**
* Create new instance of class implementing SPI.
*
* @param The SPI type
* @param spiClass Service Provider Interface Class.
* @param propertiesFileName Used to determine name of SPI implementation,
* and passed to implementation.init() method if
* implementation implements Service interface.
* @param defaultImpl Default implementation.
* @return Instance of a class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded and
* instantiated, or if the resulting class does not implement
* (or extend) the SPI.
* @throws InstantiationException see {@link Class#newInstance()}
* @throws IllegalAccessException see {@link Class#newInstance()}
* @throws NoSuchMethodException see {@link Class#newInstance()}
* @throws InvocationTargetException see {@link Class#newInstance()}
*/
public T newInstance(Class spiClass, String propertiesFileName, String defaultImpl)
throws DiscoveryException,
InstantiationException,
IllegalAccessException,
NoSuchMethodException,
InvocationTargetException {
return newInstance(getClassLoaders(spiClass),
new SPInterface(spiClass),
new PropertiesHolder(propertiesFileName),
new DefaultClassHolder(defaultImpl));
}
/**
* Create new instance of class implementing SPI.
*
* @param The SPI type
* @param loaders The class loaders holder
* @param spi Service Provider Interface Class.
* @param properties Used to determine name of SPI implementation,
* and passed to implementation.init() method if
* implementation implements Service interface.
* @param defaultImpl Default implementation.
* @return Instance of a class implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found, if the class cannot be loaded and
* instantiated, or if the resulting class does not implement
* (or extend) the SPI.
* @throws InstantiationException see {@link Class#newInstance()}
* @throws IllegalAccessException see {@link Class#newInstance()}
* @throws NoSuchMethodException see {@link Class#newInstance()}
* @throws InvocationTargetException see {@link Class#newInstance()}
*/
public static T newInstance(ClassLoaders loaders,
SPInterface spi,
PropertiesHolder properties,
DefaultClassHolder defaultImpl) throws DiscoveryException,
InstantiationException,
IllegalAccessException,
NoSuchMethodException,
InvocationTargetException {
return spi.newInstance(find(loaders, spi, properties, defaultImpl));
}
/**
* Discover names of SPI implementation Classes from properties.
* The names are the non-null values, in order, obtained from the following
* resources:
*
* - ManagedProperty.getProperty(SPI.class.getName());
* - properties.getProperty(SPI.class.getName());
*
*
* @param The SPI type
* @param spi The SPI representation
* @param properties Properties that may define the implementation
* class name(s).
* @return String[] Name of classes implementing the SPI.
* @exception DiscoveryException Thrown if the name of a class implementing
* the SPI cannot be found.
*/
public static String[] discoverClassNames(SPInterface spi,
Properties properties) {
List names = new LinkedList();
String spiName = spi.getSPName();
String propertyName = spi.getPropertyName();
boolean includeAltProperty = !spiName.equals(propertyName);
// Try the (managed) system property spiName
String className = getManagedProperty(spiName);
if (className != null) {
names.add(className);
}
if (includeAltProperty) {
// Try the (managed) system property propertyName
className = getManagedProperty(propertyName);
if (className != null) {
names.add(className);
}
}
if (properties != null) {
// Try the properties parameter spiName
className = properties.getProperty(spiName);
if (className != null) {
names.add(className);
}
if (includeAltProperty) {
// Try the properties parameter propertyName
className = properties.getProperty(propertyName);
if (className != null) {
names.add(className);
}
}
}
String[] results = new String[names.size()];
names.toArray(results);
return results;
}
/**
* Load the class whose name is given by the value of a (Managed)
* System Property.
*
* @param propertyName the name of the system property whose value is
* the name of the class to load.
* @return The managed property value
* @see ManagedProperties
*/
public static String getManagedProperty(String propertyName) {
String value;
try {
value = ManagedProperties.getProperty(propertyName);
} catch (SecurityException e) {
value = null;
}
return value;
}
}