/** | |
* 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.InvocationTargetException; | |
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.Optional; | |
import java.util.Set; | |
import java.util.concurrent.ConcurrentHashMap; | |
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.BundleRequirement; | |
import org.osgi.framework.wiring.BundleRevision; | |
import org.osgi.framework.wiring.BundleWire; | |
import org.osgi.framework.wiring.BundleWiring; | |
import aQute.bnd.header.Parameters; | |
public class ClientWeavingHookGenericCapabilityTest { | |
DynamicWeavingActivator activator; | |
@Before | |
public void setUp() { | |
activator = new DynamicWeavingActivator(); | |
BaseActivator.activator = activator; | |
} | |
@After | |
public void tearDown() { | |
BaseActivator.activator = null; | |
activator = null; | |
} | |
@Test | |
public void testAutoConsumerSystemProperty() throws Exception { | |
Dictionary<String, String> consumerHeaders = new Hashtable<String, String>(); | |
// 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.setAutoConsumerInstructions(Optional.of(new Parameters(consumerBundle.getSymbolicName()))); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY); | |
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 testAutoConsumerSystemProperty_negative() throws Exception { | |
Dictionary<String, String> consumerHeaders = new Hashtable<String, String>(); | |
// 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.REQUIRE_CAPABILITY); | |
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(0, wc.getDynamicImports().size()); | |
// 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.emptySet(), result); | |
} | |
@Test | |
public void testBasicServiceLoaderUsage() throws Exception { | |
Dictionary<String, String> consumerHeaders = new Hashtable<String, String>(); | |
consumerHeaders.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT); | |
// 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.REQUIRE_CAPABILITY); | |
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 testHeadersFromFragment() throws Exception { | |
// 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>()); | |
Dictionary<String, String> fragmentConsumerHeaders = new Hashtable<String, String>(); | |
fragmentConsumerHeaders.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT); | |
Bundle fragment = EasyMock.createMock(Bundle.class); | |
EasyMock.expect(fragment.getHeaders()).andReturn(fragmentConsumerHeaders).anyTimes(); | |
EasyMock.replay(fragment); | |
BundleRevision frev = EasyMock.createMock(BundleRevision.class); | |
EasyMock.expect(frev.getBundle()).andReturn(fragment).anyTimes(); | |
EasyMock.replay(frev); | |
BundleRequirement req = EasyMock.createMock(BundleRequirement.class); | |
EasyMock.expect(req.getRevision()).andReturn(frev).anyTimes(); | |
EasyMock.replay(req); | |
BundleWire wire = EasyMock.createMock(BundleWire.class); | |
EasyMock.expect(wire.getRequirement()).andReturn(req).anyTimes(); | |
EasyMock.replay(wire); | |
List<BundleWire> wires = Collections.singletonList(wire); | |
BundleWiring wiring = EasyMock.createMock(BundleWiring.class); | |
EasyMock.expect(wiring.getProvidedWires("osgi.wiring.host")).andReturn(wires).anyTimes(); | |
EasyMock.replay(wiring); | |
BundleRevision rev = EasyMock.createMock(BundleRevision.class); | |
EasyMock.expect(rev.getWiring()).andReturn(wiring).anyTimes(); | |
EasyMock.replay(rev); | |
Bundle consumerBundle = mockConsumerBundle(new Hashtable<String, String>(), rev, providerBundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY); | |
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 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.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT); | |
// 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.REQUIRE_CAPABILITY); | |
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() throws Exception { | |
ClassLoader cl = new URLClassLoader(new URL [] {}); | |
Thread.currentThread().setContextClassLoader(cl); | |
Assert.assertSame("Precondition", cl, Thread.currentThread().getContextClassLoader()); | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT); | |
Bundle providerBundle5 = mockProviderBundle("impl5", 1); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle5, new HashMap<String, Object>()); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle5); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY); | |
Bundle spiFlyBundle = mockSpiFlyBundle(consumerBundle,providerBundle5); | |
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); | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
// Invoke the woven class, check that it properly set the TCCL so that the implementation of impl5 is called. | |
// That implementation throws an exception, after which we are making sure that the TCCL is set back appropriately. | |
try { | |
method.invoke(cls.newInstance(), "hello"); | |
Assert.fail("Invocation should have thrown an exception"); | |
} catch (InvocationTargetException ite) { | |
RuntimeException re = (RuntimeException) ite.getCause(); | |
String msg = re.getMessage(); | |
Assert.assertEquals("Uh-oh: hello", msg); | |
// The TCCL should have been reset correctly | |
Assert.assertSame(cl, Thread.currentThread().getContextClassLoader()); | |
} | |
} | |
@Test | |
public void testAltServiceLoaderLoadUnprocessed() throws Exception { | |
Bundle spiFlyBundle = mockSpiFlyBundle(); | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT); | |
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.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT); | |
Bundle consumerBundle = mockConsumerBundle(headers, spiFlyBundle); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY); | |
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); | |
} | |
/* This is currently not supported in the generic model | |
@Test | |
public void testClientSpecifyingProvider() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT + | |
"; " + SpiFlyConstants.PROVIDER_FILTER_DIRECTIVE + ":=\"(bundle-symbolic-name=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.REQUIRE_CAPABILITY); | |
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"); | |
Assert.assertEquals("Only the services from bundle impl2 should be selected", "HELLO5", result); | |
} | |
@Test | |
public void testClientSpecifyingProviderVersion() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT + | |
"; " + SpiFlyConstants.PROVIDER_FILTER_DIRECTIVE + ":=\"(&(bundle-symbolic-name=impl2)(bundle-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.REQUIRE_CAPABILITY); | |
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", "Updated!hello!Updated", result); | |
} | |
@Test | |
public void testClientMultipleTargetBundles() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT + | |
"; " + SpiFlyConstants.PROVIDER_FILTER_DIRECTIVE + ":=\"(|(bundle-symbolic-name=impl1)(bundle-symbolic-name=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.REQUIRE_CAPABILITY); | |
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"); | |
Assert.assertEquals("All providers should be selected for this one", "ollehimpl4", result); | |
} | |
*/ | |
@Test | |
public void testServiceFiltering() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT + "," + | |
SpiFlyConstants.SERVICELOADER_CAPABILITY_NAMESPACE + | |
"; filter:=\"(osgi.serviceloader=org.apache.aries.mytest.AltSPI)\";"); | |
Bundle providerBundle2 = mockProviderBundle("impl2", 2); | |
Bundle providerBundle4 = mockProviderBundle("impl4", 4); | |
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, providerBundle2, providerBundle4); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY); | |
Bundle spiFlyBundle = mockSpiFlyBundle(consumerBundle, 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. Since MySPI wasn't selected nothing should be returned | |
Class<?> cls = wc.getDefinedClass(); | |
Method method = cls.getMethod("test", new Class [] {String.class}); | |
Object result = method.invoke(cls.newInstance(), "hello"); | |
Assert.assertEquals("No providers should be selected for this one", Collections.emptySet(), 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("All Providers should be selected", (4096L*4096L)-4096L, result2); | |
} | |
@Test | |
public void testServiceFilteringAlternative() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT + "," + | |
SpiFlyConstants.SERVICELOADER_CAPABILITY_NAMESPACE + | |
"; filter:=\"(|(!(osgi.serviceloader=org.apache.aries.mytest.AltSPI))" + | |
"(&(osgi.serviceloader=org.apache.aries.mytest.AltSPI)(bundle-symbolic-name=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>()); | |
HashMap<String, Object> attrs2 = new HashMap<String, Object>(); | |
attrs2.put("bundle-symbolic-name", "impl2"); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, attrs2); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2, attrs2); | |
HashMap<String, Object> attrs4 = new HashMap<String, Object>(); | |
attrs4.put("bundle-symbolic-name", "impl4"); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4, attrs4); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4, attrs4); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle1, providerBundle2, providerBundle4); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY); | |
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", "HELLO", "5", "impl4")); | |
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 testServiceFilteringNarrow() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT + "," + | |
SpiFlyConstants.SERVICELOADER_CAPABILITY_NAMESPACE + | |
"; filter:=\"(&(osgi.serviceloader=org.apache.aries.mytest.AltSPI)(bundle-symbolic-name=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>()); | |
HashMap<String, Object> attrs2 = new HashMap<String, Object>(); | |
attrs2.put("bundle-symbolic-name", "impl2"); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, attrs2); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2, attrs2); | |
HashMap<String, Object> attrs4 = new HashMap<String, Object>(); | |
attrs4.put("bundle-symbolic-name", "impl4"); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4, attrs4); | |
activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4, attrs4); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle1, providerBundle2, providerBundle4); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY); | |
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"); | |
Assert.assertEquals("No providers should be selected here", Collections.emptySet(), 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 testFilteringCustomAttribute() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT + ", " + | |
SpiFlyConstants.SERVICELOADER_CAPABILITY_NAMESPACE + "; filter:=\"(approval=global)\""); | |
Bundle providerBundle1 = mockProviderBundle("impl1", 1); | |
Bundle providerBundle2 = mockProviderBundle("impl2", 2); | |
Map<String, Object> attrs1 = new HashMap<String, Object>(); | |
attrs1.put("approval", "local"); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1, attrs1); | |
Map<String, Object> attrs2 = new HashMap<String, Object>(); | |
attrs2.put("approval", "global"); | |
attrs2.put("other", "attribute"); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2, attrs2); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle1, providerBundle2); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY); | |
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 testVersionedRequirement() throws Exception { | |
Dictionary<String, String> headers = new Hashtable<String, String>(); | |
headers.put( | |
SpiFlyConstants.REQUIRE_CAPABILITY, | |
String.format( | |
"%s;filter:='(&(%s=%s)(version>=1.0)(!(version>=2.0)))',%s;filter:='(%s=%s)'", | |
SpiFlyConstants.EXTENDER_CAPABILITY_NAMESPACE, | |
SpiFlyConstants.EXTENDER_CAPABILITY_NAMESPACE, | |
SpiFlyConstants.PROCESSOR_EXTENDER_NAME, | |
SpiFlyConstants.SERVICELOADER_CAPABILITY_NAMESPACE, | |
SpiFlyConstants.SERVICELOADER_CAPABILITY_NAMESPACE, | |
"org.apache.aries.mytest.MySPI")); | |
Bundle providerBundle1 = mockProviderBundle("impl1", 1); | |
Map<String, Object> attrs1 = new HashMap<String, Object>(); | |
activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1, attrs1); | |
Bundle consumerBundle = mockConsumerBundle(headers, providerBundle1); | |
activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY); | |
Bundle spiFlyBundle = mockSpiFlyBundle(consumerBundle, providerBundle1); | |
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")); | |
Assert.assertEquals(expected, result); | |
} | |
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) { | |
return mockConsumerBundle(headers, null, otherBundles); | |
} | |
private Bundle mockConsumerBundle(Dictionary<String, String> headers, BundleRevision rev, | |
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.getVersion()).andReturn(new Version(1, 2, 3)).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(rev).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; | |
} | |
// 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; | |
} | |
} | |
} |