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>