From 6ffe7085bc414aa0ccd7ac13485d95cf5cf9212b Mon Sep 17 00:00:00 2001 From: Ioannis Canellos Date: Tue, 12 Jun 2012 00:38:01 +0300 Subject: [PATCH] Improved the discovery of providers inside OSGi --- core/pom.xml | 7 + .../main/java/org/jclouds/osgi/Activator.java | 71 ++++++++++ .../jclouds/osgi/ProviderBundleListener.java | 129 ++++++++++++++++++ .../jclouds/providers/ProviderRegistry.java | 46 +++++++ .../java/org/jclouds/providers/Providers.java | 3 +- project/pom.xml | 1 + 6 files changed, 256 insertions(+), 1 deletion(-) create mode 100644 core/src/main/java/org/jclouds/osgi/Activator.java create mode 100644 core/src/main/java/org/jclouds/osgi/ProviderBundleListener.java create mode 100644 core/src/main/java/org/jclouds/providers/ProviderRegistry.java diff --git a/core/pom.xml b/core/pom.xml index 138edaad9c..0bed6e7436 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -41,6 +41,7 @@ * org.jclouds*;version=${project.version};-noimport:=true + org.jclouds.osgi.Activator @@ -106,6 +107,12 @@ guava 12.0 + + org.osgi + org.osgi.core + 4.2.0 + provided + diff --git a/core/src/main/java/org/jclouds/osgi/Activator.java b/core/src/main/java/org/jclouds/osgi/Activator.java new file mode 100644 index 0000000000..64de199e1d --- /dev/null +++ b/core/src/main/java/org/jclouds/osgi/Activator.java @@ -0,0 +1,71 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.osgi; + +import org.jclouds.providers.ProviderRegistry; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +public class Activator implements BundleActivator { + + ProviderBundleListener bundleListener = new ProviderBundleListener(); + + /** + * Called when this bundle is started so the Framework can perform the + * bundle-specific activities necessary to start this bundle. This method + * can be used to register services or to allocate any resources that this + * bundle needs. + *

+ *

+ * This method must complete and return to its caller in a timely manner. + * + * @param context The execution context of the bundle being started. + * @throws Exception If this method throws an exception, this + * bundle is marked as stopped and the Framework will remove this + * bundle's listeners, unregister all services registered by this + * bundle, and release all services used by this bundle. + */ + @Override + public void start(BundleContext context) throws Exception { + context.addBundleListener(bundleListener); + } + + /** + * Called when this bundle is stopped so the Framework can perform the + * bundle-specific activities necessary to stop the bundle. In general, this + * method should undo the work that the BundleActivator.start + * method started. There should be no active threads that were started by + * this bundle when this bundle returns. A stopped bundle must not call any + * Framework objects. + *

+ *

+ * This method must complete and return to its caller in a timely manner. + * + * @param context The execution context of the bundle being stopped. + * @throws Exception If this method throws an exception, the + * bundle is still marked as stopped, and the Framework will remove + * the bundle's listeners, unregister all services registered by the + * bundle, and release all services used by the bundle. + */ + @Override + public void stop(BundleContext context) throws Exception { + context.removeBundleListener(bundleListener); + ProviderRegistry.clear(); + } +} diff --git a/core/src/main/java/org/jclouds/osgi/ProviderBundleListener.java b/core/src/main/java/org/jclouds/osgi/ProviderBundleListener.java new file mode 100644 index 0000000000..01d3a267b6 --- /dev/null +++ b/core/src/main/java/org/jclouds/osgi/ProviderBundleListener.java @@ -0,0 +1,129 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.osgi; + +import org.jclouds.providers.ProviderMetadata; +import org.jclouds.providers.ProviderRegistry; +import org.osgi.framework.Bundle; +import org.osgi.framework.BundleEvent; +import org.osgi.framework.BundleListener; + +import java.io.BufferedReader; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.HashMap; +import java.util.Map; + +/** + * A {@link BundleListener} that listens for {@link BundleEvent} and searches for {@link org.jclouds.providers.ProviderMetadata} in newly + * installed Bundles. This is used as a workaround for OSGi environments where the ServiceLoader cannot cross bundle + * boundaries. + */ +public class ProviderBundleListener implements BundleListener { + + private Map bundleMetadataMap = new HashMap(); + + @Override + public void bundleChanged(BundleEvent event) { + ProviderMetadata metadata; + switch (event.getType()) { + case BundleEvent.STARTED: + metadata = getProviderMetadata(event.getBundle()); + if (metadata != null) { + ProviderRegistry.registerProvider(metadata); + bundleMetadataMap.put(event.getBundle().getBundleId(), metadata); + } + break; + case BundleEvent.STOPPING: + case BundleEvent.STOPPED: + metadata = bundleMetadataMap.get(event.getBundle().getBundleId()); + if (metadata != null) { + ProviderRegistry.uRegisterProvider(metadata); + } + break; + } + } + + /** + * Creates an instance of {@link ProviderMetadata} from the {@link Bundle}. + * + * @param bundle + * @return + */ + public ProviderMetadata getProviderMetadata(Bundle bundle) { + ProviderMetadata metadata = null; + String className = getProviderMetadataClassName(bundle); + if (className != null && !className.isEmpty()) { + try { + Class provideClass = bundle.loadClass(className); + metadata = provideClass.newInstance(); + } catch (ClassNotFoundException e) { + // ignore + } catch (InstantiationException e) { + // ignore + } catch (IllegalAccessException e) { + // ignore + } + } + return metadata; + } + + /** + * Retrieves the {@link ProviderMetadata} class name for the bundle if it exists. + * + * @param bundle + * @return + */ + public String getProviderMetadataClassName(Bundle bundle) { + URL resource = bundle.getEntry("/META-INF/services/org.jclouds.providers.ProviderMetadata"); + InputStream is = null; + InputStreamReader reader = null; + BufferedReader bufferedReader = null; + StringBuilder sb = new StringBuilder(); + + try { + is = resource.openStream(); + reader = new InputStreamReader(is, "UTF-8"); + bufferedReader = new BufferedReader(reader); + String line; + while ((line = bufferedReader.readLine()) != null) { + sb.append(line).append("\n"); + } + } catch (Throwable e) { + } finally { + try { + if (reader != null) + reader.close(); + } catch (Throwable e) { + } + try { + if (bufferedReader != null) + bufferedReader.close(); + } catch (Throwable e) { + } + try { + is.close(); + } catch (Throwable e) { + } + + } + return sb.toString().trim(); + } +} \ No newline at end of file diff --git a/core/src/main/java/org/jclouds/providers/ProviderRegistry.java b/core/src/main/java/org/jclouds/providers/ProviderRegistry.java new file mode 100644 index 0000000000..ef96bf35b6 --- /dev/null +++ b/core/src/main/java/org/jclouds/providers/ProviderRegistry.java @@ -0,0 +1,46 @@ +/** + * Licensed to jclouds, Inc. (jclouds) under one or more + * contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. jclouds 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.jclouds.providers; + +import java.util.HashSet; +import java.util.Set; + +/** + * A registry for holding {@link org.jclouds.providers.ProviderMetadata}. + */ +public class ProviderRegistry { + + private static final Set providers = new HashSet(); + + public static void registerProvider(ProviderMetadata provider) { + providers.add(provider); + } + + public static void uRegisterProvider(ProviderMetadata provider) { + providers.remove(provider); + } + + public static Iterable fromRegistry() { + return Iterable.class.cast(providers); + } + + public static void clear() { + providers.clear(); + } +} diff --git a/core/src/main/java/org/jclouds/providers/Providers.java b/core/src/main/java/org/jclouds/providers/Providers.java index da69a11e63..e57fb19daa 100644 --- a/core/src/main/java/org/jclouds/providers/Providers.java +++ b/core/src/main/java/org/jclouds/providers/Providers.java @@ -24,6 +24,7 @@ import static com.google.common.collect.Iterables.find; import java.util.NoSuchElementException; import java.util.ServiceLoader; +import com.google.common.collect.Iterables; import org.jclouds.Context; import org.jclouds.View; import org.jclouds.apis.ApiMetadata; @@ -82,7 +83,7 @@ public class Providers { * @return all available providers */ public static Iterable all() { - return fromServiceLoader(); + return Iterables.concat(fromServiceLoader(), ProviderRegistry.fromRegistry()); } /** diff --git a/project/pom.xml b/project/pom.xml index 787c34bb16..d978027be6 100644 --- a/project/pom.xml +++ b/project/pom.xml @@ -560,6 +560,7 @@ pageTracker._trackPageview(); NONE + ${jclouds.osgi.activator} ${project.artifactId} ${jclouds.osgi.export} ${jclouds.osgi.import}