[jboss-svn-commits] JBL Code SVN: r25238 - labs/jbossrules/trunk/drools-api/src/main/java/org/drools/util.

jboss-svn-commits at lists.jboss.org jboss-svn-commits at lists.jboss.org
Thu Feb 12 02:14:26 EST 2009


Author: mark.proctor at jboss.com
Date: 2009-02-12 02:14:26 -0500 (Thu, 12 Feb 2009)
New Revision: 25238

Added:
   labs/jbossrules/trunk/drools-api/src/main/java/org/drools/util/OSGiLocator.java
   labs/jbossrules/trunk/drools-api/src/main/java/org/drools/util/ProviderLocator.java
Log:
-start of some OSGi support

Added: labs/jbossrules/trunk/drools-api/src/main/java/org/drools/util/OSGiLocator.java
===================================================================
--- labs/jbossrules/trunk/drools-api/src/main/java/org/drools/util/OSGiLocator.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-api/src/main/java/org/drools/util/OSGiLocator.java	2009-02-12 07:14:26 UTC (rev 25238)
@@ -0,0 +1,169 @@
+/*
+ * Licensed 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.drools.util;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.Callable;
+
+/**
+ * Maintains a mapping of service names to an ordered set of service
+ * providers when running in an OSGi container.
+ * <p/>
+ * It is expected that a bundle using Drools will populate this map
+ * with properties from its own ClassLoader.
+ * <p/>
+ * This is an adaptation of the technique described by Guillaume Nodet
+ * in his article<i>
+ * <a href='http://gnodet.blogspot.com/2008/05/jee-specs-in-osgi.html'>
+ * Java EE specs in OSGi</a></i>. The main changes were to add comments.
+ *
+ * @author Guillaume Nodet
+ * @author Faron Dutton
+ * @see {@linkplain http://gnodet.blogspot.com/2008/05/jee-specs-in-osgi.html}
+ */
+public final class OSGiLocator {
+
+    /**
+     * Maps a service name (the fully qualified name of the interface)
+     * to an ordered set of factories. Each factory instantiating
+     * a specific service provider (implementation).
+     */
+    private static Map<String, List<Callable<Class< ? >>>> factories;
+
+    /**
+     * Private constructor used to prevent instantiation of this
+     * utility class (i.e., Utility Pattern).
+     */
+    private OSGiLocator() {
+        super();
+    }
+
+    /**
+     * Removes the given service provider factory from the set of
+     * providers for the service.
+     * 
+     * @param serviceName
+     *          The fully qualified name of the service interface.
+     * @param factory
+     *          A factory for creating a specific type of service
+     *          provider. May be <tt>null</tt> in which case this
+     *          method does nothing.
+     * @throws IllegalArgumentException if serviceName is <tt>null</tt>
+     */
+    public static synchronized void unregister(final String serviceName,
+                                               final Callable<Class< ? >> factory) {
+        if ( serviceName == null ) {
+            throw new IllegalArgumentException( "serviceName cannot be null" );
+        }
+        if ( factories != null ) {
+            List<Callable<Class< ? >>> l = factories.get( serviceName );
+            if ( l != null ) {
+                l.remove( factory );
+            }
+        }
+    }
+
+    /**
+     * Adds the given service provider factory to the set of
+     * providers for the service.
+     * 
+     * @param serviceName
+     *          The fully qualified name of the service interface.
+     * @param factory
+     *          A factory for creating a specific type of service
+     *          provider. May be <tt>null</tt> in which case this
+     *          method does nothing.
+     * @throws IllegalArgumentException if serviceName is <tt>null</tt>
+     */
+    public static synchronized void register(final String serviceName,
+                                             final Callable<Class< ? >> factory) {
+        if ( serviceName == null ) {
+            throw new IllegalArgumentException( "serviceName cannot be null" );
+        }
+        if ( factory != null ) {
+            if ( factories == null ) {
+                factories = new HashMap<String, List<Callable<Class< ? >>>>();
+            }
+            List<Callable<Class< ? >>> l = factories.get( serviceName );
+            if ( l == null ) {
+                l = new ArrayList<Callable<Class< ? >>>();
+                factories.put( serviceName,
+                               l );
+            }
+            l.add( factory );
+        }
+    }
+
+    /**
+     * Finds the preferred provider for the given service. The preferred
+     * provider is the last one added to the set of providers.
+     * 
+     * @param serviceName
+     *          The fully qualified name of the service interface.
+     * @return
+     *          The last provider added for the service if any exists.
+     *          Otherwise, it returns <tt>null</tt>.
+     * @throws IllegalArgumentException if serviceName is <tt>null</tt>
+     */
+    public static synchronized Class< ? > locate(final String serviceName) {
+        if ( serviceName == null ) {
+            throw new IllegalArgumentException( "serviceName cannot be null" );
+        }
+        if ( factories != null ) {
+            List<Callable<Class< ? >>> l = factories.get( serviceName );
+            if ( l != null && !l.isEmpty() ) {
+                Callable<Class< ? >> c = l.get( l.size() - 1 );
+                try {
+                    return c.call();
+                } catch ( Exception e ) {
+                }
+            }
+        }
+        return null;
+    }
+
+    /**
+     * Finds all providers for the given service.
+     * 
+     * @param serviceName
+     *          The fully qualified name of the service interface.
+     * @return
+     *          The ordered set of providers for the service if any exists.
+     *          Otherwise, it returns an empty list.
+     * @throws IllegalArgumentException if serviceName is <tt>null</tt>
+     */
+    public static synchronized List<Class< ? >> locateAll(final String serviceName) {
+        if ( serviceName == null ) {
+            throw new IllegalArgumentException( "serviceName cannot be null" );
+        }
+        List<Class< ? >> classes = new ArrayList<Class< ? >>();
+        if ( factories != null ) {
+            List<Callable<Class< ? >>> l = factories.get( serviceName );
+            if ( l != null ) {
+                for ( Callable<Class< ? >> c : l ) {
+                    try {
+                        classes.add( c.call() );
+                    } catch ( Exception e ) {
+                    }
+                }
+            }
+        }
+        return classes;
+    }
+
+}
\ No newline at end of file

