Improved the discovery of providers inside OSGi

This commit is contained in:
Ioannis Canellos 2012-06-12 00:38:01 +03:00
parent 5e69df71d1
commit 6ffe7085bc
6 changed files with 256 additions and 1 deletions

View File

@ -41,6 +41,7 @@
<properties>
<jclouds.osgi.import>*</jclouds.osgi.import>
<jclouds.osgi.export>org.jclouds*;version=${project.version};-noimport:=true</jclouds.osgi.export>
<jclouds.osgi.activator>org.jclouds.osgi.Activator</jclouds.osgi.activator>
</properties>
<dependencies>
@ -106,6 +107,12 @@
<artifactId>guava</artifactId>
<version>12.0</version>
</dependency>
<dependency>
<groupId>org.osgi</groupId>
<artifactId>org.osgi.core</artifactId>
<version>4.2.0</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>

View File

@ -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.
* <p/>
* <p/>
* 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 <code>BundleActivator.start</code>
* 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.
* <p/>
* <p/>
* 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();
}
}

View File

@ -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<Long, ProviderMetadata> bundleMetadataMap = new HashMap<Long, ProviderMetadata>();
@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<? extends ProviderMetadata> 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();
}
}

View File

@ -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<ProviderMetadata> providers = new HashSet<ProviderMetadata>();
public static void registerProvider(ProviderMetadata provider) {
providers.add(provider);
}
public static void uRegisterProvider(ProviderMetadata provider) {
providers.remove(provider);
}
public static Iterable<ProviderMetadata> fromRegistry() {
return Iterable.class.cast(providers);
}
public static void clear() {
providers.clear();
}
}

View File

@ -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<ProviderMetadata> all() {
return fromServiceLoader();
return Iterables.concat(fromServiceLoader(), ProviderRegistry.fromRegistry());
}
/**

View File

@ -560,6 +560,7 @@ pageTracker._trackPageview();
<configuration>
<obrRepository>NONE</obrRepository>
<instructions>
<Bundle-Activator>${jclouds.osgi.activator}</Bundle-Activator>
<Bundle-SymbolicName>${project.artifactId}</Bundle-SymbolicName>
<Export-Package>${jclouds.osgi.export}</Export-Package>
<Import-Package>${jclouds.osgi.import}</Import-Package>