MetadataBundleListener now works with multiple providers/apis per bundle. Added a class assignable check in MetadataBundleListener. Added unit test for MetadataBundleListener. Fixed spelling and formatting of MetadataBundleListener and MetadataBundleListenerTest.

This commit is contained in:
Ioannis Canellos 2012-10-31 11:10:20 +02:00
parent c98f7d7b72
commit 19b3400f14
2 changed files with 320 additions and 153 deletions

View File

@ -18,6 +18,11 @@
*/ */
package org.jclouds.osgi; 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 org.jclouds.apis.ApiMetadata; import org.jclouds.apis.ApiMetadata;
import org.jclouds.apis.ApiRegistry; import org.jclouds.apis.ApiRegistry;
import org.jclouds.providers.ProviderMetadata; import org.jclouds.providers.ProviderMetadata;
@ -31,8 +36,8 @@ import java.io.BufferedReader;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.net.URL; import java.net.URL;
import java.util.HashMap; import java.util.Collection;
import java.util.Map; import java.util.List;
/** /**
* A {@link BundleListener} that listens for {@link BundleEvent} and searches for {@link org.jclouds.providers.ProviderMetadata} and {@link org.jclouds.apis.ApiMetadata} in newly * A {@link BundleListener} that listens for {@link BundleEvent} and searches for {@link org.jclouds.providers.ProviderMetadata} and {@link org.jclouds.apis.ApiMetadata} in newly
@ -41,168 +46,183 @@ import java.util.Map;
*/ */
public class MetadataBundleListener implements BundleListener { public class MetadataBundleListener implements BundleListener {
private Map<Long, ProviderMetadata> providerMetadataMap = new HashMap<Long, ProviderMetadata>(); private Multimap<Long, ProviderMetadata> providerMetadataMap = ArrayListMultimap.create();
private Map<Long, ApiMetadata> apiMetadataMap = new HashMap<Long, ApiMetadata>(); private Multimap<Long, ApiMetadata> apiMetadataMap = ArrayListMultimap.create();
public void start(BundleContext bundleContext) { public void start(BundleContext bundleContext) {
bundleContext.addBundleListener(this); bundleContext.addBundleListener(this);
for (Bundle bundle : bundleContext.getBundles()) { for (Bundle bundle : bundleContext.getBundles()) {
if (bundle.getState() == Bundle.ACTIVE) { if (bundle.getState() == Bundle.ACTIVE) {
ProviderMetadata providerMetadata = getProviderMetadata(bundle); List<ProviderMetadata> providerMetadataList = getProviderMetadata(bundle);
ApiMetadata apiMetadata = getApiMetadata(bundle); List<ApiMetadata> apiMetadataList = getApiMetadata(bundle);
if (providerMetadata != null) { for (ProviderMetadata providerMetadata : providerMetadataList) {
ProviderRegistry.registerProvider(providerMetadata); if (providerMetadata != null) {
providerMetadataMap.put(bundle.getBundleId(), providerMetadata); ProviderRegistry.registerProvider(providerMetadata);
} providerMetadataMap.put(bundle.getBundleId(), providerMetadata);
if (apiMetadata != null) { }
ApiRegistry.registerApi(apiMetadata); }
apiMetadataMap.put(bundle.getBundleId(), apiMetadata);
} for (ApiMetadata apiMetadata : apiMetadataList) {
if (apiMetadata != null) {
ApiRegistry.registerApi(apiMetadata);
apiMetadataMap.put(bundle.getBundleId(), apiMetadata);
}
}
}
} }
} }
}
public void stop(BundleContext bundleContext) { public void stop(BundleContext bundleContext) {
providerMetadataMap.clear(); providerMetadataMap.clear();
apiMetadataMap.clear(); apiMetadataMap.clear();
} }
@Override @Override
public void bundleChanged(BundleEvent event) { public void bundleChanged(BundleEvent event) {
ProviderMetadata providerMetadata; Collection<ProviderMetadata> providerMetadataList = null;
ApiMetadata apiMetadata; Collection<ApiMetadata> apiMetadataList = null;
switch (event.getType()) { switch (event.getType()) {
case BundleEvent.STARTED: case BundleEvent.STARTED:
providerMetadata = getProviderMetadata(event.getBundle()); providerMetadataList = getProviderMetadata(event.getBundle());
apiMetadata = getApiMetadata(event.getBundle()); apiMetadataList = getApiMetadata(event.getBundle());
if (providerMetadata != null) { for (ProviderMetadata providerMetadata : providerMetadataList) {
ProviderRegistry.registerProvider(providerMetadata); if (providerMetadata != null) {
providerMetadataMap.put(event.getBundle().getBundleId(), providerMetadata); ProviderRegistry.registerProvider(providerMetadata);
} providerMetadataMap.put(event.getBundle().getBundleId(), providerMetadata);
if (apiMetadata != null) { }
ApiRegistry.registerApi(apiMetadata); }
apiMetadataMap.put(event.getBundle().getBundleId(), apiMetadata);
} for (ApiMetadata apiMetadata : apiMetadataList) {
break; if (apiMetadata != null) {
case BundleEvent.STOPPING: ApiRegistry.registerApi(apiMetadata);
case BundleEvent.STOPPED: apiMetadataMap.put(event.getBundle().getBundleId(), apiMetadata);
providerMetadata = providerMetadataMap.get(event.getBundle().getBundleId()); }
apiMetadata = apiMetadataMap.get(event.getBundle().getBundleId()); }
if (providerMetadata != null) { break;
ProviderRegistry.unregisterProvider(providerMetadata); case BundleEvent.STOPPING:
} case BundleEvent.STOPPED:
if (apiMetadata != null) { providerMetadataList = providerMetadataMap.get(event.getBundle().getBundleId());
ApiRegistry.unRegisterApi(apiMetadata); apiMetadataList = apiMetadataMap.get(event.getBundle().getBundleId());
}
break; if (providerMetadataList != null) {
} for (ProviderMetadata providerMetadata : providerMetadataList) {
} ProviderRegistry.unregisterProvider(providerMetadata);
}
}
if (apiMetadataList != null) {
for (ApiMetadata apiMetadata : apiMetadataList) {
ApiRegistry.unRegisterApi(apiMetadata);
}
}
break;
}
}
/**
* 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;
}
/**
* Creates an instance of {@link ApiMetadata} from the {@link Bundle}.
*
* @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();
/**
* 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 { try {
Class<? extends ProviderMetadata> providerMetadataClass = bundle.loadClass(className); is = resource.openStream();
metadata = providerMetadataClass.newInstance(); reader = new InputStreamReader(is, Charsets.UTF_8);
} catch (ClassNotFoundException e) { bufferedReader = new BufferedReader(reader);
// ignore String line;
} catch (InstantiationException e) { while ((line = bufferedReader.readLine()) != null) {
// ignore sb.append(line).append("\n");
} catch (IllegalAccessException e) { }
// ignore
}
}
return metadata;
}
/**
* Creates an instance of {@link ApiMetadata} from the {@link Bundle}.
*
* @param bundle
* @return
*/
public ApiMetadata getApiMetadata(Bundle bundle) {
ApiMetadata metadata = null;
String className = getApiMetadataClassName(bundle);
if (className != null && !className.isEmpty()) {
try {
Class<? extends ApiMetadata> apiMetadataClass = bundle.loadClass(className);
metadata = apiMetadataClass.newInstance();
} catch (ClassNotFoundException e) {
// ignore
} catch (InstantiationException e) {
// ignore
} catch (IllegalAccessException e) {
// ignore
}
}
return metadata;
}
public String getMetadataClassName(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, "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) { } catch (Throwable e) {
} finally {
Closeables.closeQuietly(reader);
Closeables.closeQuietly(bufferedReader);
Closeables.closeQuietly(is);
} }
return sb.toString().trim();
}
} /**
return sb.toString().trim(); * Retrieves the {@link ProviderMetadata} class name for the bundle if it exists.
} *
* @param bundle
* @return
*/
public String getProviderMetadataClassNames(Bundle bundle) {
return getMetadataClassNames(bundle, "/META-INF/services/org.jclouds.providers.ProviderMetadata");
}
/** /**
* Retrieves the {@link ProviderMetadata} class name for the bundle if it exists. * Retrieves the {@link ProviderMetadata} class name for the bundle if it exists.
* *
* @param bundle * @param bundle
* @return * @return
*/ */
public String getProviderMetadataClassName(Bundle bundle) { public String getApiMetadataClassNames(Bundle bundle) {
return getMetadataClassName(bundle, "/META-INF/services/org.jclouds.providers.ProviderMetadata"); return getMetadataClassNames(bundle, "/META-INF/services/org.jclouds.apis.ApiMetadata");
} }
/**
* Retrieves the {@link ProviderMetadata} class name for the bundle if it exists.
*
* @param bundle
* @return
*/
public String getApiMetadataClassName(Bundle bundle) {
return getMetadataClassName(bundle, "/META-INF/services/org.jclouds.apis.ApiMetadata");
}
} }

