<!--
 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.
-->
<document>
<properties>
<title>Best Practices for using Apache Commons &quot;Discovery&quot; Component</title>
</properties>
<body>



<section name="Best Practices for using Apache Commons &quot;Discovery&quot; Component">
$Id: best-practices.xml 1090352 2011-04-08 17:06:17Z simonetripodi $<br />
<a href="#Introduction">[Introduction]</a>
<a href="#Discovery Services">[Discovery Services]</a>
<a href="#Additional Tools">[Additional Tools]</a>
  <a href="#Calling Directly">[Calling Directly]</a>
  <a href="#Integrating into Factories : Wrapping">[Integrating into Factories : Wrapping]</a>
  <a href="#Integrating into DI framework: Google Guice">[Integrating into DI framework: Google Guice]</a>
<br /><br />



<a name="Introduction"></a>

<subsection name="1.  INTRODUCTION">

<p>
Best-practices are discussed.
See the javadoc, starting with <code>DiscoverySingleton</code> and <code>DiscoverClass</code>,
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.
</p>


<a name="Discovery Services"></a>
</subsection>
<subsection name="2.  DISCOVERY SERVICES">
<a name="Additional Tools"></a>
</subsection>
<subsection name="3.  ADDITIONAL TOOLS">

<a name="Calling Directly"></a>
<h4>3.1.  CALLING DIRECTLY</h4>

<a name="Finding Singleton Instances (Factories)"></a>
<h5>3.1.1.  Finding Singleton Instances (Factories)</h5>

<p>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 <a href="#Service Life Cycle Management">[Service Life Cycle Management]</a>).
Use of the term singleton should be applied loosely:
DiscoverSingleton will instantiate separate instances of a class if called with
different:
<ul>
  <li>thread context class loaders
      (for example, within different web applications in a J2EE managed environment)</li>
  <li>group contexts
      (maintain separation between different subsystems, if desired)</li>
</ul>
</p>

<p>To call discovery directly from user-code:
<ul>
<source>
    import org.apache.commons.discovery.DiscoverSingleton;
    import org.apache.commons.logging.LogFactory;
    ...
    LogFactory logFactory = DiscoverSingleton.find(LogFactory.class);
</source>
</ul>
DiscoverSingleton looks for the value of the system property
<code>org.apache.commons.logging.LogFactory</code>
for the name of a class that implements the <code>LogFactory</code> (abstract) class.
Failing that, it uses JDK1.3-style service discovery.
</p>
<p>
DiscoverSingleton also allows a <code>java.util.Properties</code>
parameter to be used for query for service implementation class name,
as well as a default implementation class name:
<ul>
<source>
    LogFactory factory =
        DiscoverSingleton.find(LogFactory.class,
                                           properties,
                                           LogFactory.FACTORY_DEFAULT);
</source>
</ul>
</p>
<p>
The properties can also be specified as a resource name:
<ul>
<source>
    LogFactory factory =
        DiscoverSingleton.find(LogFactory.class,
                                           LogFactory.FACTORY_PROPERTIES,
                                           LogFactory.FACTORY_DEFAULT);
</source>
</ul>
This last form is equivalent in function
to the original <code>LogFactory.getFactory()</code>
method.
</p>
<p>
There are a variety of <code>find</code> methods
provided by <code>DiscoverSingleton</code>, review the javadoc for
other forms and options available.
</p>

<a name="Finding Classes"></a>
<h5>3.1.2.  Finding Classes</h5>

<p>DiscoverClass finds and loads a class implementing a given interface.
DiscoverClass can pass a set of properties to the class if it implements
the <code>Service</code> interface (which doesn't support full-lifecycle
management as does the <code>SingletonService</code> interface).
</p>

<p>
DiscoverClass provides API's that instantiate a class, though it currently
supports only classes with default (zero-argument) constructors.
Unlike <code>DiscoverySingleton</code>, class instances are not cached,
so each call will result in a new object instance.
</p>

<p>
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.
</p>

<p>To find a class directly from user-code:
[NEED BETTER EXAMPLE]
<ul>
<source>
    import org.apache.commons.discovery.DiscoverClasses;
    import org.apache.commons.logging.LogFactory;
    ...
    DiscoverClass discoverClass = new DiscoverClass();
    Class&lt;? extends LogFactory&gt; logFactoryClass = discoverClass.find(LogFactory.class);
</source>
</ul>
In this case, DiscoverClass looks for the value of the system property
<code>org.apache.commons.logging.LogFactory</code>
for the name of a class that implements the <code>LogFactory</code> (abstract) class.
Failing that, it uses JDK1.3-style service discovery.
</p>

<p>To find <b>all</b> the SPI implementation classes from user-code, use <code>DiscoverClasses</code> instead:</p>
<source>
    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&lt;LogFactory&gt; discovery = new DiscoverClasses&lt;TestInterface2&gt;(loaders);
    ResourceClassIterator&lt;LogFactory&gt; iter = discovery.findResourceClasses(name);
    while (iter.hasNext()) {
        ResourceClass&lt;LogFactory&gt; resource = iter.nextResourceClass();
        try {
            Class&lt;? extends LogFactory&gt; implClass = resource.loadClass();
                if (implClass != null) {
                    // TODO do something
                }
            } catch (Exception e) {
                // TODO handle exception
            }
        }
    }
</source>

<p>To instantiate a class directly from user-code:
[NEED BETTER EXAMPLE]
<ul>
<source>
    import org.apache.commons.discovery.DiscoverClass;
    import org.apache.commons.logging.LogFactory;
    ...
    DiscoverClass discoverClass = new DiscoverClass();
    LogFactory logFactoryClass = discoverClass.newInstance(LogFactory.class);
</source>
</ul>
</p>
<p>
As with DiscoverSingleton, DiscoverClass provides methods that
use <code>java.util.Properties</code> and a default implementation class name
to help determine the name of the class.
</p>

<a name="Integrating into Factories : Wrapping"></a>
<h4>3.2  INTEGRATING INTO FACTORIES : WRAPPING</h4>


<p>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.

<ul>
<source>
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);
    }
    ...
}
</source>
</ul>
Note the addition of one extra parameter to the <code>find</code>
method call.
The first parameter is a <i>root wrapper class</i>,
which delegates to the discovery mechanism.
This is necessary to all <code>Discovery</code>
to determine the correct class loaders to be used
in loading an implementation class.
The second parameter is the service interface/class
for which <code>Discovery</code> will be looking for an implementation.
In this example, they are the same class, as the LogFactory
is providing helper methods that 'wrap' <code>Discovery</code>.
</p>

<a name="Integrating into DI framework: Google Guice"></a>
<h4>3.3  INTEGRATING INTO DI FRAMEWORKS : GOOGLE GUICE</h4>

<p>Sometimes, a <code>DiscoverSingleton</code>/<code>Factory</code> approach like the one exposed above,
is not enough, because you may require injecting dependencies in your discovered SPI.</p>

<p>In this example is shown how <code>DiscoverClass</code> can be useful inside DI framework such
<i>Google Guice</i>:</p>

<source>
Injector injector = Guice.createInjector(new AbstractModule() {

    @Override
    protected void configure() {
        ...
        DiscoverClass discoverClass = new DiscoverClass();
        bind(ServiceInterface.class).to(discoverClass.find(ServiceInterface.class));
        ...
    }

});
</source>

<p>In this way, <code>ServiceInterface</code> discovery is delegated to <i>Commons Discovery</i>,
but concrete class implementation creation and dependencies injection, to <i>Google Guice</i>.</p>

</subsection>
</section>
</body>
</document>
