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/03/03 10:54:51 UTC

svn commit: r1076571 - in /aries/trunk/spi-fly: ./ spi-fly-core/ spi-fly-core/src/ spi-fly-core/src/main/ spi-fly-core/src/main/java/ spi-fly-core/src/main/java/org/ spi-fly-core/src/main/java/org/apache/ spi-fly-core/src/main/java/org/apache/aries/ sp...

Author: davidb
Date: Thu Mar  3 09:54:50 2011
New Revision: 1076571

URL: http://svn.apache.org/viewvc?rev=1076571&view=rev
Log:
Adding the beginnings of the WeavingHook-based SPI Fly to the maven build.

Note that there is no jar available in a maven repository yet that contains the weaving hook interfaces, therefore the build manually pulls down the equinox jar and uses that for the moment. This step can go away once the OSGi 4.3 core jar is available through maven.

Added:
    aries/trunk/spi-fly/spi-fly-core/
    aries/trunk/spi-fly/spi-fly-core/pom.xml
    aries/trunk/spi-fly/spi-fly-core/src/
    aries/trunk/spi-fly/spi-fly-core/src/main/
    aries/trunk/spi-fly/spi-fly-core/src/main/java/
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Activator.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ArgRestrictions.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BundleDescriptor.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerRestriction.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/HeaderParser.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MethodRestriction.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MultiDelegationClassloader.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Pair.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Streams.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/TCCLSetterVisitor.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/
    aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java
    aries/trunk/spi-fly/spi-fly-deps/
    aries/trunk/spi-fly/spi-fly-deps/pom.xml
Modified:
    aries/trunk/spi-fly/pom.xml

Modified: aries/trunk/spi-fly/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/pom.xml?rev=1076571&r1=1076570&r2=1076571&view=diff
==============================================================================
--- aries/trunk/spi-fly/pom.xml (original)
+++ aries/trunk/spi-fly/pom.xml Thu Mar  3 09:54:50 2011
@@ -45,15 +45,18 @@
         <url>http://svn.apache.org/viewvc/aries/trunk/spi-fly</url>
     </scm>
 
+    <!--
     <repositories>
-        <!-- The SpringSource bundle repository is required in order to be able to download osgified AspectJ bundles -->
+        <!- - The SpringSource bundle repository is required in order to be able to download osgified AspectJ bundles - ->
         <repository>
             <id>com.springsource.repository.bundles.external</id>
             <name>SpringSource Enterprise Bundle Repository - External Bundle Releases</name>
             <url>http://repository.springsource.com/maven/bundles/external</url>
         </repository> 
     </repositories>
+    -->
 
+    <!--
     <dependencyManagement>
         <dependencies>
             <dependency>
@@ -113,7 +116,7 @@
                 <scope>test</scope>
             </dependency>
 
-            <!-- internal -->
+            <!- - internal - ->
             <dependency>
                 <groupId>org.apache.aries.spifly</groupId>
                 <artifactId>org.apache.aries.spifly.core</artifactId>
@@ -121,6 +124,7 @@
             </dependency>
         </dependencies>
     </dependencyManagement>
+    -->
 
     <dependencies>
         <dependency>
@@ -131,8 +135,9 @@
     </dependencies>
 
     <modules>
-        <!--
+        <module>spi-fly-deps</module>
         <module>spi-fly-core</module>
+        <!--
         <module>spi-fly-itests</module>
         -->
     </modules>

