mirror of https://github.com/apache/jclouds.git
fix issue #1044: Added listener interfaces for Providers and Apis.
This commit is contained in:
parent
abd2fe19b7
commit
b7863ae394
|
@ -116,6 +116,12 @@
|
|||
<version>4.2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.osgi</groupId>
|
||||
<artifactId>org.osgi.compendium</artifactId>
|
||||
<version>4.2.0</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
@ -29,6 +29,7 @@ import org.jclouds.View;
|
|||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.reflect.TypeToken;
|
||||
|
||||
|
@ -69,7 +70,9 @@ public class Apis {
|
|||
* @return all available apis
|
||||
*/
|
||||
public static Iterable<ApiMetadata> all() {
|
||||
return Iterables.concat(fromServiceLoader(), ApiRegistry.fromRegistry());
|
||||
return ImmutableSet.<ApiMetadata>builder()
|
||||
.addAll(fromServiceLoader())
|
||||
.addAll(ApiRegistry.fromRegistry()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -22,10 +22,14 @@ import org.jclouds.apis.ApiRegistry;
|
|||
import org.jclouds.providers.ProviderRegistry;
|
||||
import org.osgi.framework.BundleActivator;
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.framework.ServiceReference;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
|
||||
public class Activator implements BundleActivator {
|
||||
|
||||
MetadataBundleListener bundleListener = new MetadataBundleListener();
|
||||
private ServiceTracker providerListenerTracker = null;
|
||||
private ServiceTracker apiListenerTracker = null;
|
||||
private MetadataBundleListener bundleListener = new MetadataBundleListener();
|
||||
|
||||
/**
|
||||
* Called when this bundle is started so the Framework can perform the
|
||||
|
@ -45,7 +49,46 @@ public class Activator implements BundleActivator {
|
|||
@Override
|
||||
public void start(BundleContext context) throws Exception {
|
||||
bundleListener.start(context);
|
||||
context.addBundleListener(bundleListener);
|
||||
providerListenerTracker = new ServiceTracker(context, ProviderListener.class.getName(), null) {
|
||||
@Override
|
||||
public Object addingService(ServiceReference reference) {
|
||||
Object obj = super.addingService(reference);
|
||||
if (ProviderListener.class.isAssignableFrom(obj.getClass())) {
|
||||
bundleListener.addProviderListener((ProviderListener) obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removedService(ServiceReference reference, Object service) {
|
||||
if (ProviderListener.class.isAssignableFrom(service.getClass())) {
|
||||
bundleListener.removeProviderListener((ProviderListener) service);
|
||||
}
|
||||
super.removedService(reference, service);
|
||||
}
|
||||
};
|
||||
|
||||
apiListenerTracker = new ServiceTracker(context, ApiListener.class.getName(), null) {
|
||||
@Override
|
||||
public Object addingService(ServiceReference reference) {
|
||||
Object obj = super.addingService(reference);
|
||||
if (ApiListener.class.isAssignableFrom(obj.getClass())) {
|
||||
bundleListener.addApiListenerListener((ApiListener) obj);
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removedService(ServiceReference reference, Object service) {
|
||||
if (ApiListener.class.isAssignableFrom(service.getClass())) {
|
||||
bundleListener.removeApiListenerListener((ApiListener) service);
|
||||
}
|
||||
super.removedService(reference, service);
|
||||
}
|
||||
};
|
||||
|
||||
providerListenerTracker.open();
|
||||
apiListenerTracker.open();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -68,8 +111,13 @@ public class Activator implements BundleActivator {
|
|||
@Override
|
||||
public void stop(BundleContext context) throws Exception {
|
||||
bundleListener.stop(context);
|
||||
context.removeBundleListener(bundleListener);
|
||||
ProviderRegistry.clear();
|
||||
ApiRegistry.clear();
|
||||
if (apiListenerTracker != null) {
|
||||
apiListenerTracker.close();
|
||||
}
|
||||
if (providerListenerTracker != null) {
|
||||
providerListenerTracker.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.osgi;
|
||||
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
|
||||
/**
|
||||
* A listener interface for {@link org.jclouds.apis.ApiMetadata}.
|
||||
* In OSGi a api can be added or removed dynamically.
|
||||
* OSGi services using this interface will receive a notification whenever this happens.
|
||||
*/
|
||||
public interface ApiListener {
|
||||
|
||||
/**
|
||||
* Method to be called when an api gets added.
|
||||
*
|
||||
* @param api The api that was added.
|
||||
* @param <A> The {@link org.jclouds.apis.ApiMetadata}.
|
||||
*/
|
||||
<A extends ApiMetadata> void added(A api);
|
||||
|
||||
/**
|
||||
* Method to be called when an api gets removed.
|
||||
*
|
||||
* @param api The api that was added.
|
||||
* @param <A> The {@link ApiMetadata}.
|
||||
*/
|
||||
<A extends ApiMetadata> void removed(A api);
|
||||
}
|
|
@ -0,0 +1,143 @@
|
|||
/**
|
||||
* 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 static com.google.common.base.Preconditions.checkNotNull;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
|
||||
import org.jclouds.util.Strings2;
|
||||
import org.osgi.framework.Bundle;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicates;
|
||||
import com.google.common.base.Splitter;
|
||||
import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
* Utility functions helpful in working with {@link Bundle bundles}.
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*/
|
||||
public final class Bundles {
|
||||
private Bundles() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* instantiates the supplied classnames using the bundle classloader, and
|
||||
* casts to the supplied type. Any errors are silently ignored.
|
||||
*
|
||||
* @return instances that could be instantiated without error.
|
||||
*/
|
||||
public static <T> ImmutableSet<T> instantiateAvailableClasses(Bundle bundle, Iterable<String> classNames, Class<T> type) {
|
||||
checkNotNull(bundle, "bundle");
|
||||
checkNotNull(classNames, "classNames");
|
||||
checkNotNull(type, "type");
|
||||
return FluentIterable.from(classNames)
|
||||
.transform(loadClassIfAssignableFrom(bundle, type))
|
||||
.filter(Predicates.notNull())
|
||||
.transform(instantiateIfPossible(type))
|
||||
.filter(Predicates.notNull())
|
||||
.toImmutableSet();
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that loads classes from the bundle, or returns null if the
|
||||
* class isn't found or assignable by the input parameter
|
||||
*
|
||||
* @param bundle
|
||||
* where to find classes
|
||||
* @param clazz
|
||||
* type classes must be assignable from
|
||||
*/
|
||||
private static Function<String, Class<?>> loadClassIfAssignableFrom(final Bundle bundle, final Class<?> clazz) {
|
||||
checkNotNull(bundle, "bundle");
|
||||
checkNotNull(clazz, "clazz");
|
||||
return new Function<String, Class<?>>() {
|
||||
@Override
|
||||
public Class<?> apply(String in) {
|
||||
checkNotNull(in, "classname");
|
||||
try {
|
||||
Class<?> thing = bundle.loadClass(in);
|
||||
// Classes loaded by other class loaders are not assignable.
|
||||
if (clazz.isAssignableFrom(thing))
|
||||
return thing.asSubclass(clazz);
|
||||
} catch (ClassNotFoundException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A function that instantiates classes or returns null, if it encounters any
|
||||
* problems.
|
||||
*
|
||||
* @param clazz
|
||||
* superclass to cast as
|
||||
*/
|
||||
private static <T> Function<Class<?>, T> instantiateIfPossible(final Class<T> clazz) {
|
||||
return new Function<Class<?>, T>() {
|
||||
@Override
|
||||
public T apply(Class<?> in) {
|
||||
checkNotNull(in, "input class");
|
||||
try {
|
||||
return clazz.cast(in.newInstance());
|
||||
} catch (InstantiationException e) {
|
||||
} catch (IllegalAccessException e) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the resource from a {@link Bundle}.
|
||||
*
|
||||
* @param resourcePath
|
||||
* The path to the resource.
|
||||
* @param bundle
|
||||
* The bundle to read from.
|
||||
* @return strings delimited by newline in the stream or empty set, on any
|
||||
* exception.
|
||||
*/
|
||||
public static ImmutableSet<String> stringsForResorceInBundle(String resourcePath, Bundle bundle) {
|
||||
checkNotNull(resourcePath, "resourcePath");
|
||||
checkNotNull(bundle, "bundle");
|
||||
|
||||
URL resource = bundle.getEntry(resourcePath);
|
||||
if (resource == null)
|
||||
return ImmutableSet.of();
|
||||
try {
|
||||
return ImmutableSet.copyOf(splitOrEmptyAndClose(resource.openStream()));
|
||||
} catch (IOException e) {
|
||||
return ImmutableSet.of();
|
||||
} catch (RuntimeException ex) {
|
||||
return ImmutableSet.of();
|
||||
}
|
||||
}
|
||||
|
||||
private static Iterable<String> splitOrEmptyAndClose(InputStream in) throws IOException {
|
||||
return Splitter.on('\n').omitEmptyStrings().split(Strings2.toStringAndClose(in));
|
||||
}
|
||||
}
|
|
@ -18,11 +18,11 @@
|
|||
*/
|
||||
package org.jclouds.osgi;
|
||||
|
||||
import com.google.common.base.Charsets;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.google.common.io.Closeables;
|
||||
import static org.jclouds.osgi.Bundles.instantiateAvailableClasses;
|
||||
import static org.jclouds.osgi.Bundles.stringsForResorceInBundle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.apis.ApiRegistry;
|
||||
import org.jclouds.providers.ProviderMetadata;
|
||||
|
@ -32,122 +32,127 @@ import org.osgi.framework.BundleContext;
|
|||
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.Collection;
|
||||
import java.util.List;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
|
||||
/**
|
||||
* A {@link BundleListener} that listens for {@link BundleEvent} and searches for {@link org.jclouds.providers.ProviderMetadata} and {@link org.jclouds.apis.ApiMetadata} in newly
|
||||
* installed Bundles. This is used as a workaround for OSGi environments where the ServiceLoader cannot cross bundle
|
||||
* boundaries.
|
||||
*
|
||||
* @author iocanel
|
||||
*/
|
||||
public class MetadataBundleListener implements BundleListener {
|
||||
|
||||
private Multimap<Long, ProviderMetadata> providerMetadataMap = ArrayListMultimap.create();
|
||||
private Multimap<Long, ApiMetadata> apiMetadataMap = ArrayListMultimap.create();
|
||||
private final Multimap<Long, ProviderMetadata> providerMetadataMap = ArrayListMultimap.create();
|
||||
private final Multimap<Long, ApiMetadata> apiMetadataMap = ArrayListMultimap.create();
|
||||
|
||||
private final List<ProviderListener> providerListeners = Lists.newArrayList();
|
||||
private final List<ApiListener> apiListeners = Lists.newArrayList();
|
||||
|
||||
|
||||
public void start(BundleContext bundleContext) {
|
||||
/**
|
||||
* Starts the listener.
|
||||
* Checks the bundles that are already active and registers {@link ProviderMetadata} and {@link ApiMetadata} found.
|
||||
* Registers the itself as a {@link BundleListener}.
|
||||
* @param bundleContext
|
||||
*/
|
||||
public synchronized void start(BundleContext bundleContext) {
|
||||
bundleContext.addBundleListener(this);
|
||||
for (Bundle bundle : bundleContext.getBundles()) {
|
||||
if (bundle.getState() == Bundle.ACTIVE) {
|
||||
List<ProviderMetadata> providerMetadataList = getProviderMetadata(bundle);
|
||||
List<ApiMetadata> apiMetadataList = getApiMetadata(bundle);
|
||||
|
||||
for (ProviderMetadata providerMetadata : providerMetadataList) {
|
||||
if (providerMetadata != null) {
|
||||
ProviderRegistry.registerProvider(providerMetadata);
|
||||
providerMetadataMap.put(bundle.getBundleId(), providerMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
for (ApiMetadata apiMetadata : apiMetadataList) {
|
||||
if (apiMetadata != null) {
|
||||
ApiRegistry.registerApi(apiMetadata);
|
||||
apiMetadataMap.put(bundle.getBundleId(), apiMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
addBundle(bundle);
|
||||
}
|
||||
}
|
||||
bundleContext.addBundleListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stops the listener.
|
||||
* Removes itself from the {@link BundleListener}s.
|
||||
* Clears metadata maps and listeners lists.
|
||||
* @param bundleContext
|
||||
*/
|
||||
public void stop(BundleContext bundleContext) {
|
||||
bundleContext.removeBundleListener(this);
|
||||
providerMetadataMap.clear();
|
||||
apiMetadataMap.clear();
|
||||
apiListeners.clear();
|
||||
providerListeners.clear();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void bundleChanged(BundleEvent event) {
|
||||
Collection<ProviderMetadata> providerMetadataList = null;
|
||||
Collection<ApiMetadata> apiMetadataList = null;
|
||||
public synchronized void bundleChanged(BundleEvent event) {
|
||||
switch (event.getType()) {
|
||||
case BundleEvent.STARTED:
|
||||
providerMetadataList = getProviderMetadata(event.getBundle());
|
||||
apiMetadataList = getApiMetadata(event.getBundle());
|
||||
for (ProviderMetadata providerMetadata : providerMetadataList) {
|
||||
if (providerMetadata != null) {
|
||||
ProviderRegistry.registerProvider(providerMetadata);
|
||||
providerMetadataMap.put(event.getBundle().getBundleId(), providerMetadata);
|
||||
}
|
||||
}
|
||||
|
||||
for (ApiMetadata apiMetadata : apiMetadataList) {
|
||||
if (apiMetadata != null) {
|
||||
ApiRegistry.registerApi(apiMetadata);
|
||||
apiMetadataMap.put(event.getBundle().getBundleId(), apiMetadata);
|
||||
}
|
||||
}
|
||||
addBundle(event.getBundle());
|
||||
break;
|
||||
case BundleEvent.STOPPING:
|
||||
case BundleEvent.STOPPED:
|
||||
providerMetadataList = providerMetadataMap.get(event.getBundle().getBundleId());
|
||||
apiMetadataList = apiMetadataMap.get(event.getBundle().getBundleId());
|
||||
|
||||
if (providerMetadataList != null) {
|
||||
for (ProviderMetadata providerMetadata : providerMetadataList) {
|
||||
ProviderRegistry.unregisterProvider(providerMetadata);
|
||||
}
|
||||
}
|
||||
if (apiMetadataList != null) {
|
||||
for (ApiMetadata apiMetadata : apiMetadataList) {
|
||||
ApiRegistry.unRegisterApi(apiMetadata);
|
||||
}
|
||||
}
|
||||
removeBundle(event.getBundle());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for {@link ProviderMetadata} and {@link ApiMetadata} inside the {@link Bundle}.
|
||||
* If metadata are found they are registered in the {@link ProviderRegistry} and {@link ApiRegistry}.
|
||||
* Also the {@link ProviderListener} and {@link ApiListener} are notified.
|
||||
* @param bundle
|
||||
*/
|
||||
private synchronized void addBundle(Bundle bundle) {
|
||||
for (ProviderMetadata providerMetadata : listProviderMetadata(bundle)) {
|
||||
if (providerMetadata != null) {
|
||||
ProviderRegistry.registerProvider(providerMetadata);
|
||||
providerMetadataMap.put(bundle.getBundleId(), providerMetadata);
|
||||
for (ProviderListener listener : providerListeners) {
|
||||
listener.added(providerMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (ApiMetadata apiMetadata : listApiMetadata(bundle)) {
|
||||
if (apiMetadata != null) {
|
||||
ApiRegistry.registerApi(apiMetadata);
|
||||
apiMetadataMap.put(bundle.getBundleId(), apiMetadata);
|
||||
for (ApiListener listener : apiListeners) {
|
||||
listener.added(apiMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for {@link ProviderMetadata} and {@link ApiMetadata} registered under the {@link Bundle} id.
|
||||
* If metadata are found they are removed the {@link ProviderRegistry} and {@link ApiRegistry}.
|
||||
* Also the {@link ProviderListener} and {@link ApiListener} are notified.
|
||||
* @param bundle
|
||||
*/
|
||||
private synchronized void removeBundle(Bundle bundle) {
|
||||
for (ProviderMetadata providerMetadata : providerMetadataMap.removeAll(bundle.getBundleId())) {
|
||||
ProviderRegistry.unregisterProvider(providerMetadata);
|
||||
for (ProviderListener listener : providerListeners) {
|
||||
listener.removed(providerMetadata);
|
||||
}
|
||||
}
|
||||
for (ApiMetadata apiMetadata : apiMetadataMap.removeAll(bundle.getBundleId())) {
|
||||
ApiRegistry.unRegisterApi(apiMetadata);
|
||||
for (ApiListener listener : apiListeners) {
|
||||
listener.removed(apiMetadata);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an instance of {@link ProviderMetadata} from the {@link Bundle}.
|
||||
*
|
||||
* @param bundle
|
||||
* @return
|
||||
*/
|
||||
public List<ProviderMetadata> getProviderMetadata(Bundle bundle) {
|
||||
List<ProviderMetadata> metadataList = Lists.newArrayList();
|
||||
String classNames = getProviderMetadataClassNames(bundle);
|
||||
if (classNames != null && !classNames.isEmpty()) {
|
||||
for (String className : classNames.split("\n")) {
|
||||
try {
|
||||
Class<? extends ProviderMetadata> providerMetadataClass = bundle.loadClass(className);
|
||||
//Classes loaded by other class loaders are not assignable.
|
||||
if (ProviderMetadata.class.isAssignableFrom(providerMetadataClass)) {
|
||||
ProviderMetadata metadata = providerMetadataClass.newInstance();
|
||||
metadataList.add(metadata);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// ignore
|
||||
} catch (InstantiationException e) {
|
||||
// ignore
|
||||
} catch (IllegalAccessException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadataList;
|
||||
public Iterable<ProviderMetadata> listProviderMetadata(Bundle bundle) {
|
||||
Iterable<String> classNames = stringsForResorceInBundle("/META-INF/services/org.jclouds.providers.ProviderMetadata", bundle);
|
||||
return instantiateAvailableClasses(bundle, classNames, ProviderMetadata.class);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -156,73 +161,51 @@ public class MetadataBundleListener implements BundleListener {
|
|||
* @param bundle
|
||||
* @return
|
||||
*/
|
||||
public List<ApiMetadata> getApiMetadata(Bundle bundle) {
|
||||
List<ApiMetadata> metadataList = Lists.newArrayList();
|
||||
String classNames = getApiMetadataClassNames(bundle);
|
||||
if (classNames != null && !classNames.isEmpty()) {
|
||||
for (String className : classNames.split("\n")) {
|
||||
try {
|
||||
Class<? extends ApiMetadata> apiMetadataClass = bundle.loadClass(className);
|
||||
//Classes loaded by other class loaders are not assignable.
|
||||
if (ApiMetadata.class.isAssignableFrom(apiMetadataClass)) {
|
||||
ApiMetadata metadata = apiMetadataClass.newInstance();
|
||||
metadataList.add(metadata);
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
// ignore
|
||||
} catch (InstantiationException e) {
|
||||
// ignore
|
||||
} catch (IllegalAccessException e) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadataList;
|
||||
}
|
||||
|
||||
|
||||
public String getMetadataClassNames(Bundle bundle, String pathToMetadata) {
|
||||
URL resource = bundle.getEntry(pathToMetadata);
|
||||
InputStream is = null;
|
||||
InputStreamReader reader = null;
|
||||
BufferedReader bufferedReader = null;
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
||||
try {
|
||||
is = resource.openStream();
|
||||
reader = new InputStreamReader(is, Charsets.UTF_8);
|
||||
bufferedReader = new BufferedReader(reader);
|
||||
String line;
|
||||
while ((line = bufferedReader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
} finally {
|
||||
Closeables.closeQuietly(reader);
|
||||
Closeables.closeQuietly(bufferedReader);
|
||||
Closeables.closeQuietly(is);
|
||||
}
|
||||
return sb.toString().trim();
|
||||
public Iterable<ApiMetadata> listApiMetadata(Bundle bundle) {
|
||||
Iterable<String> classNames = stringsForResorceInBundle("/META-INF/services/org.jclouds.apis.ApiMetadata", bundle);
|
||||
return instantiateAvailableClasses(bundle, classNames, ApiMetadata.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the {@link ProviderMetadata} class name for the bundle if it exists.
|
||||
* Adds a {@link ProviderListener} and notifies it of existing {@link ProviderMetadata}.
|
||||
*
|
||||
* @param bundle
|
||||
* @return
|
||||
* @param listener The listener.
|
||||
*/
|
||||
public String getProviderMetadataClassNames(Bundle bundle) {
|
||||
return getMetadataClassNames(bundle, "/META-INF/services/org.jclouds.providers.ProviderMetadata");
|
||||
public synchronized void addProviderListener(ProviderListener listener) {
|
||||
providerListeners.add(listener);
|
||||
for (ProviderMetadata metadata : providerMetadataMap.values()) {
|
||||
listener.added(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the {@link ProviderMetadata} class name for the bundle if it exists.
|
||||
* Removes the {@link ProviderListener}
|
||||
*
|
||||
* @param bundle
|
||||
* @return
|
||||
* @param listener The listener
|
||||
*/
|
||||
public String getApiMetadataClassNames(Bundle bundle) {
|
||||
return getMetadataClassNames(bundle, "/META-INF/services/org.jclouds.apis.ApiMetadata");
|
||||
public synchronized void removeProviderListener(ProviderListener listener) {
|
||||
providerListeners.remove(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a {@link ApiListener} and notifies it of existing {@link ApiMetadata}.
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public synchronized void addApiListenerListener(ApiListener listener) {
|
||||
apiListeners.add(listener);
|
||||
for (ApiMetadata metadata : apiMetadataMap.values()) {
|
||||
listener.added(metadata);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the {@link ApiListener}
|
||||
*
|
||||
* @param listener
|
||||
*/
|
||||
public synchronized void removeApiListenerListener(ApiListener listener) {
|
||||
apiListeners.remove(listener);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* A listener interface for {@link ProviderMetadata}.
|
||||
* In OSGi a provider can be added or removed dynamically.
|
||||
* OSGi services using this interface will receive a notification whenever this happens.
|
||||
*/
|
||||
public interface ProviderListener {
|
||||
|
||||
/**
|
||||
* Method to be called when a Provider gets added.
|
||||
*
|
||||
* @param provider The provider that was added.
|
||||
* @param <P> The {@link ProviderMetadata}.
|
||||
*/
|
||||
<P extends ProviderMetadata> void added(P provider);
|
||||
|
||||
/**
|
||||
* Method to be called when a Provider gets removed.
|
||||
*
|
||||
* @param provider The provider that was added.
|
||||
* @param <P> The {@link ProviderMetadata}.
|
||||
*/
|
||||
<P extends ProviderMetadata> void removed(P provider);
|
||||
|
||||
}
|
|
@ -24,7 +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 com.google.common.collect.ImmutableSet;
|
||||
import org.jclouds.Context;
|
||||
import org.jclouds.View;
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
|
@ -83,7 +83,9 @@ public class Providers {
|
|||
* @return all available providers
|
||||
*/
|
||||
public static Iterable<ProviderMetadata> all() {
|
||||
return Iterables.concat(fromServiceLoader(), ProviderRegistry.fromRegistry());
|
||||
return ImmutableSet.<ProviderMetadata>builder()
|
||||
.addAll(fromServiceLoader())
|
||||
.addAll(ProviderRegistry.fromRegistry()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
/**
|
||||
* 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 static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
|
||||
import org.jclouds.apis.JcloudsTestComputeApiMetadata;
|
||||
import org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata;
|
||||
import org.jclouds.providers.JcloudsTestComputeProviderMetadata;
|
||||
import org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata;
|
||||
import org.jclouds.providers.ProviderMetadata;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Adrian Cole
|
||||
*
|
||||
*/
|
||||
public class BundlesTest {
|
||||
|
||||
@Test
|
||||
public void testInstantiateAvailableClassesWhenAllAssignable() throws ClassNotFoundException {
|
||||
Bundle bundle = createMock(Bundle.class);
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata"))
|
||||
.andReturn(JcloudsTestBlobStoreProviderMetadata.class);
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestComputeProviderMetadata"))
|
||||
.andReturn(JcloudsTestComputeProviderMetadata.class);
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata"))
|
||||
.andReturn(JcloudsTestYetAnotherComputeProviderMetadata.class);
|
||||
replay(bundle);
|
||||
|
||||
Iterable<ProviderMetadata> providers = Bundles.instantiateAvailableClasses(bundle, ImmutableSet.of(
|
||||
"org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata",
|
||||
"org.jclouds.providers.JcloudsTestComputeProviderMetadata",
|
||||
"org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata"), ProviderMetadata.class);
|
||||
assertEquals(providers, ImmutableSet.of(new JcloudsTestBlobStoreProviderMetadata(),
|
||||
new JcloudsTestComputeProviderMetadata(), new JcloudsTestYetAnotherComputeProviderMetadata()));
|
||||
|
||||
verify(bundle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInstantiateAvailableClassesWhenNotAllAssignable() throws ClassNotFoundException {
|
||||
Bundle bundle = createMock(Bundle.class);
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata"))
|
||||
.andReturn(JcloudsTestBlobStoreProviderMetadata.class);
|
||||
expect(bundle.loadClass("org.jclouds.apis.JcloudsTestComputeApiMetadata"))
|
||||
.andReturn(JcloudsTestComputeApiMetadata.class);
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata"))
|
||||
.andReturn(JcloudsTestYetAnotherComputeProviderMetadata.class);
|
||||
replay(bundle);
|
||||
|
||||
Iterable<ProviderMetadata> providers = Bundles.instantiateAvailableClasses(bundle, ImmutableSet.of(
|
||||
"org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata",
|
||||
"org.jclouds.apis.JcloudsTestComputeApiMetadata",
|
||||
"org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata"), ProviderMetadata.class);
|
||||
assertEquals(providers, ImmutableSet.of(new JcloudsTestBlobStoreProviderMetadata(),
|
||||
new JcloudsTestYetAnotherComputeProviderMetadata()));
|
||||
|
||||
verify(bundle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringsForResourcesInBundleWhenNoResources() throws Exception {
|
||||
|
||||
Bundle bundle = createMock(Bundle.class);
|
||||
expect(bundle.getEntry("/META-INF/services/org.jclouds.apis.ApiMetadata")).andReturn(null);
|
||||
replay(bundle);
|
||||
|
||||
assertEquals(Bundles.stringsForResorceInBundle("/META-INF/services/org.jclouds.apis.ApiMetadata", bundle),
|
||||
ImmutableSet.of());
|
||||
|
||||
verify(bundle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStringsForResourcesInBundleWhenResourcePresent() throws Exception {
|
||||
|
||||
Bundle bundle = createMock(Bundle.class);
|
||||
expect(bundle.getEntry("/META-INF/services/org.jclouds.providers.ProviderMetadata"))
|
||||
.andReturn(getClass().getResource("/META-INF/services/org.jclouds.providers.ProviderMetadata"));
|
||||
replay(bundle);
|
||||
|
||||
assertEquals(Bundles.stringsForResorceInBundle(
|
||||
"/META-INF/services/org.jclouds.providers.ProviderMetadata", bundle), ImmutableSet.of(
|
||||
"org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata",
|
||||
"org.jclouds.providers.JcloudsTestComputeProviderMetadata",
|
||||
"org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata"));
|
||||
|
||||
verify(bundle);
|
||||
}
|
||||
}
|
|
@ -18,6 +18,7 @@
|
|||
*/
|
||||
package org.jclouds.osgi;
|
||||
|
||||
import com.google.common.collect.Lists;
|
||||
import org.jclouds.apis.ApiMetadata;
|
||||
import org.jclouds.apis.JcloudsTestBlobStoreApiMetadata;
|
||||
import org.jclouds.apis.JcloudsTestComputeApiMetadata;
|
||||
|
@ -27,6 +28,7 @@ import org.jclouds.providers.JcloudsTestComputeProviderMetadata;
|
|||
import org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata;
|
||||
import org.jclouds.providers.ProviderMetadata;
|
||||
import org.osgi.framework.Bundle;
|
||||
import org.osgi.framework.BundleEvent;
|
||||
import org.testng.annotations.Test;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
|
@ -34,16 +36,22 @@ import java.net.URL;
|
|||
import java.net.URLClassLoader;
|
||||
import java.util.List;
|
||||
|
||||
import static org.easymock.EasyMock.anyObject;
|
||||
import static org.easymock.EasyMock.createMock;
|
||||
import static org.easymock.EasyMock.expect;
|
||||
import static org.easymock.EasyMock.expectLastCall;
|
||||
import static org.easymock.EasyMock.replay;
|
||||
import static org.easymock.EasyMock.verify;
|
||||
import static org.testng.Assert.assertEquals;
|
||||
import static org.testng.Assert.assertFalse;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
import static org.testng.Assert.assertNotNull;
|
||||
import static org.testng.Assert.assertTrue;
|
||||
|
||||
|
||||
/**
|
||||
*
|
||||
* @author iocanel
|
||||
*
|
||||
*/
|
||||
public class MetadataBundleListenerTest {
|
||||
|
||||
@Test
|
||||
|
@ -62,9 +70,8 @@ public class MetadataBundleListenerTest {
|
|||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata")).andReturn(JcloudsTestBlobStoreProviderMetadata.class).anyTimes();
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestComputeProviderMetadata")).andReturn(JcloudsTestComputeProviderMetadata.class).anyTimes();
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata")).andReturn(JcloudsTestYetAnotherComputeProviderMetadata.class).anyTimes();
|
||||
|
||||
replay(bundle);
|
||||
List<ProviderMetadata> providerMetadataList = listener.getProviderMetadata(bundle);
|
||||
List<ProviderMetadata> providerMetadataList = Lists.newArrayList(listener.listProviderMetadata(bundle));
|
||||
assertNotNull(providerMetadataList);
|
||||
assertEquals(3, providerMetadataList.size());
|
||||
assertTrue(providerMetadataList.contains(new JcloudsTestBlobStoreProviderMetadata()));
|
||||
|
@ -73,6 +80,33 @@ public class MetadataBundleListenerTest {
|
|||
verify(bundle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProviderListener() throws Exception {
|
||||
MetadataBundleListener listener = new MetadataBundleListener();
|
||||
ProviderListener providerListener = createMock(ProviderListener.class);
|
||||
listener.addProviderListener(providerListener);
|
||||
|
||||
Bundle bundle = createMock(Bundle.class);
|
||||
expect(bundle.getBundleId()).andReturn(10L).anyTimes();
|
||||
expect(bundle.getEntry("/META-INF/services/org.jclouds.providers.ProviderMetadata")).andReturn(getClass().getResource("/META-INF/services/org.jclouds.providers.ProviderMetadata")).anyTimes();
|
||||
expect(bundle.getEntry("/META-INF/services/org.jclouds.apis.ApiMetadata")).andReturn(null).anyTimes();
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata")).andReturn(JcloudsTestBlobStoreProviderMetadata.class).anyTimes();
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestComputeProviderMetadata")).andReturn(JcloudsTestComputeProviderMetadata.class).anyTimes();
|
||||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata")).andReturn(JcloudsTestYetAnotherComputeProviderMetadata.class).anyTimes();
|
||||
|
||||
providerListener.added(anyObject(JcloudsTestBlobStoreProviderMetadata.class));
|
||||
expectLastCall().times(1);
|
||||
providerListener.added(anyObject(JcloudsTestComputeProviderMetadata.class));
|
||||
expectLastCall().times(1);
|
||||
providerListener.added(anyObject(JcloudsTestYetAnotherComputeProviderMetadata.class));
|
||||
expectLastCall().times(1);
|
||||
replay(bundle, providerListener);
|
||||
|
||||
BundleEvent event = new BundleEvent(BundleEvent.STARTED, bundle);
|
||||
listener.bundleChanged(event);
|
||||
verify(bundle, providerListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetProviderMetadataFromMultipleClassLoaders() throws Exception {
|
||||
ClassLoader isolatedClassLoader = createIsolatedClassLoader();
|
||||
|
@ -84,7 +118,7 @@ public class MetadataBundleListenerTest {
|
|||
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestYetAnotherComputeProviderMetadata")).andReturn(JcloudsTestYetAnotherComputeProviderMetadata.class).anyTimes();
|
||||
|
||||
replay(bundle);
|
||||
List<ProviderMetadata> providerMetadataList = listener.getProviderMetadata(bundle);
|
||||
List<ProviderMetadata> providerMetadataList = Lists.newArrayList(listener.listProviderMetadata(bundle));
|
||||
assertNotNull(providerMetadataList);
|
||||
assertEquals(2, providerMetadataList.size());
|
||||
assertFalse(providerMetadataList.contains(new JcloudsTestBlobStoreProviderMetadata()));
|
||||
|
@ -103,7 +137,7 @@ public class MetadataBundleListenerTest {
|
|||
expect(bundle.loadClass("org.jclouds.apis.JcloudsTestYetAnotherComputeApiMetadata")).andReturn(JcloudsTestYetAnotherComputeApiMetadata.class).anyTimes();
|
||||
|
||||
replay(bundle);
|
||||
List<ApiMetadata> apiMetadataList = listener.getApiMetadata(bundle);
|
||||
List<ApiMetadata> apiMetadataList = Lists.newArrayList(listener.listApiMetadata(bundle));
|
||||
assertNotNull(apiMetadataList);
|
||||
assertEquals(3, apiMetadataList.size());
|
||||
assertTrue(apiMetadataList.contains(new JcloudsTestBlobStoreApiMetadata()));
|
||||
|
@ -112,6 +146,34 @@ public class MetadataBundleListenerTest {
|
|||
verify(bundle);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testApiListener() throws Exception {
|
||||
MetadataBundleListener listener = new MetadataBundleListener();
|
||||
ApiListener apiListener = createMock(ApiListener.class);
|
||||
listener.addApiListenerListener(apiListener);
|
||||
|
||||
Bundle bundle = createMock(Bundle.class);
|
||||
expect(bundle.getBundleId()).andReturn(10L).anyTimes();
|
||||
expect(bundle.getEntry("/META-INF/services/org.jclouds.providers.ProviderMetadata")).andReturn(null).anyTimes();
|
||||
expect(bundle.getEntry("/META-INF/services/org.jclouds.apis.ApiMetadata")).andReturn(getClass().getResource("/META-INF/services/org.jclouds.apis.ApiMetadata")).anyTimes();
|
||||
expect(bundle.loadClass("org.jclouds.apis.JcloudsTestBlobStoreApiMetadata")).andReturn(JcloudsTestBlobStoreApiMetadata.class).anyTimes();
|
||||
expect(bundle.loadClass("org.jclouds.apis.JcloudsTestComputeApiMetadata")).andReturn(JcloudsTestComputeApiMetadata.class).anyTimes();
|
||||
expect(bundle.loadClass("org.jclouds.apis.JcloudsTestYetAnotherComputeApiMetadata")).andReturn(JcloudsTestYetAnotherComputeApiMetadata.class).anyTimes();
|
||||
|
||||
|
||||
apiListener.added(anyObject(JcloudsTestBlobStoreApiMetadata.class));
|
||||
expectLastCall().times(1);
|
||||
apiListener.added(anyObject(JcloudsTestBlobStoreApiMetadata.class));
|
||||
expectLastCall().times(1);
|
||||
apiListener.added(anyObject(JcloudsTestComputeApiMetadata.class));
|
||||
expectLastCall().times(1);
|
||||
replay(bundle, apiListener);
|
||||
|
||||
BundleEvent event = new BundleEvent(BundleEvent.STARTED, bundle);
|
||||
listener.bundleChanged(event);
|
||||
verify(bundle, apiListener);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetApiMetadataFromMultipleClassLoaders() throws Exception {
|
||||
ClassLoader isolatedClassLoader = createIsolatedClassLoader();
|
||||
|
@ -123,7 +185,8 @@ public class MetadataBundleListenerTest {
|
|||
expect(bundle.loadClass("org.jclouds.apis.JcloudsTestYetAnotherComputeApiMetadata")).andReturn(JcloudsTestYetAnotherComputeApiMetadata.class).anyTimes();
|
||||
|
||||
replay(bundle);
|
||||
List<ApiMetadata> apiMetadataList = listener.getApiMetadata(bundle);
|
||||
|
||||
List<ApiMetadata> apiMetadataList = Lists.newArrayList(listener.listApiMetadata(bundle));
|
||||
assertNotNull(apiMetadataList);
|
||||
assertEquals(2, apiMetadataList.size());
|
||||
assertFalse(apiMetadataList.contains(new JcloudsTestBlobStoreApiMetadata()));
|
||||
|
|
|
@ -384,6 +384,7 @@
|
|||
<ignoredResource>test.jks</ignoredResource>
|
||||
<ignoredResource>CreateInternetService-options-test.xml</ignoredResource>
|
||||
<ignoredResource>.gitattributes</ignoredResource>
|
||||
<ignoredResource>OSGI-OPT/bnd.bnd</ignoredResource>
|
||||
</ignoredResources>
|
||||
<failBuildInCaseOfConflict>true</failBuildInCaseOfConflict>
|
||||
</configuration>
|
||||
|
|
Loading…
Reference in New Issue