View File

@ -0,0 +1,147 @@
/**
* 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;
import org.jclouds.apis.JcloudsTestBlobStoreApiMetadata;
import org.jclouds.apis.JcloudsTestComputeApiMetadata;
import org.jclouds.apis.JcloudsTestYetAnotherComputeApiMetadata;
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 java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
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 static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.assertNotNull;
public class MetadataBundleListenerTest {
@Test
public void testSanity() throws MalformedURLException, ClassNotFoundException {
//We are checking here that the class loader we create and use in this test series is indeed different and isolated from our tests classloader.
ClassLoader loader = createIsolatedClassLoader();
assertFalse(ProviderMetadata.class.isAssignableFrom(loader.loadClass("org.jclouds.providers.JcloudsTestComputeProviderMetadata")));
}
@Test
public void testGetProviderMetadata() throws Exception {
MetadataBundleListener listener = new MetadataBundleListener();
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")).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();
replay(bundle);
List<ProviderMetadata> providerMetadataList = listener.getProviderMetadata(bundle);
assertNotNull(providerMetadataList);
assertEquals(3, providerMetadataList.size());
assertTrue(providerMetadataList.contains(new JcloudsTestBlobStoreProviderMetadata()));
assertTrue(providerMetadataList.contains(new JcloudsTestComputeProviderMetadata()));
assertTrue(providerMetadataList.contains(new JcloudsTestYetAnotherComputeProviderMetadata()));
verify(bundle);
}
@Test
public void testGetProviderMetadataFromMultipleClassLoaders() throws Exception {
ClassLoader isolatedClassLoader = createIsolatedClassLoader();
MetadataBundleListener listener = new MetadataBundleListener();
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")).anyTimes();
expect(bundle.loadClass("org.jclouds.providers.JcloudsTestBlobStoreProviderMetadata")).andReturn(isolatedClassLoader.loadClass(JcloudsTestBlobStoreProviderMetadata.class.getName())).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);
assertNotNull(providerMetadataList);
assertEquals(2, providerMetadataList.size());
assertFalse(providerMetadataList.contains(new JcloudsTestBlobStoreProviderMetadata()));
assertTrue(providerMetadataList.contains(new JcloudsTestComputeProviderMetadata()));
assertTrue(providerMetadataList.contains(new JcloudsTestYetAnotherComputeProviderMetadata()));
verify(bundle);
}
@Test
public void testGetApiMetadata() throws Exception {
MetadataBundleListener listener = new MetadataBundleListener();
Bundle bundle = createMock(Bundle.class);
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();
replay(bundle);
List<ApiMetadata> apiMetadataList = listener.getApiMetadata(bundle);
assertNotNull(apiMetadataList);
assertEquals(3, apiMetadataList.size());
assertTrue(apiMetadataList.contains(new JcloudsTestBlobStoreApiMetadata()));
assertTrue(apiMetadataList.contains(new JcloudsTestComputeApiMetadata()));
assertTrue(apiMetadataList.contains(new JcloudsTestYetAnotherComputeApiMetadata()));
verify(bundle);
}
@Test
public void testGetApiMetadataFromMultipleClassLoaders() throws Exception {
ClassLoader isolatedClassLoader = createIsolatedClassLoader();
MetadataBundleListener listener = new MetadataBundleListener();
Bundle bundle = createMock(Bundle.class);
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(isolatedClassLoader.loadClass(JcloudsTestBlobStoreApiMetadata.class.getName())).anyTimes();
expect(bundle.loadClass("org.jclouds.apis.JcloudsTestComputeApiMetadata")).andReturn(JcloudsTestComputeApiMetadata.class).anyTimes();
expect(bundle.loadClass("org.jclouds.apis.JcloudsTestYetAnotherComputeApiMetadata")).andReturn(JcloudsTestYetAnotherComputeApiMetadata.class).anyTimes();
replay(bundle);
List<ApiMetadata> apiMetadataList = listener.getApiMetadata(bundle);
assertNotNull(apiMetadataList);
assertEquals(2, apiMetadataList.size());
assertFalse(apiMetadataList.contains(new JcloudsTestBlobStoreApiMetadata()));
assertTrue(apiMetadataList.contains(new JcloudsTestComputeApiMetadata()));
assertTrue(apiMetadataList.contains(new JcloudsTestYetAnotherComputeApiMetadata()));
verify(bundle);
}
/**
* Creates a different {@link ClassLoader}.
*
* @return
*/
private ClassLoader createIsolatedClassLoader() throws MalformedURLException {
URLClassLoader testClassLoader = (URLClassLoader) getClass().getClassLoader();
URL[] urls = testClassLoader.getURLs();
URLClassLoader loader = new URLClassLoader(urls, null);
return loader;
}
}