Added: 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=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/pom.xml (added)
+++ aries/trunk/spi-fly/spi-fly-core/pom.xml Thu Mar  3 09:54:50 2011
@@ -0,0 +1,55 @@
+<?xml version="1.0" encoding="UTF-8"?>
+    <!--
+ 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.
+    -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.aries.spifly</groupId>
+        <artifactId>spifly</artifactId>
+        <version>0.4-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>org.apache.aries.spifly.core</artifactId>
+    <packaging>bundle</packaging>
+    <name>Apache Aries SPI Fly Core</name>
+    <description>
+This bundle contains an extender that facilitates the use
+of JRE SPI providers (components typically plugged in to the 
+JRE through META-INF/services resources).
+    </description>
+
+    <dependencies>
+        <dependency>
+            <groupId>asm</groupId>
+            <artifactId>asm-all</artifactId>
+            <version>3.3.1</version>
+            <scope>provided</scope>
+        </dependency>
+
+        <dependency>
+            <!-- Use the Eclipse version of OSGi until the core 4.3 OSGi jar is available in Maven. -->
+            <groupId>org.eclipse</groupId>
+            <artifactId>osgi</artifactId>
+            <version>3.7M5</version>
+            <scope>system</scope>
+            <systemPath>${basedir}/../spi-fly-deps/target/downloaded/org.eclipse.osgi_3.7.0.jar</systemPath>
+        </dependency>
+    </dependencies>
+</project>

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Activator.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Activator.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Activator.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Activator.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,176 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.SortedMap;
+import java.util.TreeMap;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleActivator;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.framework.hooks.weaving.WeavingHook;
+import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.BundleTracker;
+import org.osgi.util.tracker.ServiceTracker;
+
+public class Activator implements BundleActivator {
+    // Provide static access to this activator. The bundle must therefore be a singleton.
+    static Activator activator;
+
+    private BundleContext bundleContext;
+    private ServiceRegistration<WeavingHook> weavingHookService;
+    private LogServiceTracker logServiceTracker;
+    private List<LogService> logServices = new CopyOnWriteArrayList<LogService>();
+    private BundleTracker<List<ServiceRegistration<?>>> bundleTracker;
+
+    private final ConcurrentMap<String, SortedMap<Long, Bundle>> registeredProviders = 
+            new ConcurrentHashMap<String, SortedMap<Long, Bundle>>();
+
+    private final ConcurrentMap<Bundle, Map<ConsumerRestriction, List<BundleDescriptor>>> consumerRestrictions = 
+            new ConcurrentHashMap<Bundle, Map<ConsumerRestriction, List<BundleDescriptor>>>(); 
+    
+    public synchronized void start(BundleContext context) throws Exception {
+        bundleContext = context;
+        
+        logServiceTracker = new LogServiceTracker(context);
+        logServiceTracker.open();
+
+        WeavingHook wh = new ClientWeavingHook(context);
+        weavingHookService = context.registerService(WeavingHook.class, wh,
+                null);
+
+        bundleTracker = new BundleTracker<List<ServiceRegistration<?>>>(context,
+                Bundle.ACTIVE, new ProviderBundleTrackerCustomizer(this, context.getBundle()));
+        bundleTracker.open();
+        
+        activator = this;
+    }
+
+    public synchronized void stop(BundleContext context) throws Exception {
+        activator = null;
+        bundleTracker.close();
+        weavingHookService.unregister();
+        logServiceTracker.close();
+    }
+
+    void log(int level, String message) {
+        synchronized (logServices) {
+            for (LogService log : logServices) {
+                log.log(level, message);
+            }
+        }
+    }
+
+    void log(int level, String message, Throwable th) {
+        synchronized (logServices) {
+            for (LogService log : logServices) {
+                log.log(level, message, th);
+            }
+        }
+    }
+
+    public void registerProviderBundle(String registrationClassName, Bundle bundle) {        
+        registeredProviders.putIfAbsent(registrationClassName, Collections.synchronizedSortedMap(new TreeMap<Long, Bundle>()));
+        SortedMap<Long, Bundle> map = registeredProviders.get(registrationClassName);
+        map.put(bundle.getBundleId(), bundle);
+    }
+
+    public Collection<Bundle> findProviderBundles(String name) {
+        SortedMap<Long, Bundle> map = registeredProviders.get(name);
+        return map == null ? Collections.<Bundle>emptyList() : map.values();
+    }
+    
+    // TODO unRegisterProviderBundle();
+    public void registerConsumerBundle( Bundle consumerBundle,
+            Set<ConsumerRestriction> restrictions, List<BundleDescriptor> allowedBundles) {
+        consumerRestrictions.putIfAbsent(consumerBundle, new HashMap<ConsumerRestriction, List<BundleDescriptor>>());
+        Map<ConsumerRestriction, List<BundleDescriptor>> map = consumerRestrictions.get(consumerBundle);
+        for (ConsumerRestriction restriction : restrictions) {
+            map.put(restriction, allowedBundles);
+        }
+    }
+
+    public Collection<Bundle> findConsumerRestrictions(Bundle consumer, String className, String methodName,
+            Map<Pair<Integer, String>, String> args) {
+        Map<ConsumerRestriction, List<BundleDescriptor>> restrictions = consumerRestrictions.get(consumer);
+        if (restrictions == null) {
+            // Null means: no restrictions
+            return null;
+        }
+        
+        for (Map.Entry<ConsumerRestriction, List<BundleDescriptor>> entry : restrictions.entrySet()) {
+            if (entry.getKey().matches(className, methodName, args)) {
+                return getBundles(entry.getValue());
+            }
+        }
+        
+        // Empty collection: nothing matches
+        return Collections.emptySet();
+    }
+
+    private Collection<Bundle> getBundles(List<BundleDescriptor> descriptors) {
+        if (descriptors == null) {
+            return null;
+        }
+        
+        List<Bundle> bundles = new ArrayList<Bundle>();
+        for (Bundle b : bundleContext.getBundles()) {
+            for (BundleDescriptor desc : descriptors) {
+                if (b.getSymbolicName().equals(desc.getSymbolicName())) {
+                    if (desc.getVersion() == null || b.getVersion().equals(desc.getVersion())) {
+                        bundles.add(b);
+                    }
+                }
+            }
+        }
+        return bundles;
+    }
+
+    // TODO unRegisterConsumerBundle();
+    
+    private class LogServiceTracker extends ServiceTracker<LogService, LogService> {
+        public LogServiceTracker(BundleContext context) {
+            super(context, LogService.class, null);
+        }
+
+        public LogService addingService(ServiceReference<LogService> reference) {
+            LogService svc = super.addingService(reference);
+            if (svc != null)
+                logServices.add(svc);
+            return svc;
+        }
+
+        @Override
+        public void removedService(ServiceReference<LogService> reference, LogService service) {
+            logServices.remove(service);
+        }        
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ArgRestrictions.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ArgRestrictions.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ArgRestrictions.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ArgRestrictions.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,74 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class ArgRestrictions {
+    private final Map<Pair<Integer, String>, List<String>> restrictions = 
+            new HashMap<Pair<Integer, String>, List<String>>();
+    
+    public void addRestriction(int argNumber, String className) {
+        addRestriction(argNumber, className, null);
+    }
+
+    public void addRestriction(int argNumber, String className, String allowedArgValue) {
+        Pair<Integer, String> key = new Pair<Integer, String>(argNumber, className);
+        List<String> allowedValues = restrictions.get(key);
+        if (allowedArgValue != null) {
+            if (allowedValues == null) {
+                allowedValues = new ArrayList<String>();
+                restrictions.put(key, allowedValues);
+            }            
+            allowedValues.add(allowedArgValue);            
+        }
+        restrictions.put(key, allowedValues);
+    }
+
+    public String[] getArgClasses() {
+        List<String> classes = new ArrayList<String>();
+        for (Pair<Integer, String> key : restrictions.keySet()) {
+            classes.add(key.getRight());
+        }
+        
+        if (classes.size() == 0)
+            return null;
+        return classes.toArray(new String [classes.size()]);
+    }
+
+    public boolean matches(Map<Pair<Integer, String>, String> args) {
+        for (Pair<Integer, String> key : args.keySet()) {
+            if (!restrictions.containsKey(key)) {
+                return false;
+            }
+            
+            List<String> values = restrictions.get(key);
+            if (values != null) {
+                String val = args.get(key);
+                if (!values.contains(val)) {
+                    return false;
+                }
+            }
+        }
+        return true;        
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BundleDescriptor.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BundleDescriptor.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BundleDescriptor.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BundleDescriptor.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,43 @@
+/**
+ * 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;
+
+import org.osgi.framework.Version;
+
+class BundleDescriptor {
+    final String symbolicName;
+    final Version version;
+    
+    BundleDescriptor(String symbolicName) {
+        this(symbolicName, null);
+    }
+
+    BundleDescriptor(String symbolicName, Version version) {
+        this.symbolicName = symbolicName;
+        this.version = version;
+    }
+
+    public String getSymbolicName() {
+        return symbolicName;
+    }
+
+    public Version getVersion() {
+        return version;
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ClientWeavingHook.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,212 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+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.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassWriter;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.Version;
+import org.osgi.framework.hooks.weaving.WeavingHook;
+import org.osgi.framework.hooks.weaving.WovenClass;
+import org.osgi.service.log.LogService;
+
+public class ClientWeavingHook implements WeavingHook {
+    private final String addedImport;
+    
+    ClientWeavingHook(BundleContext context) {
+        Bundle b = context.getBundle();
+        String bver = b.getVersion().toString();
+        String bsn = b.getSymbolicName();
+        
+        addedImport = Util.class.getPackage().getName() + 
+            ";bundle-symbolic-name=" + bsn + 
+            ";bundle-version=" + bver;
+    }
+    
+	@Override
+	public void weave(WovenClass wovenClass) {
+	    Bundle consumerBundle = wovenClass.getBundleWiring().getBundle();
+        String consumerHeader = consumerBundle.getHeaders().get(SpiFlyConstants.SPI_CONSUMER_HEADER);
+        if (consumerHeader != null) {
+	        Activator.activator.log(LogService.LOG_DEBUG, "Weaving class " + wovenClass.getClassName());            
+            
+            WeavingData[] wd = processHeader(consumerBundle, consumerHeader);
+	        
+	        ClassReader cr = new ClassReader(wovenClass.getBytes());
+	        ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS | ClassWriter.COMPUTE_FRAMES);
+	        TCCLSetterVisitor tsv = new TCCLSetterVisitor(cw, wovenClass.getClassName(), wd);
+	        cr.accept(tsv, 0);	        
+	        wovenClass.setBytes(cw.toByteArray());
+	        if (tsv.additionalImportRequired())
+	            wovenClass.getDynamicImports().add(addedImport);
+	    }			
+	}
+
+	/**
+	 * Parses headers of the following syntax:
+	 * <ul>
+	 * <li><tt>org.acme.MyClass#myMethod</tt> - apply the weaving to all overloads of <tt>myMethod()</tt> 
+	 * in <tt>MyClass</tt>
+	 * <li><tt>org.acme.MyClass#myMethod(java.lang.String, java.util.List)</tt> - apply the weaving only 
+	 * to the <tt>myMethod(String, List)</tt> overload in <tt>MyClass</tt>
+	 * <li><tt>org.acme.MyClass#myMethod()</tt> - apply the weaving only to the noarg overload of 
+	 * <tt>myMethod()</tt>
+	 * <li><b>true</b> - equivalend to <tt>java.util.ServiceLoader#load(java.lang.Class)</tt>
+	 * </ul>
+	 * Additionally, it registers the consumer's contraints with the consumer registry in the activator, if the 
+	 * consumer is only constrained to a certain set of bundles.<p/>
+	 * 
+	 * The following attributes are supported:
+	 * <ul>
+	 * <li><tt>bundle</tt> - restrict wiring to the bundle with the specifies Symbolic Name. The attribute value 
+	 * 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 
+	 * can be specified separated by a '|' sign. 
+	 * </ul>
+	 * 
+	 * @param consumerBundle the consuming bundle.
+	 * @param consumerHeader the <tt>SPI-Consumer</tt> header.
+	 * @return an instance of the {@link WeavingData} class.
+	 */
+    private WeavingData[] processHeader(Bundle consumerBundle, String consumerHeader) {
+        Set<WeavingData> weavingData = new HashSet<WeavingData>();
+        
+        for (PathElement element : HeaderParser.parseHeader(consumerHeader)) {
+            List<BundleDescriptor> allowedBundles = new ArrayList<BundleDescriptor>();
+            String name = element.getName().trim();
+
+            String className;
+            String methodName;
+            MethodRestriction methodRestriction;
+            
+            int hashIdx = name.indexOf('#');
+            if (hashIdx > 0) {                
+                className = name.substring(0, hashIdx);
+                int braceIdx = name.substring(hashIdx).indexOf('(');
+                if (braceIdx > 0) {
+                    methodName = name.substring(hashIdx + 1, hashIdx + braceIdx);
+                    ArgRestrictions argRestrictions = new ArgRestrictions();
+                    int closeIdx = name.substring(hashIdx).indexOf(')');
+                    if (closeIdx > 0) {
+                        String classes = name.substring(hashIdx + braceIdx + 1, hashIdx + closeIdx).trim();
+                        if (classes.length() > 0) {
+                            if (classes.indexOf('[') > 0) {
+                                int argNumber = 0;
+                                for (String s : classes.split(",")) {
+                                    int idx = s.indexOf('[');
+                                    int end = s.indexOf(']', idx);
+                                    if (idx > 0 && end > idx) {
+                                        argRestrictions.addRestriction(argNumber, s.substring(0, idx), s.substring(idx + 1, end));
+                                    } else {
+                                        argRestrictions.addRestriction(argNumber, s);
+                                    }
+                                    argNumber++;
+                                }
+                            } else {
+                                String[] classNames = classes.split(",");
+                                for (int i = 0; i < classNames.length; i++) {
+                                    argRestrictions.addRestriction(i, classNames[i]);
+                                }
+                            }
+                        } else {
+                            argRestrictions = null;
+                        }
+                    }
+                    methodRestriction = new MethodRestriction(methodName, argRestrictions);
+                } else {
+                    methodName = name.substring(hashIdx + 1);
+                    methodRestriction = new MethodRestriction(methodName);
+                }
+            } else {
+                if ("true".equalsIgnoreCase(name)) {
+                    className = ServiceLoader.class.getName();
+                    methodName = "load";
+                    ArgRestrictions argRestrictions = new ArgRestrictions();
+                    argRestrictions.addRestriction(0, Class.class.getName());
+                    methodRestriction = new MethodRestriction(methodName, argRestrictions);
+                } else {
+                    throw new IllegalArgumentException("Must at least specify class name and method name: " + name);
+                }
+            }  
+            ConsumerRestriction restriction = new ConsumerRestriction(className, methodRestriction);
+
+            Set<ConsumerRestriction> restrictions = new HashSet<ConsumerRestriction>();
+            restrictions.add(restriction);
+                
+            String bsn = element.getAttribute("bundle");
+            if (bsn != null) {
+                bsn = bsn.trim();
+                if (bsn.length() > 0) {
+                    for (String s : bsn.split("\\|")) {
+                        int colonIdx = s.indexOf(':');
+                        if (colonIdx > 0) {
+                            String sn = s.substring(0, colonIdx);
+                            String versionSfx = s.substring(colonIdx + 1);
+                            if (versionSfx.startsWith("version=")) {
+                                allowedBundles.add(new BundleDescriptor(sn, 
+                                        Version.parseVersion(versionSfx.substring("version=".length()))));
+                            } else {
+                                allowedBundles.add(new BundleDescriptor(sn));
+                            }
+                        } else {
+                            allowedBundles.add(new BundleDescriptor(s));
+                        }
+                    }
+                }
+            }
+                        
+            String bid = element.getAttribute("bundleId");
+            if (bid != null) {
+                bid = bid.trim();
+                if (bid.length() > 0) {
+                    for (String s : bid.split("\\|")) {
+                        for (Bundle b : consumerBundle.getBundleContext().getBundles()) {
+                            if (("" + b.getBundleId()).equals(s)) {                       
+                                allowedBundles.add(new BundleDescriptor(b.getSymbolicName()));
+                                break;                        
+                            }
+                        }                        
+                    }
+                }
+            }
+            
+            // TODO fix log message
+            // Activator.activator.log(LogService.LOG_INFO, "Weaving " + className + "#" + methodName + " from bundle " + 
+            //    consumerBundle.getSymbolicName() + " to " + (allowedBundles.size() == 0 ? " any provider" : allowedBundles));
+                        
+            Activator.activator.registerConsumerBundle(consumerBundle, restrictions, 
+                    allowedBundles.size() == 0 ? null : allowedBundles);           
+            String[] argClasses = restriction.getMethodRestriction(methodName).getArgClasses();
+
+            weavingData.add(new WeavingData(className, methodName, argClasses));
+        }
+        return weavingData.toArray(new WeavingData [weavingData.size()]);
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerRestriction.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerRestriction.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerRestriction.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ConsumerRestriction.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,46 @@
+/**
+ * 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;
+
+import java.util.Map;
+
+public class ConsumerRestriction {
+    private final String className;
+    private final MethodRestriction methodRestriction;
+
+    public ConsumerRestriction(String className, MethodRestriction methodRestriction) {
+        this.className = className;
+        this.methodRestriction = methodRestriction;
+    }
+
+    public MethodRestriction getMethodRestriction(String methodName) {
+        if (methodName.equals(methodRestriction.getMethodName())) {
+            return methodRestriction;
+        } else {
+            return null;
+        }
+    }
+
+    public boolean matches(String clsName, String mtdName, Map<Pair<Integer, String>, String> args) {
+        if (!className.equals(clsName))
+            return false;
+        
+        return methodRestriction.matches(mtdName, args);
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/HeaderParser.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/HeaderParser.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/HeaderParser.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/HeaderParser.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,115 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Utility class to parse a standard OSGi header with paths.
+ *
+ * Stolen from Aries Blueprint Core (blueprint.utils). Need to give it back! 
+ */
+public class HeaderParser  {
+    private HeaderParser() {}
+    
+    /**
+     * Parse a given OSGi header into a list of paths
+     *
+     * @param header the OSGi header to parse
+     * @return the list of paths extracted from this header
+     */
+    public static List<PathElement> parseHeader(String header) {
+        List<PathElement> elements = new ArrayList<PathElement>();
+        if (header == null || header.trim().length() == 0) {
+            return elements;
+        }
+        String[] clauses = header.split(",");
+        for (String clause : clauses) {
+            String[] tokens = clause.split(";");
+            if (tokens.length < 1) {
+                throw new IllegalArgumentException("Invalid header clause: " + clause);
+            }
+            PathElement elem = new PathElement(tokens[0].trim());
+            elements.add(elem);
+            for (int i = 1; i < tokens.length; i++) {
+                int pos = tokens[i].indexOf('=');
+                if (pos != -1) {
+                    if (pos > 0 && tokens[i].charAt(pos - 1) == ':') {
+                        String name = tokens[i].substring(0, pos - 1).trim();
+                        String value = tokens[i].substring(pos + 1).trim();
+                        elem.addDirective(name, value);
+                    } else {
+                        String name = tokens[i].substring(0, pos).trim();
+                        String value = tokens[i].substring(pos + 1).trim();
+                        elem.addAttribute(name, value);
+                    }
+                } else {
+                    elem = new PathElement(tokens[i].trim());
+                    elements.add(elem);
+                }
+            }
+        }
+        return elements;
+    }
+
+    public static class PathElement {
+        
+        private String path;
+        private Map<String, String> attributes;
+        private Map<String, String> directives;
+        
+        public PathElement(String path) {
+            this.path = path;
+            this.attributes = new HashMap<String, String>();
+            this.directives = new HashMap<String, String>();
+        }
+        
+        public String getName() {
+            return this.path;
+        }
+        
+        public Map<String, String> getAttributes() {
+            return attributes;
+        }
+        
+        public String getAttribute(String name) {
+            return attributes.get(name);
+        }
+        
+        public void addAttribute(String name, String value) {
+            attributes.put(name, value);
+        }
+        
+        public Map<String, String> getDirectives() {
+            return directives;
+        }
+        
+        public String getDirective(String name) {
+            return directives.get(name);
+        }
+        
+        public void addDirective(String name, String value) {
+            directives.put(name, value);
+        }        
+        
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MethodRestriction.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MethodRestriction.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MethodRestriction.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MethodRestriction.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,60 @@
+/**
+ * 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;
+
+import java.util.Map;
+
+public class MethodRestriction {
+    private final String methodName;
+    private final ArgRestrictions argRestrictions;
+
+    public MethodRestriction(String methodName) {
+        this(methodName, null);
+    }
+
+    public MethodRestriction(String methodName, ArgRestrictions argRestrictions) {
+        this.methodName = methodName;
+        this.argRestrictions = argRestrictions;
+    }
+
+    public String getMethodName() {
+        return methodName;
+    }
+
+    public String[] getArgClasses() {
+        if (argRestrictions == null) {
+            return null;
+        }
+
+        return argRestrictions.getArgClasses();
+    }
+
+    public boolean matches(String mtdName, Map<Pair<Integer, String>, String> args) {
+        if (!methodName.equals(mtdName))
+            return false;
+        
+        if (args == null) 
+            return true;
+        
+        if (argRestrictions == null)
+            return true;
+        
+        return argRestrictions.matches(args);
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MultiDelegationClassloader.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MultiDelegationClassloader.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MultiDelegationClassloader.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/MultiDelegationClassloader.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,87 @@
+/**
+ * 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;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Enumeration;
+import java.util.List;
+
+/** A classloader that delegates to a number of other classloaders.
+ * This classloader can be used if a single classloader is needed that has
+ * vibisility of a number of other classloaders. For example if a Thread Context
+ * Classloader is needed that has visibility of a number of bundles so that 
+ * ServiceLoader.load() can find all the services provided by these bundles.
+ */
+public class MultiDelegationClassloader extends ClassLoader {
+    private final ClassLoader[] delegates;
+    
+    public MultiDelegationClassloader(ClassLoader ... classLoaders) {
+        if (classLoaders == null) 
+            throw new NullPointerException();
+        
+        delegates = classLoaders.clone();
+    }
+    
+    @Override
+    public URL getResource(String name) {
+        for (ClassLoader cl : delegates) {
+            URL res = cl.getResource(name);
+            if (res != null)
+                return res;
+        }                
+        return null;
+    }
+    
+    @Override
+    public InputStream getResourceAsStream(String name) {
+        for (ClassLoader cl : delegates) {
+            InputStream is = cl.getResourceAsStream(name);
+            if (is != null)
+                return is;
+        }
+        return null;
+    }
+    
+    @Override
+    public Enumeration<URL> getResources(String name) throws IOException {
+        List<URL> urls = new ArrayList<URL>();
+        
+        for (ClassLoader cl : delegates) {
+            urls.addAll(Collections.list(cl.getResources(name)));
+        }
+        return Collections.enumeration(urls);
+    }
+    
+    @Override
+    public Class<?> loadClass(String name) throws ClassNotFoundException {
+        ClassNotFoundException lastEx = null;
+        for (ClassLoader cl : delegates) {
+            try {
+                return cl.loadClass(name);
+            } catch (ClassNotFoundException e) {
+                lastEx = e;
+            }
+        }
+        throw lastEx;
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Pair.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Pair.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Pair.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Pair.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,78 @@
+/**
+ * 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;
+
+/**
+ * A simple holder object for a pair of objects.
+ */
+public class Pair <A, B> {
+    private final A left;
+    private final B right;
+
+    public Pair(A left, B right) {
+        this.left = left;
+        this.right = right;
+    }
+
+    public A getLeft() {
+        return left;
+    }
+
+    public B getRight() {
+        return right;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + ((left == null) ? 0 : left.hashCode());
+        result = prime * result + ((right == null) ? 0 : right.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        
+        Pair<?, ?> other = (Pair<?, ?>) obj;
+        if (left == null) {
+            if (other.left != null)
+                return false;
+        } else if (!left.equals(other.left))
+            return false;
+        if (right == null) {
+            if (other.right != null)
+                return false;
+        } else if (!right.equals(other.right))
+            return false;
+        return true;
+    }
+
+    @Override
+    public String toString() {
+        return "Pair [left=" + left + ", right=" + right + "]";
+    }    
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,135 @@
+/**
+ * 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;
+
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.List;
+
+import org.apache.aries.spifly.api.SpiFlyConstants;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleEvent;
+import org.osgi.framework.ServiceRegistration;
+import org.osgi.service.log.LogService;
+import org.osgi.util.tracker.BundleTrackerCustomizer;
+
+/**
+ * Listens for new bundles being installed and registers them as service providers if applicable.
+ */
+public class ProviderBundleTrackerCustomizer implements BundleTrackerCustomizer<List<ServiceRegistration<?>>> {
+    final Activator activator;
+    final Bundle spiBundle;
+
+    public ProviderBundleTrackerCustomizer(Activator a, Bundle b) {
+        activator = a;
+        spiBundle = b;
+                
+        // TODO handle pre-existing bundles.
+    }
+
+    public List<ServiceRegistration<?>> addingBundle(Bundle bundle, BundleEvent event) {
+        log(LogService.LOG_INFO, "Bundle Considered for SPI providers: "
+                + bundle.getSymbolicName());
+
+        if (bundle.equals(spiBundle)) {
+            return null;
+        }
+
+        if (bundle.getHeaders().get(SpiFlyConstants.SPI_PROVIDER_HEADER) == null) {
+            log(LogService.LOG_INFO, "No '"
+                    + SpiFlyConstants.SPI_PROVIDER_HEADER
+                    + "' Manifest header. Skipping bundle: "
+                    + bundle.getSymbolicName());
+            return null;
+        } else {
+            log(LogService.LOG_INFO, "Examining bundle for SPI provider: "
+                    + bundle.getSymbolicName());
+        }
+
+        Enumeration<URL> entries = bundle.findEntries("META-INF/services", "*", false);
+        if (entries == null) {
+            return null;
+        }
+
+        List<ServiceRegistration<?>> registrations = new ArrayList<ServiceRegistration<?>>();
+        while (entries.hasMoreElements()) {
+            URL url = entries.nextElement();
+            log(LogService.LOG_INFO, "Found SPI resource: " + url);
+
+            try {
+                BufferedReader reader = new BufferedReader(
+                        new InputStreamReader(url.openStream()));
+                String className = reader.readLine();
+                // TODO need to read more than one class name!
+
+                Class<?> cls = bundle.loadClass(className);
+                Object o = cls.newInstance();
+                log(LogService.LOG_INFO, "Instantiated SPI provider: " + o);
+
+                Hashtable<String, Object> props = new Hashtable<String, Object>();
+                props.put(SpiFlyConstants.SPI_PROVIDER_URL, url);
+
+                String s = url.toExternalForm();
+                int idx = s.lastIndexOf('/');
+                String registrationClassName = className;
+                if (s.length() > idx) {
+                    registrationClassName = s.substring(idx + 1);
+                }
+
+                ServiceRegistration<?> reg = bundle.getBundleContext()
+                        .registerService(registrationClassName, o, props);
+                registrations.add(reg);
+
+                activator.registerProviderBundle(registrationClassName, bundle);
+                log(LogService.LOG_INFO, "Registered service: " + reg);                
+            } catch (Exception e) {
+                log(LogService.LOG_WARNING,
+                        "Could not load SPI implementation referred from " + url, e);
+            }
+        }
+        
+        return registrations;
+    }
+
+    public void modifiedBundle(Bundle bundle, BundleEvent event, List<ServiceRegistration<?>> registrations) {
+        // should really be doing something here...
+    }
+
+    public void removedBundle(Bundle bundle, BundleEvent event, List<ServiceRegistration<?>> registrations) {
+        if (registrations == null)
+            return;
+        
+        for (ServiceRegistration<?> reg : registrations) {
+            reg.unregister();
+            log(LogService.LOG_INFO, "Unregistered: " + reg);            
+        }
+    }
+
+    private void log(int level, String message) {
+        activator.log(level, message);
+    }
+
+    private void log(int level, String message, Throwable th) {
+        activator.log(level, message, th);
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Streams.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Streams.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Streams.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Streams.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,57 @@
+/**
+ * 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;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class Streams {
+    private Streams() {}
+    
+    public static void pump(InputStream is, OutputStream os) throws IOException {        
+        byte[] bytes = new byte[8192];
+
+        int length = 0;
+        int offset = 0;
+
+        while ((length = is.read(bytes, offset, bytes.length - offset)) != -1) {
+            offset += length;
+
+            if (offset == bytes.length) {
+                os.write(bytes, 0, bytes.length);
+                offset = 0;
+            }
+        }
+        if (offset != 0) {
+            os.write(bytes, 0, offset);
+        }
+    }
+    
+    public static byte [] suck(InputStream is) throws IOException {
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            pump(is, baos);
+            return baos.toByteArray();
+        } finally {
+            is.close();
+        }
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/TCCLSetterVisitor.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/TCCLSetterVisitor.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/TCCLSetterVisitor.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/TCCLSetterVisitor.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,203 @@
+/**
+ * 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;
+
+import java.util.Arrays;
+import java.util.ServiceLoader;
+
+import org.objectweb.asm.ClassAdapter;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.MethodAdapter;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * This class implements an ASM ClassVisitor which puts the appropriate ThreadContextClassloader
+ * calls around applicable method invocations. It does the actual bytecode weaving.
+ */
+public class TCCLSetterVisitor extends ClassAdapter implements ClassVisitor, Opcodes {
+    private static final String GENERATED_METHOD_NAME = "$$FCCL$$";
+
+    private static final String UTIL_CLASS = Util.class.getName().replace('.', '/'); 
+    private static final String VOID_RETURN_TYPE = "()V";
+    
+    private final String targetClass;
+    private final WeavingData [] weavingData;
+
+    // Set to true when the weaving code has changed the client such that an additional import 
+    // (to the Util.class.getPackage()) is needed.
+    private boolean additionalImportRequired = false;
+
+    public TCCLSetterVisitor(ClassVisitor cv, String className, WeavingData [] weavingData) {
+        super(cv);
+        this.targetClass = className.replace('.', '/');
+        this.weavingData = weavingData;
+    }
+
+    @Override
+    public MethodVisitor visitMethod(int access, String name, String desc,
+            String signature, String[] exceptions) {
+        System.out.println("@@@ " + access + ": " + name + "#" + desc + "#" + signature + "~" + Arrays.toString(exceptions));
+
+        MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions);
+        return new TCCLSetterMethodVisitor(mv);
+    }
+
+    @Override
+    public void visitEnd() {
+        // Add generated static method
+
+        for (WeavingData wd : weavingData) {
+            /* Equivalent to:
+             * private static void $$FCCL$$<className>$<methodName>(Class<?> cls) {
+             *   Util.fixContextClassLoader("java.util.ServiceLoader", "load", cls, WovenClass.class.getClassLoader());
+             * }
+             */
+             MethodVisitor mv = cv.visitMethod(ACC_PRIVATE + ACC_STATIC, getGeneratedMethodName(wd), 
+                     "(Ljava/lang/Class;)V", "(Ljava/lang/Class<*>;)V", null);
+             mv.visitCode();
+             mv.visitLdcInsn(wd.getClassName());
+             mv.visitLdcInsn(wd.getMethodName());
+             mv.visitVarInsn(ALOAD, 0);
+             String typeIdentifier = "L" + targetClass + ";";
+             mv.visitLdcInsn(Type.getType(typeIdentifier));
+             mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class",
+                     "getClassLoader", "()Ljava/lang/ClassLoader;");
+             mv.visitMethodInsn(
+                     INVOKESTATIC,
+                     "org/apache/aries/spifly/Util",
+                     "fixContextClassloader",
+                     "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Class;Ljava/lang/ClassLoader;)V");
+             mv.visitInsn(RETURN);
+             mv.visitMaxs(4, 1);
+             mv.visitEnd();            
+        }
+
+        super.visitEnd();
+    }
+
+    private String getGeneratedMethodName(WeavingData wd) {
+        StringBuilder name = new StringBuilder(GENERATED_METHOD_NAME);
+        name.append(wd.getClassName().replace('.', '#'));
+        name.append("$");
+        name.append(wd.getMethodName());
+        if (wd.getArgClasses() != null) {
+            for (String cls : wd.getArgClasses()) {
+                name.append("$");
+                name.append(cls.replace('.', '#'));
+            }
+        }
+        return name.toString();
+    }
+    
+    private class TCCLSetterMethodVisitor extends MethodAdapter implements MethodVisitor
+    {
+        Type lastLDCType;
+        
+        public TCCLSetterMethodVisitor(MethodVisitor mv) {
+            super(mv);
+        }
+        
+        /**
+         * Store the last LDC call. When ServiceLoader.load(Class cls) is called
+         * the last LDC call before the ServiceLoader.load() visitMethodInsn call
+         * contains the class being passed in. We need to pass this class to $$FCCL$$ as well
+         * so we can copy the value found in here.
+         */
+        @Override
+        public void visitLdcInsn(Object cst) {
+            if (cst instanceof Type) {
+                lastLDCType = ((Type) cst);
+            }
+            super.visitLdcInsn(cst);
+        }
+
+        /**
+         * Wrap selected method calls with
+         *  Util.storeContextClassloader();
+         *  $$FCCL$$(<class>)
+         *  Util.restoreContextClassloader(); 
+         */
+        @Override
+        public void visitMethodInsn(int opcode, String owner, String name, String desc) {
+            System.out.println("### " + opcode + ": " + owner + "#" + name + "#" + desc);
+                        
+            WeavingData wd = findWeavingData(owner, name, desc);            
+            if (opcode == INVOKESTATIC && wd != null) {
+                System.out.println("+++ Gotcha!");
+          
+                additionalImportRequired = true;
+
+                // Add: Util.storeContextClassloader();                
+                mv.visitMethodInsn(INVOKESTATIC, UTIL_CLASS,
+                        "storeContextClassloader", VOID_RETURN_TYPE);
+
+                // Add: MyClass.$$FCCL$$<classname>$<methodname>(<class>);                
+                if (ServiceLoader.class.getName().equals(wd.getClassName()) &&
+                    "load".equals(wd.getMethodName()) &&
+                    (wd.getArgClasses() == null || Arrays.equals(new String [] {Class.class.getName()}, wd.getArgClasses()))) {
+                    // ServiceLoader.load() is a special case because it's a general-purpose service loader, 
+                    // therefore, the target class it the class being passed in to the ServiceLoader.load() 
+                    // call itself.
+                    mv.visitLdcInsn(lastLDCType);
+                } else {
+                    // In any other case, we're not dealing with a general-purpose service loader, but rather
+                    // with a specific one, such as DocumentBuilderFactory.newInstance(). In that case the 
+                    // target class is the class that is being invoked on (i.e. DocumentBuilderFactory).
+                    Type type = Type.getObjectType(owner);
+                    mv.visitLdcInsn(type);
+                }
+                mv.visitMethodInsn(INVOKESTATIC, targetClass,
+                        getGeneratedMethodName(wd), "(Ljava/lang/Class;)V");
+
+                super.visitMethodInsn(opcode, owner, name, desc);
+
+                // Add: Util.restoreContextClassloader();
+                mv.visitMethodInsn(INVOKESTATIC, UTIL_CLASS,
+                        "restoreContextClassloader", VOID_RETURN_TYPE);
+            } else {                
+                super.visitMethodInsn(opcode, owner, name, desc);
+            }
+        }
+
+        private WeavingData findWeavingData(String owner, String methodName, String methodDesc) {
+            owner = owner.replace('/', '.');
+
+            Type[] argTypes = Type.getArgumentTypes(methodDesc);
+            String [] argClassNames = new String[argTypes.length];
+            for (int i = 0; i < argTypes.length; i++) {
+                argClassNames[i] = argTypes[i].getClassName();
+            }
+
+            for (WeavingData wd : weavingData) {
+                if (wd.getClassName().equals(owner) &&
+                    wd.getMethodName().equals(methodName) &&
+                    (wd.getArgClasses() != null ? Arrays.equals(argClassNames, wd.getArgClasses()) : true)) {
+                    return wd;
+                }
+            }
+            return null;
+        }
+    }
+
+    public boolean additionalImportRequired() {
+        return additionalImportRequired ;
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/Util.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,111 @@
+/**
+ * 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;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleReference;
+import org.osgi.framework.wiring.BundleWiring;
+import org.osgi.service.log.LogService;
+
+/** 
+ * Methods used from ASM-generated code. They store, change and reset the thread context classloader.
+ * The methods are static to make it easy to access them from generated code.
+ */
+public class Util {
+    static ThreadLocal<ClassLoader> storedClassLoaders = new ThreadLocal<ClassLoader>();
+    
+    public static void storeContextClassloader() {
+        storedClassLoaders.set(Thread.currentThread().getContextClassLoader());
+    }
+    
+    public static void restoreContextClassloader() {
+        Thread.currentThread().setContextClassLoader(storedClassLoaders.get());
+        storedClassLoaders.set(null);
+    }
+        
+    public static void fixContextClassloader(String cls, String method, Class<?> clsArg, ClassLoader bundleLoader) {
+        if (!(bundleLoader instanceof BundleReference)) {
+            Activator.activator.log(LogService.LOG_WARNING, "Classloader of consuming bundle doesn't implement BundleReference: " + bundleLoader);
+            return;
+        }
+
+        BundleReference br = ((BundleReference) bundleLoader);
+        System.out.println("~~~ cls: " + cls + " method: " + method + " clarg:" + clsArg + " cl:" + bundleLoader + " clientBundle: " + br.getBundle().getSymbolicName());        
+        
+        ClassLoader cl = findContextClassloader(br.getBundle(), cls, method, clsArg);
+        if (cl != null) {
+            Activator.activator.log(LogService.LOG_INFO, "Temporarily setting Thread Context Classloader to: " + cl);
+            Thread.currentThread().setContextClassLoader(cl);
+        } else {
+            Activator.activator.log(LogService.LOG_WARNING, "No classloader found for " + cls + ":" + method + "(" + clsArg + ")");
+        }
+    }
+    
+    private static ClassLoader findContextClassloader(Bundle consumerBundle, String className, String methodName, Class<?> clsArg) {
+        Activator activator = Activator.activator;
+        
+        String requestedClass;
+        Map<Pair<Integer, String>, String> args;
+        if (ServiceLoader.class.getName().equals(className) && "load".equals(methodName)) {
+            requestedClass = clsArg.getName();
+            args = new HashMap<Pair<Integer,String>, String>();
+            args.put(new Pair<Integer, String>(0, Class.class.getName()), clsArg.getName());
+        } else {
+            requestedClass = className;
+            args = null; // only supported on ServiceLoader.load() at the moment
+        }
+
+        Collection<Bundle> bundles = new ArrayList<Bundle>(activator.findProviderBundles(requestedClass));
+        activator.log(LogService.LOG_DEBUG, "Found bundles providing " + requestedClass + ": " + bundles);
+                
+        Collection<Bundle> allowedBundles = activator.findConsumerRestrictions(consumerBundle, className, methodName, args);
+
+        if (allowedBundles != null) {
+            for (Iterator<Bundle> it = bundles.iterator(); it.hasNext(); ) {
+                if (!allowedBundles.contains(it.next())) {
+                    it.remove();
+                }
+            }
+        }
+        
+        switch (bundles.size()) {
+        case 0:
+            return null;
+        case 1:
+            Bundle bundle = bundles.iterator().next();
+            BundleWiring wiring = bundle.adapt(BundleWiring.class);
+            return wiring.getClassLoader();            
+        default:
+            List<ClassLoader> loaders = new ArrayList<ClassLoader>();
+            for (Bundle b : bundles) {
+                BundleWiring bw = b.adapt(BundleWiring.class);
+                loaders.add(bw.getClassLoader());
+            }
+            return new MultiDelegationClassloader(loaders.toArray(new ClassLoader[loaders.size()]));
+        }
+    }
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/WeavingData.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,94 @@
+/**
+ * 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;
+
+import java.util.Arrays;
+
+/** Contains information needed for the byte code weaver.
+ */
+public class WeavingData {
+    private final String className;
+    private final String methodName;
+    private final String[] argClasses;
+
+    /**
+     * Constructor.
+     * @param className The class name of the call that needs to be woven.
+     * @param methodName The method name of the call that needs to be woven.
+     * @param argClasses The overload (class names of the signature) of the call
+     * that needs to be woven. If <code>null</code> then all overloads of the method
+     * need to be woven.
+     */
+    public WeavingData(String className, String methodName, String[] argClasses) {
+        this.className = className;
+        this.methodName = methodName;
+        this.argClasses = argClasses;
+    }
+
+    public String getClassName() {
+        return className;
+    }
+
+    public String getMethodName() {
+        return methodName;
+    }
+
+    public String[] getArgClasses() {
+        return argClasses;
+    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + Arrays.hashCode(argClasses);
+        result = prime * result + ((className == null) ? 0 : className.hashCode());
+        result = prime * result + ((methodName == null) ? 0 : methodName.hashCode());
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        
+        if (obj == null)
+            return false;
+        
+        if (getClass() != obj.getClass())
+            return false;
+        
+        WeavingData other = (WeavingData) obj;
+        if (!Arrays.equals(argClasses, other.argClasses))
+            return false;
+        if (className == null) {
+            if (other.className != null)
+                return false;
+        } else if (!className.equals(other.className))
+            return false;
+        if (methodName == null) {
+            if (other.methodName != null)
+                return false;
+        } else if (!methodName.equals(other.methodName))
+            return false;
+        return true;
+    }    
+    
+    
+}

Added: aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java (added)
+++ aries/trunk/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/api/SpiFlyConstants.java Thu Mar  3 09:54:50 2011
@@ -0,0 +1,26 @@
+/**
+ * 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.api;
+
+public interface SpiFlyConstants {
+    String SPI_CONSUMER_HEADER = "SPI-Consumer";
+    String SPI_PROVIDER_HEADER = "SPI-Provider";
+
+    String SPI_PROVIDER_URL = "spi.provider.url";
+}

Added: aries/trunk/spi-fly/spi-fly-deps/pom.xml
URL: http://svn.apache.org/viewvc/aries/trunk/spi-fly/spi-fly-deps/pom.xml?rev=1076571&view=auto
==============================================================================
--- aries/trunk/spi-fly/spi-fly-deps/pom.xml (added)
+++ aries/trunk/spi-fly/spi-fly-deps/pom.xml Thu Mar  3 09:54:50 2011
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+    <!--
+ 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.
+    -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.aries.spifly</groupId>
+        <artifactId>spifly</artifactId>
+        <version>0.4-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>org.apache.aries.spifly.deps</artifactId>
+    <packaging>pom</packaging>
+    <name>Apache Aries SPI Fly Deps</name>
+    <description>
+        This maven module pulls down a dependency that is not available through maven repositories for use as a 'system' scope dependency.
+        This module can go once the OSGi 4.3 core is available through a Maven repository.
+    </description>
+
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-antrun-plugin</artifactId>
+                <executions>
+                    <execution>
+                        <phase>generate-resources</phase>
+                        <configuration>
+                            <tasks>
+                                <!-- Add Ant tasks from the ant-contrib jar -->
+                                <taskdef resource="net/sf/antcontrib/antcontrib.properties" />
+
+                                <if>
+                                    <available file="${basedir}/target/downloaded/org.eclipse.osgi_3.7.0.jar"/>
+                                    <then>
+                                        <echo message="Equinox jar already downloaded, not downloading again"/>
+                                    </then>
+                                    <else>
+                                        <mkdir dir="${basedir}/target/downloaded"/>
+                                        <get src="http://download.eclipse.org/equinox/drops/S-3.7M5-201101272034/org.eclipse.osgi_3.7.0.v20110124-0830.jar"
+                                            dest="${basedir}/target/downloaded/org.eclipse.osgi_3.7.0.jar"
+                                            verbose="true"/>
+                                    </else>
+                                </if>
+                            </tasks>
+                        </configuration>
+                        <goals>
+                            <goal>run</goal>
+                        </goals>
+                    </execution>
+                </executions>
+            </plugin>
+        </plugins>
+    </build>
+</project>