From f49cca46f0cf5bb1db5ffa20cdb1ce1222f6f566 Mon Sep 17 00:00:00 2001 From: Ioannis Canellos Date: Wed, 31 Oct 2012 11:10:20 +0200 Subject: [PATCH 1/4] 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. --- .../jclouds/osgi/MetadataBundleListener.java | 326 +++++++++--------- .../osgi/MetadataBundleListenerTest.java | 147 ++++++++ 2 files changed, 319 insertions(+), 154 deletions(-) create mode 100644 core/src/test/java/org/jclouds/osgi/MetadataBundleListenerTest.java diff --git a/core/src/main/java/org/jclouds/osgi/MetadataBundleListener.java b/core/src/main/java/org/jclouds/osgi/MetadataBundleListener.java index 32b2a3dac4..2c79713d5c 100644 --- a/core/src/main/java/org/jclouds/osgi/MetadataBundleListener.java +++ b/core/src/main/java/org/jclouds/osgi/MetadataBundleListener.java @@ -19,7 +19,10 @@ 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.ApiRegistry; import org.jclouds.providers.ProviderMetadata; @@ -33,8 +36,8 @@ import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.net.URL; -import java.util.HashMap; -import java.util.Map; +import java.util.Collection; +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 @@ -43,168 +46,183 @@ import java.util.Map; */ public class MetadataBundleListener implements BundleListener { - private Map providerMetadataMap = new HashMap(); - private Map apiMetadataMap = new HashMap(); + private Multimap providerMetadataMap = ArrayListMultimap.create(); + private Multimap apiMetadataMap = ArrayListMultimap.create(); - public void start(BundleContext bundleContext) { - bundleContext.addBundleListener(this); - for (Bundle bundle : bundleContext.getBundles()) { - if (bundle.getState() == Bundle.ACTIVE) { - ProviderMetadata providerMetadata = getProviderMetadata(bundle); - ApiMetadata apiMetadata = getApiMetadata(bundle); + public void start(BundleContext bundleContext) { + bundleContext.addBundleListener(this); + for (Bundle bundle : bundleContext.getBundles()) { + if (bundle.getState() == Bundle.ACTIVE) { + List providerMetadataList = getProviderMetadata(bundle); + List apiMetadataList = getApiMetadata(bundle); - if (providerMetadata != null) { - ProviderRegistry.registerProvider(providerMetadata); - providerMetadataMap.put(bundle.getBundleId(), providerMetadata); - } - if (apiMetadata != null) { - ApiRegistry.registerApi(apiMetadata); - apiMetadataMap.put(bundle.getBundleId(), apiMetadata); - } + 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); + } + } + } } - } - } + } - public void stop(BundleContext bundleContext) { - providerMetadataMap.clear(); - apiMetadataMap.clear(); - } + public void stop(BundleContext bundleContext) { + providerMetadataMap.clear(); + apiMetadataMap.clear(); + } - @Override - public void bundleChanged(BundleEvent event) { - ProviderMetadata providerMetadata; - ApiMetadata apiMetadata; - switch (event.getType()) { - case BundleEvent.STARTED: - providerMetadata = getProviderMetadata(event.getBundle()); - apiMetadata = getApiMetadata(event.getBundle()); - if (providerMetadata != null) { - ProviderRegistry.registerProvider(providerMetadata); - providerMetadataMap.put(event.getBundle().getBundleId(), providerMetadata); - } - if (apiMetadata != null) { - ApiRegistry.registerApi(apiMetadata); - apiMetadataMap.put(event.getBundle().getBundleId(), apiMetadata); - } - break; - case BundleEvent.STOPPING: - case BundleEvent.STOPPED: - providerMetadata = providerMetadataMap.get(event.getBundle().getBundleId()); - apiMetadata = apiMetadataMap.get(event.getBundle().getBundleId()); - if (providerMetadata != null) { - ProviderRegistry.unregisterProvider(providerMetadata); - } - if (apiMetadata != null) { - ApiRegistry.unRegisterApi(apiMetadata); - } - break; - } - } + @Override + public void bundleChanged(BundleEvent event) { + Collection providerMetadataList = null; + Collection apiMetadataList = null; + 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); + } + } + 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); + } + } + break; + } + } + + /** + * Creates an instance of {@link ProviderMetadata} from the {@link Bundle}. + * + * @param bundle + * @return + */ + public List getProviderMetadata(Bundle bundle) { + List metadataList = Lists.newArrayList(); + String classNames = getProviderMetadataClassNames(bundle); + if (classNames != null && !classNames.isEmpty()) { + for (String className : classNames.split("\n")) { + try { + Class 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 getApiMetadata(Bundle bundle) { + List metadataList = Lists.newArrayList(); + String classNames = getApiMetadataClassNames(bundle); + if (classNames != null && !classNames.isEmpty()) { + for (String className : classNames.split("\n")) { + try { + Class 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 { - Class providerMetadataClass = bundle.loadClass(className); - metadata = providerMetadataClass.newInstance(); - } catch (ClassNotFoundException e) { - // ignore - } catch (InstantiationException e) { - // ignore - } 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 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, Charsets.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(); + 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(); + } - } - 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. - * - * @param bundle - * @return - */ - public String getProviderMetadataClassName(Bundle bundle) { - return getMetadataClassName(bundle, "/META-INF/services/org.jclouds.providers.ProviderMetadata"); - } - - /** - * 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"); - } + /** + * Retrieves the {@link ProviderMetadata} class name for the bundle if it exists. + * + * @param bundle + * @return + */ + public String getApiMetadataClassNames(Bundle bundle) { + return getMetadataClassNames(bundle, "/META-INF/services/org.jclouds.apis.ApiMetadata"); + } } \ No newline at end of file diff --git a/core/src/test/java/org/jclouds/osgi/MetadataBundleListenerTest.java b/core/src/test/java/org/jclouds/osgi/MetadataBundleListenerTest.java new file mode 100644 index 0000000000..697877491e --- /dev/null +++ b/core/src/test/java/org/jclouds/osgi/MetadataBundleListenerTest.java @@ -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 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 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 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 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; + } +} From f76b67ac331895f5796fb6b6425c8cf46dd5af5e Mon Sep 17 00:00:00 2001 From: Ioannis Canellos Date: Wed, 31 Oct 2012 11:10:57 +0200 Subject: [PATCH 2/4] Specified a wider version range for rocoto. --- core/pom.xml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/core/pom.xml b/core/pom.xml index 45c91918b9..be19d53667 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -39,7 +39,10 @@ - * + + org.nnsoft.guice.rocoto*;version="[6.1,7)", + * + org.jclouds*;version=${project.version};-noimport:=true org.jclouds.osgi.Activator From 0932195d33226d2381e328c632a566034ff783b8 Mon Sep 17 00:00:00 2001 From: Ioannis Canellos Date: Wed, 31 Oct 2012 11:11:12 +0200 Subject: [PATCH 3/4] Specified a wider version range for netty. --- drivers/netty/pom.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/netty/pom.xml b/drivers/netty/pom.xml index eae81f6158..b52819dccd 100644 --- a/drivers/netty/pom.xml +++ b/drivers/netty/pom.xml @@ -35,7 +35,11 @@ org.jclouds.netty*;version="${project.version}" - org.jclouds*;version="${project.version}",* + + org.jclouds*;version="${project.version}", + org.jboss.netty*;version="[3.3,4)", + * + From af7f14e76e695177a81f5fc1f23e786a56b2bbc6 Mon Sep 17 00:00:00 2001 From: Ioannis Canellos Date: Wed, 31 Oct 2012 11:11:27 +0200 Subject: [PATCH 4/4] Specified a wider version range for slf4j . --- drivers/slf4j/pom.xml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/drivers/slf4j/pom.xml b/drivers/slf4j/pom.xml index 91db097d8d..9204a6d758 100644 --- a/drivers/slf4j/pom.xml +++ b/drivers/slf4j/pom.xml @@ -35,7 +35,11 @@ org.jclouds.logging.slf4j*;version="${project.version}" - org.jclouds*;version="${project.version}",* + + org.jclouds*;version="${project.version}", + org.slf4j*;version="[1.5,2)", + * +