All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.commons.discovery.tools.DiscoverClass Maven / Gradle / Ivy

Go to download

Its an port to fix bugs in OSGi support. ... The Apache Commons Discovery 0.5 component is about discovering, or finding, implementations for pluggable interfaces.

There is a newer version: 7.4.0
Show newest version
/*
 * 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; } }