aries-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From dav...@apache.org
Subject 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...
Date Tue, 22 Nov 2011 19:02:52 GMT
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



Mime
View raw message