Added: labs/jbossrules/trunk/drools-api/src/main/java/org/drools/util/ProviderLocator.java
===================================================================
--- labs/jbossrules/trunk/drools-api/src/main/java/org/drools/util/ProviderLocator.java	                        (rev 0)
+++ labs/jbossrules/trunk/drools-api/src/main/java/org/drools/util/ProviderLocator.java	2009-02-12 07:14:26 UTC (rev 25238)
@@ -0,0 +1,306 @@
+/*
+ * Licensed 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.drools.util;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+import org.drools.ProviderInitializationException;
+
+/**
+ * Provides mechanisms for loading service providers dynamically.
+ * <p/>
+ * Provider factories may extend this class to provide a consistent
+ * lookup mechanism. For example, KnowledgeBaseFactory creates
+ * a KonwledgeBase using a service provider that implements the
+ * KnowlwdgeProvider interface.
+ * <pre>
+ * public class KnowledgeBaseFactory extends ProviderLocator {
+ *     private static KnowledgeBaseProvider provider;
+ * 
+ *     public static KnowledgeBase newKnowledgeBase() {
+ *         return getKnowledgeBaseProvider().newKnowledgeBase();
+ *     }
+ * 
+ *     private static synchronized KnowledgeBaseProvider getKnowledgeBaseProvider() {
+ *         if ( provider == null ) {
+ *             provider = newProviderFor( KnowledgeBaseProvider.class );
+ *         }
+ *         return provider;
+ *     }
+ * }
+ * </pre>
+ * <tt>getKnowledgeBaseProvider()</tt> simply calls a method on this class
+ * to instantiate a provider for <tt>KnowledgeBaseProvider</tt>.
+ * <p/>
+ * This is an adaptation of the technique described by Guillaume Nodet
+ * in his article<i>
+ * <a href='http://gnodet.blogspot.com/2008/05/jee-specs-in-osgi.html'>
+ * Java EE specs in OSGi</a></i>, which is an adaptation of the
+ * FactoryLocator class in <a href='http://stax.codehaus.org/'>StAX</a>.
+ * The main changes were to use the existing Drools's lookup strategy
+ * for properties, add comments and error handling, and properly
+ * parse a Service Configuration File.
+ *
+ * @author Guillaume Nodet
+ * @author Faron Dutton
+ * @see {@linkplain http://gnodet.blogspot.com/2008/05/jee-specs-in-osgi.html}
+ * @see {@linkplain http://svn.apache.org/repos/asf/servicemix/smx4/specs/trunk/stax-api-1.0/src/main/java/javax/xml/stream/FactoryLocator.java}
+ */
+public abstract class ProviderLocator {
+
+    // TODO: May need to change the name of the properties file.
+    private static final String CONFIG_FILE = "services.conf";
+
+    /**
+     * Locates and instantiates a new provider for the given service.
+     * <p/>
+     * This is a convenience method equivalent to calling
+     * {@link #newProviderFor(Class, ClassLoader) newProviderFor(serviceClass, null)}.
+     * 
+     * @param <T>
+     *          Either an interface or a (usually abstract) class
+     *          defining a service as described in the JAR File
+     *          Specification.
+     * @param serviceClass
+     *          The Class for <tt>T</tt>. Used to cast the new object
+     *          to <tt>T</tt>. It is important that this Class be
+     *          visible from the given ClassLoader (if provided).
+     * @return A new instance of the service provider.
+     * @throws ProviderInitializationException
+     *          If a provider cannot be found, cannot be instantiated,
+     *          is not accessible, or does not implement (extend) the
+     *          interface (class) <tt>T</tt>.
+     * @see {@linkplain http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Service%20Provider}
+     */
+    protected static final <T> T newProviderFor(final Class<T> serviceClass) throws ProviderInitializationException {
+        return newProviderFor( serviceClass,
+                               (ClassLoader) null );
+    }
+
+    /**
+     * Locates and instantiates a new provider for the given service.
+     * <p/>
+     * This method uses the following strategy to locate a provider:
+     * <ol>
+     * <li>If running in an OSGi container then use the provider from the client bundle</li>
+     * <li>Use the strategy from {@link org.drools.util.ChainedProperties ChainedProperties}</li>
+     * <li>Use the Service API from the JAR File Specification</li>
+     * </ol>
+     * 
+     * @param <T>
+     *          Either an interface or a (usually abstract) class
+     *          defining a service as described in the JAR File
+     *          Specification.
+     * @param serviceClass
+     *          The Class for <tt>T</tt>. Used to cast the new object
+     *          to <tt>T</tt>. It is important that this Class be
+     *          visible from the given ClassLoader (if provided).
+     * @param classLoader
+     *          An optional ClassLoader used to load and instantiate
+     *          the service provider. May be <tt>null</tt>.
+     * @return A new instance of the service provider.
+     * @throws ProviderInitializationException
+     *          If a provider cannot be found, cannot be instantiated,
+     *          is not accessible, or does not implement (extend) the
+     *          interface (class) <tt>T</tt>.
+     * @see {@linkplain http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Service%20Provider}
+     */
+    protected static final <T> T newProviderFor(final Class<T> serviceClass,
+                                                final ClassLoader classLoader) throws ProviderInitializationException {
+        // If we are deployed into an OSGi environment, leverage it
+        Class< ? > providerClass = OSGiLocator.locate( serviceClass.getName() );
+        if ( providerClass != null ) {
+            return newInstance( serviceClass,
+                                providerClass );
+        }
+
+        // Look for a definition in the Drools config files.
+        ChainedProperties props = new ChainedProperties( classLoader,
+                                                         CONFIG_FILE );
+        String providerName = props.getProperty( serviceClass.getName(),
+                                                 null );
+        if ( providerName != null ) {
+            return newInstance( serviceClass,
+                                providerName,
+                                classLoader );
+        }
+
+        // Look for a service provider specified using the mechanism
+        // described in the JAR File Specification.
+        String serviceId = "META-INF/services/".concat( serviceClass.getName() );
+        InputStream providerConfigFile = (classLoader != null) ? classLoader.getResourceAsStream( serviceId ) : ClassLoader.getSystemResourceAsStream( serviceId );
+        if ( providerConfigFile != null ) {
+            try {
+                List<String> results = readProvidersFrom( providerConfigFile );
+                if ( !results.isEmpty() ) {
+                    return newInstance( serviceClass,
+                                        results.get( 0 ),
+                                        classLoader );
+                }
+            } catch ( IOException e ) {
+                final String msg = MessageFormat.format( ERR_FILE_READ,
+                                                         serviceClass.getName() );
+                throw new ProviderInitializationException( msg,
+                                                           e );
+            } finally {
+                try {
+                    providerConfigFile.close();
+                } catch ( IOException e ) {
+                    // FIXME: This should use the Drools logging mechanism.
+                    e.printStackTrace();
+                }
+            }
+        }
+
+        // did not find an implementation anywhere
+        final String msg = MessageFormat.format( ERR_NOT_FOUND,
+                                                 serviceClass.getName() );
+        throw new ProviderInitializationException( msg );
+    }
+
+    /**
+     * Parses a Provider-Configuration File as described in the JAR
+     * File Specification.
+     * 
+     * @param stream
+     *         An open byte stream encoded using UTF-8.
+     * @return
+     *         A list of all the service providers identified in the
+     *         given stream.
+     * @throws IOException If an I/O error occurs
+     */
+    protected static final List<String> readProvidersFrom(final InputStream stream) throws IOException {
+        final Set<String> results = new LinkedHashSet<String>();
+
+        if ( stream != null ) {
+            BufferedReader reader = new BufferedReader( new InputStreamReader( stream,
+                                                                               "UTF-8" ) );
+            String line;
+            while ( (line = reader.readLine()) != null ) {
+                int pos = line.indexOf( '#' );
+                if ( pos >= 0 ) {
+                    line = line.substring( 0,
+                                           pos );
+                }
+                line = line.trim();
+                if ( line.length() != 0 ) {
+                    results.add( line );
+                }
+            }
+        }
+
+        return new ArrayList<String>( results );
+    }
+
+    /**
+     * Instantiates a new object implementing the interface <tt>T</tt>.
+     * 
+     * @param <T>
+     *          Either an interface or a (usually abstract) class
+     *          defining a service as described in the JAR File
+     *          Specification.
+     * @param serviceClass
+     *          The Class for <tt>T</tt>. Used to cast the new object
+     *          to <tt>T</tt>. It is important that this Class be
+     *          visible from the given ClassLoader (if provided).
+     * @param providerName
+     *          A fully qualified name of a class implementing the
+     *          service.
+     * @param classLoader
+     *          An optional ClassLoader used to load and instantiate
+     *          the service provider. May be <tt>null</tt>.
+     * @return A new instance of the service provider.
+     * @throws ProviderInitializationException
+     *          If the class specified by providerName cannot be found,
+     *          cannot be instantiated, is not accessible, or does not
+     *          implement (extend) the interface (class) <tt>T</tt>.
+     * @see {@linkplain http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Service%20Provider}
+     */
+    private static <T> T newInstance(final Class<T> serviceClass,
+                                     final String providerName,
+                                     final ClassLoader classLoader) throws ProviderInitializationException {
+        try {
+            Class< ? > providerClass = classLoader == null ? Class.forName( providerName ) : classLoader.loadClass( providerName );
+            return newInstance( serviceClass,
+                                providerClass );
+        } catch ( ClassNotFoundException e ) {
+            final String msg = MessageFormat.format( ERR_NOT_FOUND,
+                                                     serviceClass.getName() );
+            throw new ProviderInitializationException( msg,
+                                                       e );
+        }
+    }
+
+    /**
+     * Instantiates a new object implementing the interface <tt>T</tt>.
+     * 
+     * @param <T>
+     *          Either an interface or a (usually abstract) class
+     *          defining a service as described in the JAR File
+     *          Specification.
+     * @param serviceClass
+     *          The Class for <tt>T</tt>. Used to cast the new object
+     *          to <tt>T</tt>. It is important that this Class be
+     *          visible from the given ClassLoader (if provided).
+     * @param providerClass
+     *          A class implementing the interface <tt>T</tt>.
+     * @return A new instance of the service provider.
+     * @throws ProviderInitializationException
+     *          If the class cannot be instantiated, is not accessible,
+     *          or does not implement (extend) the interface (class)
+     *          <tt>T</tt>.
+     * @see {@linkplain http://java.sun.com/j2se/1.4.2/docs/guide/jar/jar.html#Service%20Provider}
+     */
+    private static <T> T newInstance(final Class<T> serviceClass,
+                                     final Class< ? > providerClass) throws ProviderInitializationException {
+        try {
+            Object provider = providerClass.newInstance();
+            return serviceClass.cast( provider );
+        } catch ( InstantiationException e ) {
+            final String msg = MessageFormat.format( ERR_NOT_CONCRETE,
+                                                     providerClass.getName() );
+            throw new ProviderInitializationException( msg,
+                                                       e );
+        } catch ( IllegalAccessException e ) {
+            final String msg = MessageFormat.format( ERR_NOT_ACCESSIBLE,
+                                                     providerClass.getName() );
+            throw new ProviderInitializationException( msg,
+                                                       e );
+        } catch ( ClassCastException e ) {
+            final String pattern = serviceClass.isInterface() ? ERR_IMPLEMENTS : ERR_EXTENDS;
+            final String msg = MessageFormat.format( pattern,
+                                                     providerClass.getName(),
+                                                     serviceClass.getName() );
+            throw new ProviderInitializationException( msg,
+                                                       e );
+        }
+    }
+
+    // Localized messages -- move to resource bundle.
+    private static final String ERR_FILE_READ      = "Unable to read the Provider-Configuration File for {0}.";
+    private static final String ERR_EXTENDS        = "Provider {0} does not extend {2}.";
+    private static final String ERR_IMPLEMENTS     = "Provider {0} does not implement {2}.";
+    private static final String ERR_NOT_ACCESSIBLE = "Provider {0} is not accessible.";
+    private static final String ERR_NOT_CONCRETE   = "Provider {0} cannot be instantiated.";
+    private static final String ERR_NOT_FOUND      = "Provider for {0} could not be found.";
+}




More information about the jboss-svn-commits mailing list