/** | |
* Licensed to the Apache Software Foundation (ASF) under one | |
* or more contributor license agreements. See the NOTICE file | |
* distributed with this work for additional information | |
* regarding copyright ownership. The ASF 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.apache.aries.spifly.dynamic; | |
import java.io.BufferedReader; | |
import java.io.File; | |
import java.io.FileReader; | |
import java.io.IOException; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.Method; | |
import java.net.URL; | |
import java.net.URLClassLoader; | |
import java.security.ProtectionDomain; | |
import java.util.ArrayList; | |
import java.util.Arrays; | |
import java.util.Collection; | |
import java.util.Collections; | |
import java.util.Dictionary; | |
import java.util.Enumeration; | |
import java.util.HashMap; | |
import java.util.HashSet; | |
import java.util.Hashtable; | |
import java.util.List; | |
import java.util.Map; | |
import java.util.Set; | |
import java.util.concurrent.ConcurrentHashMap; | |
import javax.xml.parsers.DocumentBuilderFactory; | |
import org.apache.aries.mytest.MySPI; | |
import org.apache.aries.spifly.BaseActivator; | |
import org.apache.aries.spifly.SpiFlyConstants; | |
import org.apache.aries.spifly.Streams; | |
import org.easymock.EasyMock; | |
import org.easymock.IAnswer; | |
import org.junit.After; | |
import org.junit.Assert; | |
import org.junit.Before; | |
import org.junit.Test; | |
import org.osgi.framework.Bundle; | |
import org.osgi.framework.BundleContext; | |
import org.osgi.framework.BundleReference; | |
import org.osgi.framework.Version; | |
import org.osgi.framework.hooks.weaving.WeavingHook; | |
import org.osgi.framework.hooks.weaving.WovenClass; | |
import org.osgi.framework.wiring.BundleRevision; | |
import org.osgi.framework.wiring.BundleWiring; | |
public class ClientWeavingHookTest { | |
DynamicWeavingActivator activator; | |
private static final String thisJVMsDBF = DocumentBuilderFactory.newInstance().getClass().getName(); | |
@Before | |
public void setUp() { | |
activator = new DynamicWeavingActivator(); | |
BaseActivator.activator = activator; | |
} | |
@After | |
public void tearDown() { | |
BaseActivator.activator = null; | |
activator = null; | |
} | |
@Test | |
public void testBasicServiceLoaderUsage() throws Exception { | |
Dictionary<String, String> consumerHeaders = new Hashtable<String, String>(); | |
consumerHeaders.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "*"); | |
// Register the bundle that provides the SPI implementation. | |
Bundle providerBundle = mockProviderBundle("impl1", 1); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(consumerHeaders, providerBundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle("spifly", Version.parseVersion("1.9.4"), consumerBundle, providerBundle); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
Assert.assertNotNull("Precondition", clsUrl); | |
String clientClassName = "org.apache.aries.spifly.dynamic.TestClient"; | |
WovenClass wc = new MyWovenClass(clsUrl, clientClassName, consumerBundle); | |
Assert.assertEquals("Precondition", 0, wc.getDynamicImports().size()); | |
wh.weave(wc); | |
Assert.assertEquals(1, wc.getDynamicImports().size()); | |
String di1 = "org.apache.aries.spifly"; | |
String di = wc.getDynamicImports().get(0); | |
Assert.assertTrue("Weaving should have added a dynamic import", di1.equals(di)); | |
// Invoke the woven class and check that it properly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl1 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Assert.assertEquals(Collections.singleton("olleh"), result); | |
} | |
@Test | |
public void test_ARIES_1755_ServiceLoaderUsage() throws Exception { | |
Dictionary<String, String> consumerHeaders = new Hashtable<String, String>(); | |
consumerHeaders.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "*"); | |
// Register the bundle that provides the SPI implementation. | |
Bundle providerBundle = mockProviderBundle("impl1", 1); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(consumerHeaders, providerBundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle("spifly", Version.parseVersion("1.9.4"), consumerBundle, providerBundle); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient3$EnumLoader.class"); | |
Assert.assertNotNull("Precondition", clsUrl); | |
String clientClassName = "org.apache.aries.spifly.dynamic.TestClient3$EnumLoader"; | |
WovenClass wc = new MyWovenClass(clsUrl, clientClassName, consumerBundle); | |
Assert.assertEquals("Precondition", 0, wc.getDynamicImports().size()); | |
wh.weave(wc); | |
Assert.assertEquals(1, wc.getDynamicImports().size()); | |
String di1 = "org.apache.aries.spifly"; | |
String di = wc.getDynamicImports().get(0); | |
Assert.assertTrue("Weaving should have added a dynamic import", di1.equals(di)); | |
// Invoke the woven class and check that it properly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl1 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("load", new Class [] {Class.class}); | |
@SuppressWarnings({ "unchecked", "rawtypes" }) | |
Object result = method.invoke(Enum.valueOf((Class<? extends Enum>)cls, "INSTANCE"), MySPI.class); | |
Assert.assertTrue("Either null or not a MySPI", result instanceof MySPI); | |
MySPI instance = (MySPI)result; | |
String someMethod = instance.someMethod("Hello"); | |
Assert.assertEquals("olleH", someMethod); | |
} | |
@Test | |
public void testBasicServiceLoaderUsage2() throws Exception { | |
Dictionary<String, String> consumerHeaders = new Hashtable<String, String>(); | |
consumerHeaders.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "*"); | |
// Register the bundle that provides the SPI implementation. | |
Bundle providerBundle = mockProviderBundle("impl1", 1); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(consumerHeaders, providerBundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle("spifly", Version.parseVersion("1.9.4"), consumerBundle, providerBundle); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
Assert.assertNotNull("Precondition", clsUrl); | |
String clientClassName = "org.apache.aries.spifly.dynamic.TestClient"; | |
WovenClass wc = new MyWovenClass(clsUrl, clientClassName, consumerBundle); | |
Assert.assertEquals("Precondition", 0, wc.getDynamicImports().size()); | |
wh.weave(wc); | |
Assert.assertEquals(1, wc.getDynamicImports().size()); | |
String di1 = "org.apache.aries.spifly"; | |
String di = wc.getDynamicImports().get(0); | |
Assert.assertTrue("Weaving should have added a dynamic import", di1.equals(di)); | |
// Invoke the woven class and check that it properly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl1 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("testService2", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Assert.assertEquals(Collections.singleton("olleh"), result); | |
} | |
@Test | |
public void testBasicServiceLoaderUsage3() throws Exception { | |
Dictionary<String, String> consumerHeaders = new Hashtable<String, String>(); | |
consumerHeaders.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "*"); | |
// Register the bundle that provides the SPI implementation. | |
Bundle providerBundle = mockProviderBundle("impl1", 1); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(consumerHeaders, providerBundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle("spifly", Version.parseVersion("1.9.4"), consumerBundle, providerBundle); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient2.class"); | |
Assert.assertNotNull("Precondition", clsUrl); | |
String clientClassName = "org.apache.aries.spifly.dynamic.TestClient2"; | |
WovenClass wc = new MyWovenClass(clsUrl, clientClassName, consumerBundle); | |
Assert.assertEquals("Precondition", 0, wc.getDynamicImports().size()); | |
wh.weave(wc); | |
Assert.assertEquals(1, wc.getDynamicImports().size()); | |
String di1 = "org.apache.aries.spifly"; | |
String di = wc.getDynamicImports().get(0); | |
Assert.assertTrue("Weaving should have added a dynamic import", di1.equals(di)); | |
// Invoke the woven class and check that it properly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl1 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Assert.assertEquals(Collections.singleton("olleh"), result); | |
} | |
@Test | |
public void testTCCLResetting() throws Exception { | |
ClassLoader cl = new URLClassLoader(new URL [] {}); | |
Thread.currentThread().setContextClassLoader(cl); | |
Assert.assertSame("Precondition", cl, Thread.currentThread().getContextClassLoader()); | |
Dictionary<String, String> consumerHeaders = new Hashtable<String, String>(); | |
consumerHeaders.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "*"); | |
// Register the bundle that provides the SPI implementation. | |
Bundle providerBundle = mockProviderBundle("impl1", 1); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(consumerHeaders, providerBundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle("spifly", Version.parseVersion("1.9.4"), consumerBundle, providerBundle); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
Assert.assertNotNull("Precondition", clsUrl); | |
String clientClassName = "org.apache.aries.spifly.dynamic.TestClient"; | |
WovenClass wc = new MyWovenClass(clsUrl, clientClassName, consumerBundle); | |
Assert.assertEquals("Precondition", 0, wc.getDynamicImports().size()); | |
wh.weave(wc); | |
Assert.assertEquals(1, wc.getDynamicImports().size()); | |
String di1 = "org.apache.aries.spifly"; | |
String di = wc.getDynamicImports().get(0); | |
Assert.assertTrue("Weaving should have added a dynamic import", di1.equals(di)); | |
// Invoke the woven class and check that it properly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl1 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
method.invoke(cls.newInstance(), "hi there"); | |
Assert.assertSame(cl, Thread.currentThread().getContextClassLoader()); | |
} | |
@Test | |
public void testTCCLResettingOnException() { | |
// TODO | |
} | |
@Test | |
public void testAltServiceLoaderLoadUnprocessed() throws Exception { | |
Bundle spiFlyBundle = mockSpiFlyBundle(); | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "*"); | |
Bundle consumerBundle = mockConsumerBundle(headers, spiFlyBundle); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("UnaffectedTestClient.class"); | |
Assert.assertNotNull("Precondition", clsUrl); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.UnaffectedTestClient", consumerBundle); | |
Assert.assertEquals("Precondition", 0, wc.getDynamicImports().size()); | |
wh.weave(wc); | |
Assert.assertEquals("The client is not affected so no additional imports should have been added", | |
0, wc.getDynamicImports().size()); | |
// ok the weaving is done, now prepare the registry for the call | |
Bundle providerBundle = mockProviderBundle("impl1", 1); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle, new HashMap<String, Object>()); | |
// Invoke the woven class and check that it propertly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl1 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Assert.assertEquals("impl4", result); | |
} | |
@Test | |
public void testMultipleProviders() throws Exception { | |
Bundle spiFlyBundle = mockSpiFlyBundle(); | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "*"); | |
Bundle consumerBundle = mockConsumerBundle(headers, spiFlyBundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.TestClient", consumerBundle); | |
wh.weave(wc); | |
Bundle providerBundle1 = mockProviderBundle("impl1", 1); | |
Bundle providerBundle2 = mockProviderBundle("impl2", 2); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, new HashMap<String, Object>()); | |
// Invoke the woven class and check that it propertly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI files from impl1 and impl2 are visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Set<String> expected = new HashSet<String>(Arrays.asList("olleh", "HELLO", "5")); | |
Assert.assertEquals("All three services should be invoked", expected, result); | |
} | |
@Test | |
public void testClientSpecifyingProvider() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "java.util.ServiceLoader#load(java.lang.Class);bundle=impl2"); | |
Bundle providerBundle1 = mockProviderBundle("impl1", 1); | |
Bundle providerBundle2 = mockProviderBundle("impl2", 2); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle1, providerBundle2); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle(consumerBundle, providerBundle1, providerBundle2); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.TestClient", consumerBundle); | |
wh.weave(wc); | |
// Invoke the woven class and check that it propertly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl2 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Set<String> expected = new HashSet<String>(Arrays.asList("HELLO", "5")); | |
Assert.assertEquals("Only the services from bundle impl2 should be selected", expected, result); | |
} | |
@Test | |
public void testClientSpecifyingProviderVersion() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "java.util.ServiceLoader#load(java.lang.Class);bundle=impl2:version=1.2.3"); | |
Bundle providerBundle1 = mockProviderBundle("impl1", 1); | |
Bundle providerBundle2 = mockProviderBundle("impl2", 2); | |
Bundle providerBundle3 = mockProviderBundle("impl2_123", 3, new Version(1, 2, 3)); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle3, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle1, providerBundle2, providerBundle3); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle(consumerBundle, providerBundle1, providerBundle2, providerBundle3); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.TestClient", consumerBundle); | |
wh.weave(wc); | |
// Invoke the woven class and check that it propertly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl2 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Assert.assertEquals("Only the services from bundle impl2 should be selected", Collections.singleton("Updated!hello!Updated"), result); | |
} | |
@Test | |
public void testClientMultipleTargetBundles() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, | |
"java.util.ServiceLoader#load(java.lang.Class);bundle=impl1|impl4"); | |
Bundle providerBundle1 = mockProviderBundle("impl1", 1); | |
Bundle providerBundle2 = mockProviderBundle("impl2", 2); | |
Bundle providerBundle4 = mockProviderBundle("impl4", 4); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle1, providerBundle2, providerBundle4); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle(consumerBundle, providerBundle1, providerBundle2, providerBundle4); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.TestClient", consumerBundle); | |
wh.weave(wc); | |
// Invoke the woven class and check that it propertly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl2 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Set<String> expected = new HashSet<String>(Arrays.asList("olleh", "impl4")); | |
Assert.assertEquals("All providers should be selected for this one", expected, result); | |
} | |
@Test | |
public void testClientMultipleTargetBundles2() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, | |
"java.util.ServiceLoader#load(java.lang.Class);bundleId=1|4"); | |
Bundle providerBundle1 = mockProviderBundle("impl1", 1); | |
Bundle providerBundle2 = mockProviderBundle("impl2", 2); | |
Bundle providerBundle4 = mockProviderBundle("impl4", 4); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle1, providerBundle2, providerBundle4); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle(consumerBundle, providerBundle1, providerBundle2, providerBundle4); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.TestClient", consumerBundle); | |
wh.weave(wc); | |
// Invoke the woven class and check that it propertly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl2 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Set<String> expected = new HashSet<String>(Arrays.asList("olleh", "impl4")); | |
Assert.assertEquals("All providers should be selected for this one", expected, result); | |
} | |
@Test | |
public void testClientSpecificProviderLoadArgument() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, | |
"java.util.ServiceLoader#load(java.lang.Class[org.apache.aries.mytest.MySPI])," + | |
"java.util.ServiceLoader#load(java.lang.Class[org.apache.aries.mytest.AltSPI]);bundle=impl4"); | |
Bundle providerBundle1 = mockProviderBundle("impl1", 1); | |
Bundle providerBundle2 = mockProviderBundle("impl2", 2); | |
Bundle providerBundle4 = mockProviderBundle("impl4", 4); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle1, providerBundle2, providerBundle4); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle(consumerBundle, providerBundle1, providerBundle2, providerBundle4); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.TestClient", consumerBundle); | |
wh.weave(wc); | |
// Invoke the woven class and check that it propertly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl2 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Set<String> expected = new HashSet<String>(Arrays.asList("olleh", "impl4", "HELLO", "5")); | |
Assert.assertEquals("All providers should be selected for this one", expected, result); | |
// Weave the AltTestClient class. | |
URL cls2Url = getClass().getResource("AltTestClient.class"); | |
WovenClass wc2 = new MyWovenClass(cls2Url, "org.apache.aries.spifly.dynamic.AltTestClient", consumerBundle); | |
wh.weave(wc2); | |
// Invoke the AltTestClient | |
Class<?> cls2 = wc2.getDefinedClass(); | |
Method method2 = cls2.getMethod("test", new Class [] {long.class}); | |
Object result2 = method2.invoke(cls2.newInstance(), 4096); | |
Assert.assertEquals("Only the services from bundle impl4 should be selected", -4096L, result2); | |
} | |
@Test | |
public void testClientSpecifyingDifferentMethodsLimitedToDifferentProviders() throws Exception { | |
Dictionary<String, String> headers1 = new Hashtable<String, String>(); | |
headers1.put(SpiFlyConstants.SPI_CONSUMER_HEADER, | |
"javax.xml.parsers.DocumentBuilderFactory#newInstance();bundle=impl3," + | |
"java.util.ServiceLoader#load(java.lang.Class[org.apache.aries.mytest.MySPI]);bundle=impl4"); | |
Dictionary<String, String> headers2 = new Hashtable<String, String>(); | |
headers2.put(SpiFlyConstants.SPI_CONSUMER_HEADER, | |
"javax.xml.parsers.DocumentBuilderFactory#newInstance();bundle=system.bundle," + | |
"java.util.ServiceLoader#load;bundle=impl1"); | |
Dictionary<String, String> headers3 = new Hashtable<String, String>(); | |
headers3.put(SpiFlyConstants.SPI_CONSUMER_HEADER, | |
"org.acme.blah#someMethod();bundle=mybundle"); | |
Bundle providerBundle1 = mockProviderBundle("impl1", 1); | |
Bundle providerBundle2 = mockProviderBundle("impl2", 2); | |
Bundle providerBundle3 = mockProviderBundle("impl3", 3); | |
Bundle providerBundle4 = mockProviderBundle("impl4", 4); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2, new HashMap<String, Object>()); | |
activator.registerProviderBundle("javax.xml.parsers.DocumentBuilderFactory", providerBundle3, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4, new HashMap<String, Object>()); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4, new HashMap<String, Object>()); | |
Bundle consumerBundle1 = mockConsumerBundle(headers1, providerBundle1, providerBundle2, providerBundle3, providerBundle4); | |
activator.addConsumerWeavingData(consumerBundle1, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle consumerBundle2 = mockConsumerBundle(headers2, providerBundle1, providerBundle2, providerBundle3, providerBundle4); | |
activator.addConsumerWeavingData(consumerBundle2, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle consumerBundle3 = mockConsumerBundle(headers3, providerBundle1, providerBundle2, providerBundle3, providerBundle4); | |
activator.addConsumerWeavingData(consumerBundle3, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
Bundle spiFlyBundle = mockSpiFlyBundle(consumerBundle1, consumerBundle2, consumerBundle3, | |
providerBundle1, providerBundle2, providerBundle3, providerBundle4); | |
WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator); | |
testConsumerBundleWeaving(consumerBundle1, wh, Collections.singleton("impl4"), "org.apache.aries.spifly.dynamic.impl3.MyAltDocumentBuilderFactory"); | |
testConsumerBundleWeaving(consumerBundle2, wh, Collections.singleton("olleh"), thisJVMsDBF); | |
testConsumerBundleWeaving(consumerBundle3, wh, Collections.<String>emptySet(), thisJVMsDBF); | |
testConsumerBundleWeavingNonConst(consumerBundle1, wh, Collections.singleton("impl4"), "org.apache.aries.spifly.dynamic.impl3.MyAltDocumentBuilderFactory"); | |
testConsumerBundleWeavingNonConst(consumerBundle2, wh, Collections.singleton("olleh"), thisJVMsDBF); | |
testConsumerBundleWeavingNonConst(consumerBundle3, wh, Collections.<String>emptySet(), thisJVMsDBF); | |
} | |
private void testConsumerBundleWeaving(Bundle consumerBundle, WeavingHook wh, Set<String> testClientResult, String jaxpClientResult) throws Exception { | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, TestClient.class.getName(), consumerBundle); | |
wh.weave(wc); | |
// Invoke the woven class and check that it propertly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl2 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Assert.assertEquals(testClientResult, result); | |
URL clsUrl2 = getClass().getResource("JaxpClient.class"); | |
WovenClass wc2 = new MyWovenClass(clsUrl2, JaxpClient.class.getName(), consumerBundle); | |
wh.weave(wc2); | |
Class<?> cls2 = wc2.getDefinedClass(); | |
Method method2 = cls2.getMethod("test", new Class [] {}); | |
Class<?> result2 = (Class<?>) method2.invoke(cls2.newInstance()); | |
Assert.assertEquals(jaxpClientResult, result2.getName()); | |
} | |
private void testConsumerBundleWeavingNonConst(Bundle consumerBundle, WeavingHook wh, Set<String> testClientResult, String jaxpClientResult) throws Exception { | |
// Weave the TestClient class. | |
URL clsUrl = getClass().getResource("TestClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, TestClient.class.getName(), consumerBundle); | |
wh.weave(wc); | |
// Invoke the woven class and check that it propertly sets the TCCL so that the | |
// META-INF/services/org.apache.aries.mytest.MySPI file from impl2 is visible. | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("testService", new Class [] {String.class, Class.class}); | |
Object result = method.invoke(cls.newInstance(), "hello", MySPI.class); | |
Assert.assertEquals(testClientResult, result); | |
} | |
@Test | |
public void testJAXPClientWantsJREImplementation1() throws Exception { | |
Bundle systembundle = mockSystemBundle(); | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "javax.xml.parsers.DocumentBuilderFactory#newInstance()"); | |
Bundle consumerBundle = mockConsumerBundle(headers, systembundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
WeavingHook wh = new ClientWeavingHook(mockSpiFlyBundle(consumerBundle, systembundle).getBundleContext(), activator); | |
URL clsUrl = getClass().getResource("JaxpClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.JaxpClient", consumerBundle); | |
wh.weave(wc); | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {}); | |
Class<?> result = (Class<?>) method.invoke(cls.newInstance()); | |
Assert.assertEquals("JAXP implementation from JRE", thisJVMsDBF, result.getName()); | |
} | |
// If there is an alternate implementation it should always be favoured over the JRE one | |
@Test | |
public void testJAXPClientWantsAltImplementation1() throws Exception { | |
Bundle systembundle = mockSystemBundle(); | |
Bundle providerBundle = mockProviderBundle("impl3", 1); | |
activator.registerProviderBundle("javax.xml.parsers.DocumentBuilderFactory", providerBundle, new HashMap<String, Object>()); | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "javax.xml.parsers.DocumentBuilderFactory#newInstance()"); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle, systembundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
WeavingHook wh = new ClientWeavingHook(mockSpiFlyBundle(consumerBundle, providerBundle, systembundle).getBundleContext(), activator); | |
URL clsUrl = getClass().getResource("JaxpClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.JaxpClient", consumerBundle); | |
wh.weave(wc); | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {}); | |
Class<?> result = (Class<?>) method.invoke(cls.newInstance()); | |
Assert.assertEquals("JAXP implementation from JRE", "org.apache.aries.spifly.dynamic.impl3.MyAltDocumentBuilderFactory", result.getName()); | |
} | |
@Test | |
public void testJAXPClientWantsJREImplementation2() throws Exception { | |
Bundle systembundle = mockSystemBundle(); | |
Bundle providerBundle = mockProviderBundle("impl3", 1); | |
activator.registerProviderBundle("javax.xml.parsers.DocumentBuilderFactory", providerBundle, new HashMap<String, Object>()); | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "javax.xml.parsers.DocumentBuilderFactory#newInstance();bundleId=0"); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle, systembundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
WeavingHook wh = new ClientWeavingHook(mockSpiFlyBundle(consumerBundle, providerBundle, systembundle).getBundleContext(), activator); | |
URL clsUrl = getClass().getResource("JaxpClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.JaxpClient", consumerBundle); | |
wh.weave(wc); | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {}); | |
Class<?> result = (Class<?>) method.invoke(cls.newInstance()); | |
Assert.assertEquals("JAXP implementation from JRE", thisJVMsDBF, result.getName()); | |
} | |
@Test | |
public void testJAXPClientWantsAltImplementation2() throws Exception { | |
Bundle systembundle = mockSystemBundle(); | |
Bundle providerBundle = mockProviderBundle("impl3", 1); | |
activator.registerProviderBundle("javax.xml.parsers.DocumentBuilderFactory", providerBundle, new HashMap<String, Object>()); | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.SPI_CONSUMER_HEADER, "javax.xml.parsers.DocumentBuilderFactory#newInstance();bundle=impl3"); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle, systembundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.SPI_CONSUMER_HEADER); | |
WeavingHook wh = new ClientWeavingHook(mockSpiFlyBundle(consumerBundle, providerBundle, systembundle).getBundleContext(), activator); | |
URL clsUrl = getClass().getResource("JaxpClient.class"); | |
WovenClass wc = new MyWovenClass(clsUrl, "org.apache.aries.spifly.dynamic.JaxpClient", consumerBundle); | |
wh.weave(wc); | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {}); | |
Class<?> result = (Class<?>) method.invoke(cls.newInstance()); | |
Assert.assertEquals("JAXP implementation from alternative bundle", "org.apache.aries.spifly.dynamic.impl3.MyAltDocumentBuilderFactory", result.getName()); | |
} | |
private Bundle mockSpiFlyBundle(Bundle ... bundles) throws Exception { | |
return mockSpiFlyBundle("spifly", new Version(1, 0, 0), bundles); | |
} | |
private Bundle mockSpiFlyBundle(String bsn, Version version, Bundle ... bundles) throws Exception { | |
Bundle spiFlyBundle = EasyMock.createMock(Bundle.class); | |
BundleContext spiFlyBundleContext = EasyMock.createMock(BundleContext.class); | |
EasyMock.expect(spiFlyBundleContext.getBundle()).andReturn(spiFlyBundle).anyTimes(); | |
List<Bundle> allBundles = new ArrayList<Bundle>(Arrays.asList(bundles)); | |
allBundles.add(spiFlyBundle); | |
EasyMock.expect(spiFlyBundleContext.getBundles()).andReturn(allBundles.toArray(new Bundle [] {})).anyTimes(); | |
EasyMock.replay(spiFlyBundleContext); | |
EasyMock.expect(spiFlyBundle.getSymbolicName()).andReturn(bsn).anyTimes(); | |
EasyMock.expect(spiFlyBundle.getVersion()).andReturn(version).anyTimes(); | |
EasyMock.expect(spiFlyBundle.getBundleId()).andReturn(Long.MAX_VALUE).anyTimes(); | |
EasyMock.expect(spiFlyBundle.getBundleContext()).andReturn(spiFlyBundleContext).anyTimes(); | |
EasyMock.replay(spiFlyBundle); | |
// Set the bundle context for testing purposes | |
Field bcField = BaseActivator.class.getDeclaredField("bundleContext"); | |
bcField.setAccessible(true); | |
bcField.set(activator, spiFlyBundle.getBundleContext()); | |
return spiFlyBundle; | |
} | |
private Bundle mockProviderBundle(String subdir, long id) throws Exception { | |
return mockProviderBundle(subdir, id, Version.emptyVersion); | |
} | |
private Bundle mockProviderBundle(String subdir, long id, Version version) throws Exception { | |
URL url = getClass().getResource("/" + getClass().getName().replace('.', '/') + ".class"); | |
File classFile = new File(url.getFile()); | |
File baseDir = new File(classFile.getParentFile(), subdir); | |
File directory = new File(baseDir, "/META-INF/services"); | |
final List<String> classNames = new ArrayList<String>(); | |
// Do a directory listing of the applicable META-INF/services directory | |
List<String> resources = new ArrayList<String>(); | |
for (File f : directory.listFiles()) { | |
String fileName = f.getName(); | |
if (fileName.startsWith(".") || fileName.endsWith(".")) | |
continue; | |
classNames.addAll(getClassNames(f)); | |
// Needs to be something like: META-INF/services/org.apache.aries.mytest.MySPI | |
String path = f.getAbsolutePath().substring(baseDir.getAbsolutePath().length()); | |
path = path.replace('\\', '/'); | |
if (path.startsWith("/")) { | |
path = path.substring(1); | |
} | |
resources.add(path); | |
} | |
// Set up the classloader that will be used by the ASM-generated code as the TCCL. | |
// It can load a META-INF/services file | |
@SuppressWarnings("resource") | |
final ClassLoader cl = new TestProviderBundleClassLoader(subdir, resources.toArray(new String [] {})); | |
final List<String> classResources = new ArrayList<String>(); | |
for(String className : classNames) { | |
classResources.add("/" + className.replace('.', '/') + ".class"); | |
} | |
BundleContext bc = EasyMock.createNiceMock(BundleContext.class); | |
EasyMock.replay(bc); | |
Bundle providerBundle = EasyMock.createMock(Bundle.class); | |
String bsn = subdir; | |
int idx = bsn.indexOf('_'); | |
if (idx > 0) { | |
bsn = bsn.substring(0, idx); | |
} | |
EasyMock.expect(providerBundle.getSymbolicName()).andReturn(bsn).anyTimes(); | |
EasyMock.expect(providerBundle.getBundleId()).andReturn(id).anyTimes(); | |
EasyMock.expect(providerBundle.getBundleContext()).andReturn(bc).anyTimes(); | |
EasyMock.expect(providerBundle.getVersion()).andReturn(version).anyTimes(); | |
EasyMock.expect(providerBundle.getEntryPaths("/")).andAnswer(new IAnswer<Enumeration<String>>() { | |
@Override | |
public Enumeration<String> answer() throws Throwable { | |
return Collections.enumeration(classResources); | |
} | |
}).anyTimes(); | |
EasyMock.<Class<?>>expect(providerBundle.loadClass(EasyMock.anyObject(String.class))).andAnswer(new IAnswer<Class<?>>() { | |
@Override | |
public Class<?> answer() throws Throwable { | |
String name = (String) EasyMock.getCurrentArguments()[0]; | |
if (!classNames.contains(name)) { | |
throw new ClassCastException(name); | |
} | |
return cl.loadClass(name); | |
} | |
}).anyTimes(); | |
EasyMock.replay(providerBundle); | |
return providerBundle; | |
} | |
private Collection<String> getClassNames(File f) throws IOException { | |
List<String> names = new ArrayList<String>(); | |
BufferedReader br = new BufferedReader(new FileReader(f)); | |
try { | |
String line = null; | |
while((line = br.readLine()) != null) { | |
if (line.trim().startsWith("#")) { | |
continue; | |
} | |
names.add(line.trim()); | |
} | |
} finally { | |
br.close(); | |
} | |
return names; | |
} | |
private Bundle mockConsumerBundle(Dictionary<String, String> headers, Bundle ... otherBundles) { | |
// Create a mock object for the client bundle which holds the code that uses ServiceLoader.load() | |
// or another SPI invocation. | |
BundleContext bc = EasyMock.createMock(BundleContext.class); | |
Bundle consumerBundle = EasyMock.createMock(Bundle.class); | |
EasyMock.expect(consumerBundle.getSymbolicName()).andReturn("testConsumer").anyTimes(); | |
EasyMock.expect(consumerBundle.getHeaders()).andReturn(headers).anyTimes(); | |
EasyMock.expect(consumerBundle.getBundleContext()).andReturn(bc).anyTimes(); | |
EasyMock.expect(consumerBundle.getBundleId()).andReturn(Long.MAX_VALUE).anyTimes(); | |
EasyMock.expect(consumerBundle.adapt(BundleRevision.class)).andReturn(null).anyTimes(); | |
EasyMock.replay(consumerBundle); | |
List<Bundle> allBundles = new ArrayList<Bundle>(Arrays.asList(otherBundles)); | |
allBundles.add(consumerBundle); | |
EasyMock.expect(bc.getBundles()).andReturn(allBundles.toArray(new Bundle [] {})).anyTimes(); | |
EasyMock.replay(bc); | |
return consumerBundle; | |
} | |
private Bundle mockSystemBundle() { | |
Bundle systemBundle = EasyMock.createMock(Bundle.class); | |
EasyMock.expect(systemBundle.getBundleId()).andReturn(0L).anyTimes(); | |
EasyMock.expect(systemBundle.getSymbolicName()).andReturn("system.bundle").anyTimes(); | |
EasyMock.replay(systemBundle); | |
return systemBundle; | |
} | |
// A classloader that loads anything starting with org.apache.aries.spifly.dynamic.impl1 from it | |
// and the rest from the parent. This is to mimic a bundle that holds a specific SPI implementation. | |
public static class TestProviderBundleClassLoader extends URLClassLoader { | |
private final List<String> resources; | |
private final String prefix; | |
private final String classPrefix; | |
private final Map<String, Class<?>> loadedClasses = new ConcurrentHashMap<String, Class<?>>(); | |
public TestProviderBundleClassLoader(String subdir, String ... resources) { | |
super(new URL [] {}, TestProviderBundleClassLoader.class.getClassLoader()); | |
this.prefix = TestProviderBundleClassLoader.class.getPackage().getName().replace('.', '/') + "/" + subdir + "/"; | |
this.classPrefix = prefix.replace('/', '.'); | |
this.resources = Arrays.asList(resources); | |
} | |
@Override | |
public Class<?> loadClass(String name) throws ClassNotFoundException { | |
if (name.startsWith(classPrefix)) | |
return loadClassLocal(name); | |
return super.loadClass(name); | |
} | |
@Override | |
protected synchronized Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException { | |
if (name.startsWith(classPrefix)) { | |
Class<?> cls = loadClassLocal(name); | |
if (resolve) | |
resolveClass(cls); | |
return cls; | |
} | |
return super.loadClass(name, resolve); | |
} | |
protected Class<?> loadClassLocal(String name) throws ClassNotFoundException { | |
Class<?> prevLoaded = loadedClasses.get(name); | |
if (prevLoaded != null) | |
return prevLoaded; | |
URL res = TestProviderBundleClassLoader.class.getClassLoader().getResource(name.replace('.', '/') + ".class"); | |
try { | |
byte[] bytes = Streams.suck(res.openStream()); | |
Class<?> cls = defineClass(name, bytes, 0, bytes.length); | |
loadedClasses.put(name, cls); | |
return cls; | |
} catch (Exception e) { | |
throw new ClassNotFoundException(name, e); | |
} | |
} | |
@Override | |
public URL findResource(String name) { | |
if (resources.contains(name)) { | |
return getClass().getClassLoader().getResource(prefix + name); | |
} else { | |
return super.findResource(name); | |
} | |
} | |
@Override | |
public Enumeration<URL> findResources(String name) throws IOException { | |
if (resources.contains(name)) { | |
return getClass().getClassLoader().getResources(prefix + name); | |
} else { | |
return super.findResources(name); | |
} | |
} | |
} | |
private static class MyWovenClass implements WovenClass { | |
byte [] bytes; | |
final String className; | |
final Bundle bundleContainingOriginalClass; | |
List<String> dynamicImports = new ArrayList<String>(); | |
boolean weavingComplete = false; | |
private MyWovenClass(URL clazz, String name, Bundle bundle) throws Exception { | |
bytes = Streams.suck(clazz.openStream()); | |
className = name; | |
bundleContainingOriginalClass = bundle; | |
} | |
@Override | |
public byte[] getBytes() { | |
return bytes; | |
} | |
@Override | |
public void setBytes(byte[] newBytes) { | |
bytes = newBytes; | |
} | |
@Override | |
public List<String> getDynamicImports() { | |
return dynamicImports; | |
} | |
@Override | |
public boolean isWeavingComplete() { | |
return weavingComplete; | |
} | |
@Override | |
public String getClassName() { | |
return className; | |
} | |
@Override | |
public ProtectionDomain getProtectionDomain() { | |
return null; | |
} | |
@Override | |
public Class<?> getDefinedClass() { | |
try { | |
weavingComplete = true; | |
return new MyWovenClassClassLoader(className, getBytes(), getClass().getClassLoader(), bundleContainingOriginalClass).loadClass(className); | |
} catch (ClassNotFoundException e) { | |
e.printStackTrace(); | |
return null; | |
} | |
} | |
@Override | |
public BundleWiring getBundleWiring() { | |
BundleWiring bw = EasyMock.createMock(BundleWiring.class); | |
EasyMock.expect(bw.getBundle()).andReturn(bundleContainingOriginalClass); | |
EasyMock.expect(bw.getClassLoader()).andReturn(getClass().getClassLoader()); | |
EasyMock.replay(bw); | |
return bw; | |
} | |
} | |
private static class MyWovenClassClassLoader extends ClassLoader implements BundleReference { | |
private final String className; | |
private final Bundle bundle; | |
private final byte [] bytes; | |
private Class<?> wovenClass; | |
public MyWovenClassClassLoader(String className, byte[] bytes, ClassLoader parent, Bundle bundle) { | |
super(parent); | |
this.className = className; | |
this.bundle = bundle; | |
this.bytes = bytes; | |
} | |
@Override | |
protected synchronized Class<?> loadClass(String name, boolean resolve) | |
throws ClassNotFoundException { | |
if (name.equals(className)) { | |
if (wovenClass == null) | |
wovenClass = defineClass(className, bytes, 0, bytes.length); | |
return wovenClass; | |
} else { | |
return super.loadClass(name, resolve); | |
} | |
} | |
@Override | |
public Class<?> loadClass(String name) throws ClassNotFoundException { | |
return loadClass(name, false); | |
} | |
@Override | |
public Bundle getBundle() { | |
return bundle; | |
} | |
} | |
} |