activemq-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From chir...@apache.org
Subject svn commit: r816444 - in /activemq/trunk: ./ activemq-core/ activemq-core/src/main/java/org/apache/activemq/util/ activemq-core/src/main/java/org/apache/activemq/util/osgi/
Date Fri, 18 Sep 2009 02:49:22 GMT
Author: chirino
Date: Fri Sep 18 02:49:21 2009
New Revision: 816444

URL: http://svn.apache.org/viewvc?rev=816444&view=rev
Log:
- refactored FactoryFinder so that it Implementation is pluggable.
- made an OSGi friendly strategy for it using a BundleActivator (needs testing)


Added:
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/osgi/
      - copied from r816369, servicemix/smx4/specs/trunk/locator/src/main/java/org/apache/servicemix/specs/locator/
Removed:
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/osgi/OsgiLocator.java
Modified:
    activemq/trunk/activemq-core/pom.xml
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/FactoryFinder.java
    activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/osgi/Activator.java
    activemq/trunk/pom.xml

Modified: activemq/trunk/activemq-core/pom.xml
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/pom.xml?rev=816444&r1=816443&r2=816444&view=diff
==============================================================================
--- activemq/trunk/activemq-core/pom.xml (original)
+++ activemq/trunk/activemq-core/pom.xml Fri Sep 18 02:49:21 2009
@@ -83,11 +83,18 @@
       <groupId>org.apache.activemq.protobuf</groupId>
       <artifactId>activemq-protobuf</artifactId>
       <optional>false</optional>
-    </dependency>  
-    
+    </dependency>
+
     <!-- =============================== -->
     <!-- Optional Dependencies           -->
     <!-- =============================== -->
+
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.core</artifactId>
+    </dependency>
+    
+
     <dependency>
       <groupId>org.apache.activemq</groupId>
       <artifactId>activemq-jaas</artifactId>

Modified: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/FactoryFinder.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/FactoryFinder.java?rev=816444&r1=816443&r2=816444&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/FactoryFinder.java
(original)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/FactoryFinder.java
Fri Sep 18 02:49:21 2009
@@ -22,89 +22,129 @@
 import java.util.Properties;
 import java.util.concurrent.ConcurrentHashMap;
 
