Best-practices are discussed. See the javadoc, starting with DiscoverySingleton and DiscoverClass, for detail on the API: where service implementations are looked for, the order in which those places are checked, which classloaders are used, and the order in which they are used.
DiscoverSingleton finds, loads, and manages the lifecycle of a class implementing a given interface. It only supports classes with default (zero-argument) constructors. DiscoverSingleton can pass a set of properties to the class (see [Service Life Cycle Management]). Use of the term singleton should be applied loosely: DiscoverSingleton will instantiate separate instances of a class if called with different:
To call discovery directly from user-code:
import org.apache.commons.discovery.DiscoverSingleton; import org.apache.commons.logging.LogFactory; ... LogFactory logFactory = DiscoverSingleton.find(LogFactory.class);
DiscoverSingleton also allows a java.util.Properties parameter to be used for query for service implementation class name, as well as a default implementation class name:
LogFactory factory = DiscoverSingleton.find(LogFactory.class, properties, LogFactory.FACTORY_DEFAULT);
The properties can also be specified as a resource name:
LogFactory factory = DiscoverSingleton.find(LogFactory.class, LogFactory.FACTORY_PROPERTIES, LogFactory.FACTORY_DEFAULT);
There are a variety of find methods provided by DiscoverSingleton, review the javadoc for other forms and options available.
DiscoverClass finds and loads a class implementing a given interface. DiscoverClass can pass a set of properties to the class if it implements the Service interface (which doesn't support full-lifecycle management as does the SingletonService interface).
DiscoverClass provides API's that instantiate a class, though it currently supports only classes with default (zero-argument) constructors. Unlike DiscoverySingleton, class instances are not cached, so each call will result in a new object instance.
DiscoverClass is more oriented toward calling multiple times within similar contexts, so it's use is slightly different than DiscoverSingleton: where as DiscoverSingleton provides a set of static methods (no state), DiscoverClass must be instantiated before it is used and maintains internal state.
To find a class directly from user-code: [NEED BETTER EXAMPLE]
import org.apache.commons.discovery.DiscoverClasses; import org.apache.commons.logging.LogFactory; ... DiscoverClass discoverClass = new DiscoverClass(); Class<? extends LogFactory> logFactoryClass = discoverClass.find(LogFactory.class);
To find all the SPI implementation classes from user-code, use DiscoverClasses instead:
import org.apache.commons.discovery.ResourceClassIterator; import org.apache.commons.discovery.ResourceClass: import org.apache.commons.discovery.resource.classes.DiscoverClasses; import org.apache.commons.logging.LogFactory; ... DiscoverClasses<LogFactory> discovery = new DiscoverClasses<TestInterface2>(loaders); ResourceClassIterator<LogFactory> iter = discovery.findResourceClasses(name); while (iter.hasNext()) { ResourceClass<LogFactory> resource = iter.nextResourceClass(); try { Class<? extends LogFactory> implClass = resource.loadClass(); if (implClass != null) { // TODO do something } } catch (Exception e) { // TODO handle exception } } }
To instantiate a class directly from user-code: [NEED BETTER EXAMPLE]
import org.apache.commons.discovery.DiscoverClass; import org.apache.commons.logging.LogFactory; ... DiscoverClass discoverClass = new DiscoverClass(); LogFactory logFactoryClass = discoverClass.newInstance(LogFactory.class);
As with DiscoverSingleton, DiscoverClass provides methods that use java.util.Properties and a default implementation class name to help determine the name of the class.
In this example, a factory (such as is used in commons-logging) internalizes the discovery mechanism, passing appropriate defaults for a default properties file and a default implementation. In this case, the factory plays double duty as both the service to be discovered (abstract class), and the discovery mechanism.
import java.util.Properties; import org.apache.commons.discovery.DiscoverSingleton; import org.apache.commons.discovery.DiscoveryException; public abstract class LogFactory { protected static final String FACTORY_DEFAULT = org.apache.commons.logging.impl.DefaultLogFactory.class.getName(); protected static final String FACTORY_PROPERTIES = "commons-logging.properties"; /** * Protected constructor that is not available for public use. */ protected LogFactory() { } public static LogFactory getFactory() throws ServiceException { return DiscoverSingleton.find(LogFactory.class, LogFactory.class, FACTORY_PROPERTIES, FACTORY_DEFAULT); } public static LogFactory getFactory(Properties properties) throws ServiceException { return DiscoverSingleton.find(LogFactory.class, LogFactory.class, properties, FACTORY_DEFAULT); } ... }
Sometimes, a DiscoverSingleton/Factory approach like the one exposed above, is not enough, because you may require injecting dependencies in your discovered SPI.
In this example is shown how DiscoverClass can be useful inside DI framework such Google Guice:
Injector injector = Guice.createInjector(new AbstractModule() { @Override protected void configure() { ... DiscoverClass discoverClass = new DiscoverClass(); bind(ServiceInterface.class).to(discoverClass.find(ServiceInterface.class)); ... } });
In this way, ServiceInterface discovery is delegated to Commons Discovery, but concrete class implementation creation and dependencies injection, to Google Guice.