You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@aries.apache.org by da...@apache.org on 2011/11/22 20:02:52 UTC
svn commit: r1205128 - in /aries/trunk/spi-fly: spi-fly-core/
spi-fly-core/src/main/java/org/apache/aries/spifly/
spi-fly-core/src/test/java/org/apache/aries/spifly/ spi-fly-dynamic-bundle/
spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/d...
Author: davidb
Date: Tue Nov 22 19:02:48 2011
New Revision: 1205128
URL: http://svn.apache.org/viewvc?rev=1205128&view=rev
Log:
First pass at support for Generic Capabilities and Generic Requirements to opt in to SPI Fly support. This is consistent with the latest RFC 167. The following headers are supported:
Provide-Capability: osgi.spi.provider; effective:=active
this is identical to the old SPI-Provider: * header
Require-Capability: osgi.spi.provider; effective:=active
this is identical to the old SPI-Consumer: * header.
The SPI-Provider and SPI-Consumer headers will continue to be supported by this component but are not part of the OSGi Specification any more.
Added:
aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java (contents, props changed)
- copied, changed from r1203707, aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java
aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java (with props)
Removed:
aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java
Modified:
aries/trunk/spi-fly/spi-fly-core/ (props changed)
aries/trunk/spi-fly/spi-fly-core/pom.xml
aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ (props changed)
aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java
aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java
aries/trunk/spi-fly/spi-fly-dynamic-bundle/ (props changed)
aries/trunk/spi-fly/spi-fly-dynamic-bundle/pom.xml
aries/trunk/spi-fly/spi-fly-examples/ (props changed)
aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client1-bundle/ (props changed)
aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client1-jar/ (props changed)
aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client2-bundle/ (props changed)
aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client2-bundle/pom.xml
aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider1-bundle/ (props changed)
aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider1-jar/ (props changed)
aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider2-bundle/ (props changed)
aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider2-bundle/pom.xml
aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-spi-bundle/ (props changed)
aries/trunk/spi-fly/spi-fly-static-bundle/ (props changed)
aries/trunk/spi-fly/spi-fly-static-tool/ (props changed)
aries/trunk/spi-fly/spi-fly-static-tool/src/main/java/org/apache/aries/spifly/statictool/Main.java
aries/trunk/spi-fly/spi-fly-weaver/ (props changed)
Propchange: aries/trunk/spi-fly/spi-fly-core/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Modified: aries/trunk/spi-fly/spi-fly-core/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/pom.xml?rev=1205128&r1=1205127&r2=1205128&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/pom.xml (original)
+++ aries/trunk/spi-fly/spi-fly-core/pom.xml Tue Nov 22 19:02:48 2011
@@ -49,6 +49,12 @@ JRE through META-INF/services resources)
</dependency>
<dependency>
+ <groupId>org.apache.aries</groupId>
+ <artifactId>org.apache.aries.util</artifactId>
+ <version>0.4-SNAPSHOT</version>
+ </dependency>
+
+ <dependency>
<groupId>org.easymock</groupId>
<artifactId>easymock</artifactId>
<version>3.0</version>
Propchange: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -0,0 +1,5 @@
+.project
+
+.settings
+
+ConsumerHeaderProcessorNew.java
Modified: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java?rev=1205128&r1=1205127&r2=1205128&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java (original)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java Tue Nov 22 19:02:48 2011
@@ -91,7 +91,7 @@ public abstract class BaseActivator impl
Object consumerHeader = bundle.getHeaders().get(consumerHeaderName);
if (consumerHeader instanceof String) {
- Set<WeavingData> wd = ConsumerHeaderProcessor.processHeader((String) consumerHeader);
+ Set<WeavingData> wd = ConsumerHeaderProcessor.processHeader(consumerHeaderName, (String) consumerHeader);
bundleWeavingData.put(bundle, Collections.unmodifiableSet(wd));
for (WeavingData w : wd) {
Modified: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java?rev=1205128&r1=1205127&r2=1205128&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java (original)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerHeaderProcessor.java Tue Nov 22 19:02:48 2011
@@ -19,12 +19,16 @@
package org.apache.aries.spifly;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.ServiceLoader;
import java.util.Set;
import org.apache.aries.spifly.HeaderParser.PathElement;
+import org.apache.aries.spifly.api.SpiFlyConstants;
+import org.apache.aries.util.manifest.ManifestHeaderProcessor;
+import org.apache.aries.util.manifest.ManifestHeaderProcessor.GenericMetadata;
import org.osgi.framework.Version;
public class ConsumerHeaderProcessor {
@@ -48,15 +52,19 @@ public class ConsumerHeaderProcessor {
* is a list of bundle identifiers separated by a '|' sign. The bundle identifier starts with the Symbolic name
* and can optionally contain a version suffix. E.g. bundle=impl2:version=1.2.3 or bundle=impl2|impl4.
* <li><tt>bundleId</tt> - restrict wiring to the bundle with the specified bundle ID. Typically used when
- * the service should be forceably picked up from the system bundle (<tt>bundleId=0</tt>). Multiple bundle IDs
+ * the service should be forcibly picked up from the system bundle (<tt>bundleId=0</tt>). Multiple bundle IDs
* can be specified separated by a '|' sign.
* </ul>
*
- * @param consumerBundle the consuming bundle.
+ * @param consumerHeaderName the name of the header (either Require-Capability or SPI-Consumer)
* @param consumerHeader the <tt>SPI-Consumer</tt> header.
* @return an instance of the {@link WeavingData} class.
*/
- public static Set<WeavingData> processHeader(String consumerHeader) {
+ public static Set<WeavingData> processHeader(String consumerHeaderName, String consumerHeader) {
+ if (SpiFlyConstants.REQUIRE_CAPABILITY.equals(consumerHeaderName)) {
+ return processRequireCapabilityHeader(consumerHeader);
+ }
+
Set<WeavingData> weavingData = new HashSet<WeavingData>();
for (PathElement element : HeaderParser.parseHeader(consumerHeader)) {
@@ -116,11 +124,7 @@ public class ConsumerHeaderProcessor {
throw new IllegalArgumentException("Must at least specify class name and method name: " + name);
}
}
- ConsumerRestriction restriction = new ConsumerRestriction(className, methodRestriction);
- // TODO is this correct? Why is it added to a set?
- Set<ConsumerRestriction> restrictions = new HashSet<ConsumerRestriction>();
- restrictions.add(restriction);
String bsn = element.getAttribute("bundle");
if (bsn != null) {
@@ -154,13 +158,46 @@ public class ConsumerHeaderProcessor {
}
}
- // TODO this can be done in the WeavingData itself?
- String[] argClasses = restriction.getMethodRestriction(methodName).getArgClasses();
+ weavingData.add(createWeavingData(className, methodName, methodRestriction, allowedBundles));
+ }
+ return weavingData;
+ }
+
+ private static Set<WeavingData> processRequireCapabilityHeader(String consumerHeader) {
+ Set<WeavingData> weavingData = new HashSet<WeavingData>();
+
+ List<GenericMetadata> requirements = ManifestHeaderProcessor.parseRequirementString(consumerHeader);
+ for (GenericMetadata req : requirements) {
+ if (SpiFlyConstants.SPI_CAPABILITY_NAMESPACE.equals(req.getNamespace())) {
+ if (!"active".equals(req.getDirectives().get("effective"))) {
+ continue;
+ }
- WeavingData wd = new WeavingData(className, methodName, argClasses, restrictions,
- allowedBundles.size() == 0 ? null : allowedBundles);
- weavingData.add(wd);
+ ArgRestrictions ar = new ArgRestrictions();
+ ar.addRestriction(0, Class.class.getName());
+ MethodRestriction mr = new MethodRestriction("load", ar);
+
+ List<BundleDescriptor> allowedBundles = Collections.emptyList();
+
+ weavingData.add(createWeavingData(ServiceLoader.class.getName(), "load", mr, allowedBundles));
+ }
}
+
return weavingData;
}
+
+ private static WeavingData createWeavingData(String className, String methodName,
+ MethodRestriction methodRestriction, List<BundleDescriptor> allowedBundles) {
+ ConsumerRestriction restriction = new ConsumerRestriction(className, methodRestriction);
+
+ // TODO is this correct? Why is it added to a set?
+ Set<ConsumerRestriction> restrictions = new HashSet<ConsumerRestriction>();
+ restrictions.add(restriction);
+
+ // TODO this can be done in the WeavingData itself?
+ String[] argClasses = restriction.getMethodRestriction(methodName).getArgClasses();
+
+ return new WeavingData(className, methodName, argClasses, restrictions,
+ allowedBundles.size() == 0 ? null : allowedBundles);
+ }
}
Copied: aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java (from r1203707, aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java)
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java?p2=aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java&p1=aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java&r1=1203707&r2=1205128&rev=1205128&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest2.java (original)
+++ aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java Tue Nov 22 19:02:48 2011
@@ -42,7 +42,7 @@ import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
-public class ProviderBundleTrackerCustomizerTest2 {
+public class ProviderBundleTrackerCustomizerGenericCapabilityTest {
@Test
public void testAddingRemovedBundle() throws Exception {
Bundle spiBundle = EasyMock.createMock(Bundle.class);
Propchange: aries/trunk/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Propchange: aries/trunk/spi-fly/spi-fly-dynamic-bundle/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Modified: aries/trunk/spi-fly/spi-fly-dynamic-bundle/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-dynamic-bundle/pom.xml?rev=1205128&r1=1205127&r2=1205128&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-dynamic-bundle/pom.xml (original)
+++ aries/trunk/spi-fly/spi-fly-dynamic-bundle/pom.xml Tue Nov 22 19:02:48 2011
@@ -95,6 +95,7 @@ JRE through META-INF/services resources)
<artifactId>maven-bundle-plugin</artifactId>
<configuration>
<instructions>
+ <Bundle-Activator>org.apache.aries.spifly.dynamic.DynamicWeavingActivator</Bundle-Activator>
<Import-Package>
*
</Import-Package>
@@ -106,7 +107,6 @@ JRE through META-INF/services resources)
org.apache.aries.spifly.dynamic
</Private-Package>
<Embed-Dependency>org.apache.aries.spifly.*;scope=compile;inline=true</Embed-Dependency>
- <Bundle-Activator>org.apache.aries.spifly.dynamic.DynamicWeavingActivator</Bundle-Activator>
</instructions>
</configuration>
</plugin>
Added: aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java?rev=1205128&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java (added)
+++ aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java Tue Nov 22 19:02:48 2011
@@ -0,0 +1,863 @@
+/**
+ * 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.Hashtable;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+import org.apache.aries.spifly.BaseActivator;
+import org.apache.aries.spifly.Streams;
+import org.apache.aries.spifly.api.SpiFlyConstants;
+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.BundleWiring;
+
+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 testBasicServiveLoaderUsage() throws Exception {
+ Dictionary<String, String> consumerHeaders = new Hashtable<String, String>();
+ consumerHeaders.put(SpiFlyConstants.REQUIRE_CAPABILITY, "osgi.spi.provider; effective:=active");
+
+ // Register the bundle that provides the SPI implementation.
+ Bundle providerBundle = mockProviderBundle("impl1", 1);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle);
+
+ 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;bundle-symbolic-name=spifly;bundle-version=1.9.4";
+ String di2 = "org.apache.aries.spifly;bundle-version=1.9.4;bundle-symbolic-name=spifly";
+ String di = wc.getDynamicImports().get(0);
+ Assert.assertTrue("Weaving should have added a dynamic import", di1.equals(di) || di2.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("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);
+
+ 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;bundle-symbolic-name=spifly;bundle-version=1.9.4";
+ String di2 = "org.apache.aries.spifly;bundle-version=1.9.4;bundle-symbolic-name=spifly";
+ String di = wc.getDynamicImports().get(0);
+ Assert.assertTrue("Weaving should have added a dynamic import", di1.equals(di) || di2.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);
+
+ // 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);
+
+ // Register in reverse order to make sure the order in which bundles are sorted is correct
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle1);
+
+ // 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");
+ Assert.assertEquals("All three services should be invoked in the correct order", "ollehHELLO5", 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);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2);
+
+ 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");
+ 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.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);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle3);
+
+ 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", "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);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2);
+ activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4);
+ activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4);
+
+ 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");
+ Assert.assertEquals("All providers should be selected for this one", "ollehimpl4", 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);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2);
+ activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4);
+ activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4);
+
+ 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");
+ Assert.assertEquals("All providers should be selected for this one", "ollehimpl4", 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);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2);
+ activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4);
+ activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4);
+
+ 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");
+ Assert.assertEquals("All providers should be selected for this one", "ollehHELLO5impl4", 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*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);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle2);
+ activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle2);
+ activator.registerProviderBundle("javax.xml.parsers.DocumentBuilderFactory", providerBundle3);
+ activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle4);
+ activator.registerProviderBundle("org.apache.aries.mytest.AltSPI", providerBundle4);
+
+ 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, "impl4", "org.apache.aries.spifly.dynamic.impl3.MyAltDocumentBuilderFactory");
+ testConsumerBundleWeaving(consumerBundle2, wh, "olleh", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
+ testConsumerBundleWeaving(consumerBundle3, wh, "", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl");
+ }
+
+ private void testConsumerBundleWeaving(Bundle consumerBundle, WeavingHook wh, 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());
+ }
+
+ @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", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", 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);
+
+ 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);
+
+ 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", "com.sun.org.apache.xerces.internal.jaxp.DocumentBuilderFactoryImpl", result.getName());
+ }
+
+ @Test
+ public void testJAXPClientWantsAltImplementation2() throws Exception {
+ Bundle systembundle = mockSystemBundle();
+
+ Bundle providerBundle = mockProviderBundle("impl3", 1);
+ activator.registerProviderBundle("javax.xml.parsers.DocumentBuilderFactory", providerBundle);
+
+ 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
+ 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");
+ }
+ 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.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) {
+ 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.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.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;
+ }
+ }
+}
Propchange: aries/trunk/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java
------------------------------------------------------------------------------
svn:mime-type = text/plain
Propchange: aries/trunk/spi-fly/spi-fly-examples/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,5 @@
target
+
+.project
+
+.settings
Propchange: aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client1-bundle/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Propchange: aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client1-jar/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Propchange: aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client2-bundle/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Modified: aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client2-bundle/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client2-bundle/pom.xml?rev=1205128&r1=1205127&r2=1205128&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client2-bundle/pom.xml (original)
+++ aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-client2-bundle/pom.xml Tue Nov 22 19:02:48 2011
@@ -65,7 +65,7 @@
org.apache.aries.spifly.examples.client2.impl
</Private-Package>
<Bundle-Activator>org.apache.aries.spifly.examples.client2.impl.Activator</Bundle-Activator>
- <SPI-Consumer>*</SPI-Consumer>
+ <Require-Capability>osgi.spi.provider; effective:=active</Require-Capability>
</instructions>
</configuration>
</plugin>
Propchange: aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider1-bundle/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Propchange: aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider1-jar/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Propchange: aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider2-bundle/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Modified: aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider2-bundle/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider2-bundle/pom.xml?rev=1205128&r1=1205127&r2=1205128&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider2-bundle/pom.xml (original)
+++ aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-provider2-bundle/pom.xml Tue Nov 22 19:02:48 2011
@@ -59,7 +59,7 @@
<Private-Package>
org.apache.aries.spifly.mysvc.impl2
</Private-Package>
- <SPI-Provider>*</SPI-Provider>
+ <Provide-Capability>osgi.spi.provider; effective:=active;</Provide-Capability>
</instructions>
</configuration>
</plugin>
Propchange: aries/trunk/spi-fly/spi-fly-examples/spi-fly-example-spi-bundle/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Propchange: aries/trunk/spi-fly/spi-fly-static-bundle/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Propchange: aries/trunk/spi-fly/spi-fly-static-tool/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath
Modified: aries/trunk/spi-fly/spi-fly-static-tool/src/main/java/org/apache/aries/spifly/statictool/Main.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-static-tool/src/main/java/org/apache/aries/spifly/statictool/Main.java?rev=1205128&r1=1205127&r2=1205128&view=diff
==============================================================================
--- aries/trunk/spi-fly/spi-fly-static-tool/src/main/java/org/apache/aries/spifly/statictool/Main.java (original)
+++ aries/trunk/spi-fly/spi-fly-static-tool/src/main/java/org/apache/aries/spifly/statictool/Main.java Tue Nov 22 19:02:48 2011
@@ -153,7 +153,8 @@ public class Main {
className = className.substring(0, className.length() - ".class".length());
className = className.replace(File.separator, ".");
- Set<WeavingData> wd = ConsumerHeaderProcessor.processHeader(consumerHeader);
+ // TODO support Require-Capability in addition to SPI-Consumer
+ Set<WeavingData> wd = ConsumerHeaderProcessor.processHeader(SpiFlyConstants.SPI_CONSUMER_HEADER, consumerHeader);
InputStream is = new FileInputStream(f);
byte[] b;
try {
Propchange: aries/trunk/spi-fly/spi-fly-weaver/
------------------------------------------------------------------------------
--- svn:ignore (original)
+++ svn:ignore Tue Nov 22 19:02:48 2011
@@ -1 +1,7 @@
target
+
+.project
+
+.settings
+
+.classpath