+/**
+ * 
+ */
 public class FactoryFinder {
 
-    private final String path;
-    private final ConcurrentHashMap<String, Class> classMap = new ConcurrentHashMap<String,
Class>();
+    /**
+     * The strategey that the FactoryFinder uses to find load and instanciate Objects
+     * can be chagned out by calling the
+     * {@link org.apache.activemq.util.FactoryFinder#setObjectFactory(org.apache.activemq.util.FactoryFinder.ObjectFactory)}
+     * method with a custom implemenation of ObjectFactory.
+     *
+     * The default ObjectFactory is typically changed out when running in a specialized container
+     * enviorment where service discovery needs to be done via the container system.  For
example,
+     * in an OSGi scenario.
+     */
+    public interface ObjectFactory {
+        /**
+         * @param path the full service path 
+         * @return
+         */
+        public Object create(String path) throws IllegalAccessException, InstantiationException,
IOException, ClassNotFoundException;
 
-    public FactoryFinder(String path) {
-        this.path = path;
     }
 
     /**
-     * Creates a new instance of the given key
-     * 
-     * @param key is the key to add to the path to find a text file containing
-     *                the factory name
-     * @return a newly created instance
+     * The default implementation of Object factory which works well in standalone applications.
      */
-    public Object newInstance(String key) throws IllegalAccessException, InstantiationException,
IOException, ClassNotFoundException {
-        return newInstance(key, null);
-    }
+    protected static class StandaloneObjectFactory implements ObjectFactory {
+        final ConcurrentHashMap<String, Class> classMap = new ConcurrentHashMap<String,
Class>();
 
-    public Object newInstance(String key, String propertyPrefix) throws IllegalAccessException,
InstantiationException, IOException, ClassNotFoundException {
-        if (propertyPrefix == null) {
-            propertyPrefix = "";
+        public Object create(final String path) throws InstantiationException, IllegalAccessException,
ClassNotFoundException, IOException {
+            Class clazz = classMap.get(path);
+            if (clazz == null) {
+                clazz = loadClass(loadProperties(path));
+                classMap.put(path, clazz);
+            }
+            return clazz.newInstance();
         }
 
-        Class clazz = classMap.get(propertyPrefix + key);
-        if (clazz == null) {
-            clazz = newInstance(doFindFactoryProperies(key), propertyPrefix);
-            classMap.put(propertyPrefix + key, clazz);
-        }
-        return clazz.newInstance();
-    }
+        static public Class loadClass(Properties properties) throws ClassNotFoundException,
IOException {
 
-    private Class newInstance(Properties properties, String propertyPrefix) throws ClassNotFoundException,
IOException {
+            String className = properties.getProperty("class");
+            if (className == null) {
+                throw new IOException("Expected property is missing: class");
+            }
+            Class clazz = null;
+            ClassLoader loader = Thread.currentThread().getContextClassLoader();
+            if (loader != null) {
+                try {
+                    clazz = loader.loadClass(className);
+                } catch (ClassNotFoundException e) {
+                    // ignore
+                }
+            }
+            if (clazz == null) {
+                clazz = FactoryFinder.class.getClassLoader().loadClass(className);
+            }
 
-        String className = properties.getProperty(propertyPrefix + "class");
-        if (className == null) {
-            throw new IOException("Expected property is missing: " + propertyPrefix + "class");
+            return clazz;
         }
-        Class clazz = null;
-        ClassLoader loader = Thread.currentThread().getContextClassLoader();
-        if (loader != null) {
+
+        static public Properties loadProperties(String uri) throws IOException {
+            // lets try the thread context class loader first
+            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
+            if (classLoader == null) {
+                classLoader = StandaloneObjectFactory.class.getClassLoader();
+            }
+            InputStream in = classLoader.getResourceAsStream(uri);
+            if (in == null) {
+                in = FactoryFinder.class.getClassLoader().getResourceAsStream(uri);
+                if (in == null) {
+                    throw new IOException("Could not find factory class for resource: " +
uri);
+                }
+            }
+
+            // lets load the file
+            BufferedInputStream reader = null;
             try {
-                clazz = loader.loadClass(className);
-            } catch (ClassNotFoundException e) {
-                // ignore
+                reader = new BufferedInputStream(in);
+                Properties properties = new Properties();
+                properties.load(reader);
+                return properties;
+            } finally {
+                try {
+                    reader.close();
+                } catch (Exception e) {
+                }
             }
         }
-        if (clazz == null) {
-            clazz = FactoryFinder.class.getClassLoader().loadClass(className);
-        }
+    }
+
+    // ================================================================
+    // Class methods and properties
+    // ================================================================
+    private static ObjectFactory objectFactory = new StandaloneObjectFactory();
 
-        return clazz;
+    public static ObjectFactory getObjectFactory() {
+        return objectFactory;
     }
 
-    private Properties doFindFactoryProperies(String key) throws IOException {
-        String uri = path + key;
+    public static void setObjectFactory(ObjectFactory objectFactory) {
+        FactoryFinder.objectFactory = objectFactory;
+    }
 
-        // lets try the thread context class loader first
-        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
-        if (classLoader == null) {
-            classLoader = getClass().getClassLoader();
-        }
-        InputStream in = classLoader.getResourceAsStream(uri);
-        if (in == null) {
-            in = FactoryFinder.class.getClassLoader().getResourceAsStream(uri);
-            if (in == null) {
-                throw new IOException("Could not find factory class for resource: " + uri);
-            }
-        }
+    // ================================================================
+    // Instance methods and properties
+    // ================================================================
+    private final String path;
 
-        // lets load the file
-        BufferedInputStream reader = null;
-        try {
-            reader = new BufferedInputStream(in);
-            Properties properties = new Properties();
-            properties.load(reader);
-            return properties;
-        } finally {
-            try {
-                reader.close();
-            } catch (Exception e) {
-            }
-        }
+    public FactoryFinder(String path) {
+        this.path = path;
     }
+
+    /**
+     * Creates a new instance of the given key
+     *
+     * @param key is the key to add to the path to find a text file containing
+     *                the factory name
+     * @return a newly created instance
+     */
+    public Object newInstance(String key) throws IllegalAccessException, InstantiationException,
IOException, ClassNotFoundException {
+        return objectFactory.create(path+key);
+    }
+
+    
 }

Modified: activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/osgi/Activator.java
URL: http://svn.apache.org/viewvc/activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/osgi/Activator.java?rev=816444&r1=816369&r2=816444&view=diff
==============================================================================
--- activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/osgi/Activator.java
(original)
+++ activemq/trunk/activemq-core/src/main/java/org/apache/activemq/util/osgi/Activator.java
Fri Sep 18 02:49:21 2009
@@ -14,17 +14,22 @@
  *  See the License for the specific language governing permissions and
  *  limitations under the License.
  */
-package org.apache.servicemix.specs.locator;
+package org.apache.activemq.util.osgi;
 
-import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
 import java.io.InputStreamReader;
-import java.net.URL;
-import java.util.Enumeration;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.Callable;
+import java.io.BufferedReader;
+import java.util.Properties;
+import java.util.ArrayList;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.ConcurrentMap;
+import java.net.URL;
+
+import org.apache.activemq.util.FactoryFinder;
+import org.apache.activemq.util.FactoryFinder.ObjectFactory;
+import org.apache.commons.logging.LogFactory;
+import org.apache.commons.logging.Log;
 
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleActivator;
@@ -32,57 +37,56 @@
 import org.osgi.framework.BundleEvent;
 import org.osgi.framework.SynchronousBundleListener;
 
-public class Activator implements BundleActivator, SynchronousBundleListener {
-
-    private static boolean debug = false;
+/**
+ * An OSGi bundle activator for ActiveMQ which adapts the {@link org.apache.activemq.util.FactoryFinder}
+ * to the OSGi enviorment.
+ *
+ */
+public class Activator implements BundleActivator, SynchronousBundleListener, ObjectFactory
{
 
-    private ConcurrentMap<Long, Map<String, Callable<Class>>> factories
= new ConcurrentHashMap<Long, Map<String, Callable<Class>>>();
+    private static final Log LOG = LogFactory.getLog(Activator.class);
 
+    private final ConcurrentHashMap<String, Class> serviceCache = new ConcurrentHashMap<String,
Class>();
+    private final ConcurrentMap<Long, BundleWrapper> bundleWrappers = new ConcurrentHashMap<Long,
BundleWrapper>();
     private BundleContext bundleContext;
 
-    static {
-        try {
-            String prop = System.getProperty("org.apache.servicemix.specs.debug");
-            debug = prop != null && !"false".equals(prop);
-        } catch (Throwable t) { }
-    }
-
-    /**
-     * <p>Output debugging messages.</p>
-     *
-     * @param msg <code>String</code> to print to <code>stderr</code>.
-     */
-    protected void debugPrintln(String msg) {
-        if (debug) {
-            System.err.println("Spec(" + bundleContext.getBundle().getBundleId() + "): "
+ msg);
-        }
-    }
+    // ================================================================
+    // BundleActivator interface impl
+    // ================================================================
 
     public synchronized void start(BundleContext bundleContext) throws Exception {
+
+        // This is how we replace the default FactoryFinder strategy
+        // with one that is more compatible in an OSGi env.
+        FactoryFinder.setObjectFactory(this);
+
+        debug("activating");
         this.bundleContext = bundleContext;
-        debugPrintln("activating");
-        debugPrintln("adding bundle listener");
-        bundleContext.addBundleListener(this);
-        debugPrintln("checking existing bundles");
+        debug("checking existing bundles");
         for (Bundle bundle : bundleContext.getBundles()) {
             if (bundle.getState() == Bundle.RESOLVED || bundle.getState() == Bundle.STARTING
||
-                    bundle.getState() == Bundle.ACTIVE || bundle.getState() == Bundle.STOPPING)
{
+                bundle.getState() == Bundle.ACTIVE || bundle.getState() == Bundle.STOPPING)
{
                 register(bundle);
             }
         }
-        debugPrintln("activated");
+        debug("activated");
     }
 
+
     public synchronized void stop(BundleContext bundleContext) throws Exception {
-        debugPrintln("deactivating");
+        debug("deactivating");
         bundleContext.removeBundleListener(this);
-        while (!factories.isEmpty()) {
-            unregister(factories.keySet().iterator().next());
+        while (!bundleWrappers.isEmpty()) {
+            unregister(bundleWrappers.keySet().iterator().next());
         }
-        debugPrintln("deactivated");
+        debug("deactivated");
         this.bundleContext = null;
     }
 
+    // ================================================================
+    // SynchronousBundleListener interface impl
+    // ================================================================
+
     public void bundleChanged(BundleEvent event) {
         if (event.getType() == BundleEvent.RESOLVED) {
             register(event.getBundle());
@@ -92,87 +96,121 @@
     }
 
     protected void register(final Bundle bundle) {
-        debugPrintln("checking bundle " + bundle.getBundleId());
-        Map<String, Callable<Class>> map = factories.get(bundle.getBundleId());
-        Enumeration e = bundle.findEntries("META-INF/services/", "*", false);
-        if (e != null) {
-            while (e.hasMoreElements()) {
-                final URL u = (URL) e.nextElement();
-                final String url = u.toString();
-                if (url.endsWith("/")) {
-                    continue;
-                }
-                final String factoryId = url.substring(url.lastIndexOf("/") + 1);
-                if (map == null) {
-                    map = new HashMap<String, Callable<Class>>();
-                    factories.put(bundle.getBundleId(), map);
-                }
-                map.put(factoryId, new BundleFactoryLoader(factoryId, u, bundle));
-            }
-        }
-        if (map != null) {
-            for (Map.Entry<String, Callable<Class>> entry : map.entrySet()) {
-                debugPrintln("registering service for key " + entry.getKey() + "with value
" + entry.getValue());
-                OsgiLocator.register(entry.getKey(), entry.getValue());
-            }
+        debug("checking bundle " + bundle.getBundleId());
+        if( !isImportingUs(bundle) ) {
+            debug("The bundle does not import us: "+ bundle.getBundleId());
+            return;
         }
+        bundleWrappers.put(bundle.getBundleId(), new BundleWrapper(bundle));
     }
 
+    /**
+     * When bundles unload.. we remove them thier cached Class entries from the
+     * serviceCache.  Future service lookups for the service will fail.
+     *
+     * TODO: consider a way to get the Broker release any references to
+     * instances of the service.
+     *
+     * @param bundleId
+     */
     protected void unregister(long bundleId) {
-        Map<String, Callable<Class>> map = factories.remove(bundleId);
-        if (map != null) {
-            for (Map.Entry<String, Callable<Class>> entry : map.entrySet()) {
-                debugPrintln("unregistering service for key " + entry.getKey() + "with value
" + entry.getValue());
-                OsgiLocator.unregister(entry.getKey(), entry.getValue());
+        BundleWrapper bundle = bundleWrappers.remove(bundleId);
+        if (bundle != null) {
+            for (String path : bundle.cachedServices) {
+                debug("unregistering service for key: " +path );
+                serviceCache.remove(path);
             }
         }
     }
 
-    private class BundleFactoryLoader implements Callable<Class> {
-        private final String factoryId;
-        private final URL u;
-        private final Bundle bundle;
+    // ================================================================
+    // ObjectFactory interface impl
+    // ================================================================
+
+    public Object create(String path) throws IllegalAccessException, InstantiationException,
IOException, ClassNotFoundException {
+        Class clazz = serviceCache.get(path);
+        if (clazz == null) {
+            StringBuffer warnings = new StringBuffer();
+            // We need to look for a bundle that has that class.
+            int wrrningCounter=1;
+            for (BundleWrapper wrapper : bundleWrappers.values()) {
+                URL resource = wrapper.bundle.getResource(path);
+                if( resource == null ) {
+                    continue;
+                }
 
-        public BundleFactoryLoader(String factoryId, URL u, Bundle bundle) {
-            this.factoryId = factoryId;
-            this.u = u;
-            this.bundle = bundle;
+                Properties properties = loadProperties(resource);
+
+                String className = properties.getProperty("class");
+                if (className == null) {
+                    warnings.append("("+(wrrningCounter++)+") Invalid sevice file in bundle
"+wrapper+": 'class' property not defined.");
+                    continue;
+                }
+
+                try {
+                    clazz = wrapper.bundle.loadClass(className);
+                } catch (ClassNotFoundException e) {
+                    warnings.append("("+(wrrningCounter++)+") Bundle "+wrapper+" could not
load "+className+": "+e);
+                    continue;
+                }
+
+                // Yay.. the class was found.  Now cache it.
+                serviceCache.put(path, clazz);
+                wrapper.cachedServices.add(path);
+                break;
+            }
+
+            if( clazz == null ) {
+                // Since OSGi is such a tricky enviorment to work in.. lets give folks the
+                // most information we can in the error message.
+                String msg = "Service not found: '" + path + "'";
+                if (warnings.length()!= 0) {
+                    msg += ", "+warnings;
+                }
+                throw new IOException(msg);
+            }
         }
+        return clazz.newInstance();
+    }
+
+    // ================================================================
+    // Internal Helper Methods
+    // ================================================================
+
+    private void debug(Object msg) {
+        LOG.debug(msg);
+    }
 
-        public Class call() throws Exception {
+    private Properties loadProperties(URL resource) throws IOException {
+        InputStream in = resource.openStream();
+        try {
+            BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
+            Properties properties = new Properties();
+            properties.load(in);
+            return properties;
+        } finally {
             try {
-                debugPrintln("creating factory for key: " + factoryId);
-                BufferedReader br = new BufferedReader(new InputStreamReader(u.openStream(),
"UTF-8"));
-                String factoryClassName = br.readLine();
-                br.close();
-                debugPrintln("factory implementation: " + factoryClassName);
-                return bundle.loadClass(factoryClassName);
+                in.close();
             } catch (Exception e) {
-                debugPrintln("exception caught while creating factory: " + e);
-                throw e;
-            } catch (Error e) {
-                debugPrintln("error caught while creating factory: " + e);
-                throw e;
             }
         }
+    }
 
-        @Override
-        public String toString() {
-           return u.toString();
-        }
-
-        @Override
-        public int hashCode() {
-           return u.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object obj) {
-            if (obj instanceof BundleFactoryLoader) {
-                return u.equals(((BundleFactoryLoader) obj).u);
-            } else {
-                return false;
-            }
+    private boolean isImportingUs(Bundle bundle) {
+        try {
+            // If that bundle can load our classes.. then it must be importing us.
+            return bundle.loadClass(Activator.class.getName())==Activator.class;
+        } catch (ClassNotFoundException e) {
+            return false;
+        }
+    }
+
+    private static class BundleWrapper {
+        private final Bundle bundle;
+        private final ArrayList<String> cachedServices = new ArrayList<String>();
+
+        public BundleWrapper(Bundle bundle) {
+            this.bundle = bundle;
         }
     }
 }

Modified: activemq/trunk/pom.xml
URL: http://svn.apache.org/viewvc/activemq/trunk/pom.xml?rev=816444&r1=816443&r2=816444&view=diff
==============================================================================
--- activemq/trunk/pom.xml (original)
+++ activemq/trunk/pom.xml Fri Sep 18 02:49:21 2009
@@ -401,6 +401,13 @@
       </dependency>
 
       <dependency>
+        <groupId>org.osgi</groupId>
+        <artifactId>org.osgi.core</artifactId>
+        <version>4.1.0</version>
+        <scope>provided</scope>
+      </dependency>
+
+      <dependency>
         <groupId>org.apache.hadoop.zookeeper</groupId>
         <artifactId>zookeeper</artifactId>
         <version>3.0.0</version>



Mime
View raw message