mirror of https://github.com/apache/activemq.git
- refactored FactoryFinder so that it Implementation is pluggable.
- made an OSGi friendly strategy for it using a BundleActivator (needs testing) git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@816444 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
583d8490fa
commit
76381791e8
|
@ -88,6 +88,13 @@
|
|||
<!-- =============================== -->
|
||||
<!-- Optional Dependencies -->
|
||||
<!-- =============================== -->
|
||||
|
||||
<dependency>
|
||||
<groupId>org.osgi</groupId>
|
||||
<artifactId>org.osgi.core</artifactId>
|
||||
</dependency>
|
||||
|
||||
|
||||
<dependency>
|
||||
<groupId>org.apache.activemq</groupId>
|
||||
<artifactId>activemq-jaas</artifactId>
|
||||
|
|
|
@ -22,44 +22,50 @@ import java.io.InputStream;
|
|||
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 = "";
|
||||
}
|
||||
|
||||
Class clazz = classMap.get(propertyPrefix + key);
|
||||
public Object create(final String path) throws InstantiationException, IllegalAccessException, ClassNotFoundException, IOException {
|
||||
Class clazz = classMap.get(path);
|
||||
if (clazz == null) {
|
||||
clazz = newInstance(doFindFactoryProperies(key), propertyPrefix);
|
||||
classMap.put(propertyPrefix + key, clazz);
|
||||
clazz = loadClass(loadProperties(path));
|
||||
classMap.put(path, clazz);
|
||||
}
|
||||
return clazz.newInstance();
|
||||
}
|
||||
|
||||
private Class newInstance(Properties properties, String propertyPrefix) throws ClassNotFoundException, IOException {
|
||||
static public Class loadClass(Properties properties) throws ClassNotFoundException, IOException {
|
||||
|
||||
String className = properties.getProperty(propertyPrefix + "class");
|
||||
String className = properties.getProperty("class");
|
||||
if (className == null) {
|
||||
throw new IOException("Expected property is missing: " + propertyPrefix + "class");
|
||||
throw new IOException("Expected property is missing: class");
|
||||
}
|
||||
Class clazz = null;
|
||||
ClassLoader loader = Thread.currentThread().getContextClassLoader();
|
||||
|
@ -77,13 +83,11 @@ public class FactoryFinder {
|
|||
return clazz;
|
||||
}
|
||||
|
||||
private Properties doFindFactoryProperies(String key) throws IOException {
|
||||
String uri = path + key;
|
||||
|
||||
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 = getClass().getClassLoader();
|
||||
classLoader = StandaloneObjectFactory.class.getClassLoader();
|
||||
}
|
||||
InputStream in = classLoader.getResourceAsStream(uri);
|
||||
if (in == null) {
|
||||
|
@ -107,4 +111,40 @@ public class FactoryFinder {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Class methods and properties
|
||||
// ================================================================
|
||||
private static ObjectFactory objectFactory = new StandaloneObjectFactory();
|
||||
|
||||
public static ObjectFactory getObjectFactory() {
|
||||
return objectFactory;
|
||||
}
|
||||
|
||||
public static void setObjectFactory(ObjectFactory objectFactory) {
|
||||
FactoryFinder.objectFactory = objectFactory;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// Instance methods and properties
|
||||
// ================================================================
|
||||
private final String path;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
package org.apache.activemq.util.osgi;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
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;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.BundleEvent;
|
||||
import org.osgi.framework.SynchronousBundleListener;
|
||||
|
||||
/**
|
||||
* 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 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;
|
||||
|
||||
// ================================================================
|
||||
// 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;
|
||||
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) {
|
||||
register(bundle);
|
||||
}
|
||||
}
|
||||
debug("activated");
|
||||
}
|
||||
|
||||
|
||||
public synchronized void stop(BundleContext bundleContext) throws Exception {
|
||||
debug("deactivating");
|
||||
bundleContext.removeBundleListener(this);
|
||||
while (!bundleWrappers.isEmpty()) {
|
||||
unregister(bundleWrappers.keySet().iterator().next());
|
||||
}
|
||||
debug("deactivated");
|
||||
this.bundleContext = null;
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// SynchronousBundleListener interface impl
|
||||
// ================================================================
|
||||
|
||||
public void bundleChanged(BundleEvent event) {
|
||||
if (event.getType() == BundleEvent.RESOLVED) {
|
||||
register(event.getBundle());
|
||||
} else if (event.getType() == BundleEvent.UNRESOLVED || event.getType() == BundleEvent.UNINSTALLED) {
|
||||
unregister(event.getBundle().getBundleId());
|
||||
}
|
||||
}
|
||||
|
||||
protected void register(final Bundle bundle) {
|
||||
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) {
|
||||
BundleWrapper bundle = bundleWrappers.remove(bundleId);
|
||||
if (bundle != null) {
|
||||
for (String path : bundle.cachedServices) {
|
||||
debug("unregistering service for key: " +path );
|
||||
serviceCache.remove(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ================================================================
|
||||
// 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;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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 {
|
||||
in.close();
|
||||
} catch (Exception e) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
7
pom.xml
7
pom.xml
|
@ -400,6 +400,13 @@
|
|||
<type>war</type>
|
||||
</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>
|
||||
|
|
Loading…
Reference in New Issue