ARIES-1933 manual opt-in

Signed-off-by: Raymond Augé <rotty3000@apache.org>
diff --git a/spi-fly/pom.xml b/spi-fly/pom.xml
index 46b345b..3a49544 100644
--- a/spi-fly/pom.xml
+++ b/spi-fly/pom.xml
@@ -31,7 +31,7 @@
     <groupId>org.apache.aries.spifly</groupId>
     <artifactId>spifly</artifactId>
     <name>Apache Aries SPI Fly</name>
-    <version>1.2.5-SNAPSHOT</version>
+    <version>1.3.0-SNAPSHOT</version>
     <packaging>pom</packaging>
     <description>
         SPI support for OSGi
diff --git a/spi-fly/spi-fly-core/pom.xml b/spi-fly/spi-fly-core/pom.xml
index f2dc9f0..e5715aa 100644
--- a/spi-fly/spi-fly-core/pom.xml
+++ b/spi-fly/spi-fly-core/pom.xml
@@ -30,7 +30,7 @@
 
     <groupId>org.apache.aries.spifly</groupId>
     <artifactId>org.apache.aries.spifly.core-internal</artifactId>
-    <version>1.2.5-SNAPSHOT</version>
+    <version>1.3.0-SNAPSHOT</version>
     <packaging>jar</packaging>
     <name>Apache Aries SPI Fly Core (internal module)</name>
     <description>
diff --git a/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java b/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java
index f295ae4..75e592b 100644
--- a/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java
+++ b/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/BaseActivator.java
@@ -19,6 +19,7 @@
 package org.apache.aries.spifly;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
@@ -28,6 +29,7 @@
 import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
+import java.util.Optional;
 import java.util.ServiceLoader;
 import java.util.Set;
 import java.util.SortedMap;
@@ -45,6 +47,10 @@
 import org.osgi.framework.wiring.BundleWiring;
 import org.osgi.util.tracker.BundleTracker;
 
+import aQute.bnd.header.Parameters;
+import aQute.bnd.stream.MapStream;
+import aQute.libg.glob.Glob;
+
 public abstract class BaseActivator implements BundleActivator {
     private static final Set<WeavingData> NON_WOVEN_BUNDLE = Collections.emptySet();
     private static final Logger logger = Logger.getLogger(BaseActivator.class.getName());
@@ -59,6 +65,8 @@
     private BundleTracker consumerBundleTracker;
     @SuppressWarnings("rawtypes")
     private BundleTracker providerBundleTracker;
+    private Optional<Parameters> autoConsumerInstructions;
+    private Optional<Parameters> autoProviderInstructions;
 
     private final ConcurrentMap<Bundle, Set<WeavingData>> bundleWeavingData =
         new ConcurrentHashMap<Bundle, Set<WeavingData>>();
@@ -73,6 +81,19 @@
     public synchronized void start(BundleContext context, final String consumerHeaderName) throws Exception {
         bundleContext = context;
 
+        try {
+        autoConsumerInstructions = Optional.ofNullable(
+            bundleContext.getProperty("org.apache.aries.spifly.auto.consumers")
+        ).map(Parameters::new);
+
+        autoProviderInstructions = Optional.ofNullable(
+            bundleContext.getProperty("org.apache.aries.spifly.auto.providers")
+        ).map(Parameters::new);
+        }
+        catch (Throwable t) {
+            logger.log(Level.SEVERE, t.getMessage(), t);
+        }
+
         providerBundleTracker = new BundleTracker(context,
                 Bundle.ACTIVE, new ProviderBundleTrackerCustomizer(this, context.getBundle()));
         providerBundleTracker.open();
@@ -95,8 +116,25 @@
         }
 
         Map<String, List<String>> allHeaders = new HashMap<String, List<String>>();
-        allHeaders.put(consumerHeaderName, getAllHeaders(consumerHeaderName, bundle));
-        allHeaders.put(SpiFlyConstants.REQUIRE_CAPABILITY, getAllHeaders(SpiFlyConstants.REQUIRE_CAPABILITY, bundle));
+        Set<String> addedHeaders = new HashSet<String>();
+        List<String> added = allHeaders.put(consumerHeaderName, getAllHeaders(consumerHeaderName, bundle));
+        if (added != null) {
+            added.stream().forEach(addedHeaders::add);
+        }
+        added = allHeaders.put(SpiFlyConstants.REQUIRE_CAPABILITY, getAllHeaders(SpiFlyConstants.REQUIRE_CAPABILITY, bundle));
+        if (added != null) {
+            added.stream().forEach(addedHeaders::add);
+        }
+        if (addedHeaders.isEmpty()) {
+            getAutoConsumerInstructions().map(Parameters::stream).orElseGet(MapStream::empty).filterKey(
+                i -> Glob.toPattern(i).asPredicate().test(bundle.getSymbolicName())
+            ).findFirst().ifPresent(
+                un -> allHeaders.put(
+                    SpiFlyConstants.REQUIRE_CAPABILITY,
+                    Arrays.asList(
+                        SpiFlyConstants.CLIENT_REQUIREMENT.concat(",osgi.serviceloader;filter:='(osgi.serviceloader=*)'")))
+            );
+        }
 
         Set<WeavingData> wd = new HashSet<WeavingData>();
         for (Map.Entry<String, List<String>> entry : allHeaders.entrySet()) {
@@ -217,12 +255,20 @@
     }
 
     public void registerProviderBundle(String registrationClassName, Bundle bundle, Map<String, Object> customAttributes) {
-        registrationClassName = registrationClassName.trim();
-        registeredProviders.putIfAbsent(registrationClassName,
-                Collections.synchronizedSortedMap(new TreeMap<Long, Pair<Bundle, Map<String, Object>>>()));
+        SortedMap<Long, Pair<Bundle, Map<String, Object>>> map = registeredProviders.computeIfAbsent(registrationClassName,
+            k -> Collections.synchronizedSortedMap(new TreeMap<Long, Pair<Bundle, Map<String, Object>>>()));
 
-        SortedMap<Long, Pair<Bundle, Map<String, Object>>> map = registeredProviders.get(registrationClassName);
-        map.put(bundle.getBundleId(), new Pair<Bundle, Map<String, Object>>(bundle, customAttributes));
+        map.compute(
+            bundle.getBundleId(),
+            (k,v) -> {
+                if (v == null) {
+                    return new Pair<Bundle, Map<String, Object>>(bundle, customAttributes);
+                }
+                else {
+                    v.getRight().putAll(customAttributes);
+                    return v;
+                }
+            });
     }
 
     public void unregisterProviderBundle(Bundle bundle) {
@@ -288,6 +334,24 @@
         return Collections.emptySet();
     }
 
+    public Optional<Parameters> getAutoConsumerInstructions() {
+        if (autoConsumerInstructions == null) return Optional.empty();
+        return autoConsumerInstructions;
+    }
+
+    public void setAutoConsumerInstructions(Optional<Parameters> autoConsumerInstructions) {
+        this.autoConsumerInstructions = autoConsumerInstructions;
+    }
+
+    public Optional<Parameters> getAutoProviderInstructions() {
+        if (autoProviderInstructions == null) return Optional.empty();
+        return autoProviderInstructions;
+    }
+
+    public void setAutoProviderInstructions(Optional<Parameters> autoProviderInstructions) {
+        this.autoProviderInstructions = autoProviderInstructions;
+    }
+
     private Collection<Bundle> getBundles(List<BundleDescriptor> descriptors, String className, String methodName,
             Map<Pair<Integer, String>, String> args) {
         if (descriptors == null) {
diff --git a/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java b/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java
index 9a50b48..28b9b9a 100644
--- a/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java
+++ b/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizer.java
@@ -18,23 +18,27 @@
  */

 package org.apache.aries.spifly;

 

+import static java.util.stream.Collectors.toList;

+

 import java.io.BufferedReader;

 import java.io.IOException;

 import java.io.InputStreamReader;

 import java.net.URL;

+import java.util.AbstractMap;

 import java.util.ArrayList;

 import java.util.Arrays;

 import java.util.Collections;

-import java.util.Dictionary;

 import java.util.Enumeration;

 import java.util.HashMap;

 import java.util.Hashtable;

 import java.util.List;

 import java.util.Map;

 import java.util.Map.Entry;

+import java.util.Objects;

 import java.util.jar.JarEntry;

 import java.util.jar.JarInputStream;

 import java.util.logging.Level;

+import java.util.stream.Stream;

 

 import org.osgi.framework.Bundle;

 import org.osgi.framework.BundleEvent;

@@ -50,6 +54,8 @@
 import aQute.bnd.header.Attrs;

 import aQute.bnd.header.OSGiHeader;

 import aQute.bnd.header.Parameters;

+import aQute.bnd.stream.MapStream;

+import aQute.libg.glob.Glob;

 

 /**

  * Listens for new bundles being installed and registers them as service providers if applicable.

@@ -57,6 +63,9 @@
 @SuppressWarnings("rawtypes")

 public class ProviderBundleTrackerCustomizer implements BundleTrackerCustomizer {

     private static final String METAINF_SERVICES = "META-INF/services";

+    private static final List<String> MERGE_HEADERS = Arrays.asList(

+        Constants.IMPORT_PACKAGE, Constants.REQUIRE_BUNDLE, Constants.EXPORT_PACKAGE,

+        Constants.PROVIDE_CAPABILITY, Constants.REQUIRE_CAPABILITY);

 

     final BaseActivator activator;

     final Bundle spiBundle;

@@ -68,12 +77,13 @@
 

     @Override

     public List<ServiceRegistration> addingBundle(final Bundle bundle, BundleEvent event) {

-        log(Level.FINE, "Bundle Considered for SPI providers: "

-                + bundle.getSymbolicName());

-

         if (bundle.equals(spiBundle))

             return null; // don't process the SPI bundle itself

 

+        log(Level.FINE, "Bundle Considered for SPI providers: "

+            + bundle.getSymbolicName());

+

+        DiscoveryMode discoveryMode = DiscoveryMode.SERVICELOADER_CAPABILITIES;

         List<String> providedServices = null;

         Map<String, Object> customAttributes = new HashMap<String, Object>();

         if (bundle.getHeaders().get(SpiFlyConstants.REQUIRE_CAPABILITY) != null) {

@@ -84,22 +94,28 @@
             }

         }

 

-        boolean fromSPIProviderHeader = false;

         String spiProviderHeader = getHeaderFromBundleOrFragment(bundle, SpiFlyConstants.SPI_PROVIDER_HEADER);

         if (providedServices == null && spiProviderHeader != null) {

             String header = spiProviderHeader.trim();

             if ("*".equals(header)) {

                 providedServices = new ArrayList<String>();

             } else {

-                providedServices = Arrays.asList(header.split(","));

+                providedServices = Stream.of(header.split(",")).map(String::trim).collect(toList());

             }

-            fromSPIProviderHeader = true;

+            discoveryMode = DiscoveryMode.SPI_PROVIDER_HEADER;

+        }

+

+        List<URL> serviceFileURLs = null;

+        if (providedServices == null) {

+            Entry<List<String>, List<URL>> autoServices = getFromAutoProviderProperty(bundle, customAttributes);

+

+            providedServices = autoServices.getKey();

+            serviceFileURLs = autoServices.getValue();

+            discoveryMode = DiscoveryMode.AUTO_PROVIDERS_PROPERTY;

         }

 

         if (providedServices == null) {

-            log(Level.FINE, "No '"

-                    + SpiFlyConstants.SPI_PROVIDER_HEADER

-                    + "' Manifest header. Skipping bundle: "

+            log(Level.FINE, "No provided SPI services. Skipping bundle: "

                     + bundle.getSymbolicName());

             return null;

         } else {

@@ -107,33 +123,65 @@
                     + bundle.getSymbolicName());

         }

 

-        for (String svc : providedServices) {

+        for (String serviceType : providedServices) {

             // Eagerly register any services that are explicitly listed, as they may not be found in META-INF/services

-            activator.registerProviderBundle(svc, bundle, customAttributes);

+            activator.registerProviderBundle(serviceType, bundle, customAttributes);

         }

 

-        List<URL> serviceFileURLs = new ArrayList<URL>();

-

-        Enumeration<URL> entries = bundle.findEntries(METAINF_SERVICES, "*", false);

-        if (entries != null) {

-            serviceFileURLs.addAll(Collections.list(entries));

-        }

-

-        Object bcp = bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH);

-        if (bcp instanceof String) {

-            for (String entry : ((String) bcp).split(",")) {

-                entry = entry.trim();

-                if (entry.equals("."))

-                    continue;

-

-                URL url = bundle.getResource(entry);

-                if (url != null) {

-                    serviceFileURLs.addAll(getMetaInfServiceURLsFromJar(url));

-                }

-            }

+        if (serviceFileURLs == null) {

+            serviceFileURLs = getServiceFileUrls(bundle);

         }

 

         final List<ServiceRegistration> registrations = new ArrayList<ServiceRegistration>();

+        for (ServiceDetails details : collectServiceDetails(bundle, serviceFileURLs, discoveryMode)) {

+            if (providedServices.size() > 0 && !providedServices.contains(details.serviceType))

+                continue;

+

+            try {

+                final Class<?> cls = bundle.loadClass(details.instanceType);

+                log(Level.INFO, "Loaded SPI provider: " + cls);

+

+                if (details.properties != null) {

+                    ServiceRegistration reg = null;

+                    Object instance =

+                        (details.properties.containsKey("service.scope") &&

+                        "prototype".equalsIgnoreCase(String.valueOf(details.properties.get("service.scope")))) ?

+                            new ProviderPrototypeServiceFactory(cls) :

+                            new ProviderServiceFactory(cls);

+

+                    SecurityManager sm = System.getSecurityManager();

+                    if (sm != null) {

+                        if (bundle.hasPermission(new ServicePermission(details.serviceType, ServicePermission.REGISTER))) {

+                            reg = bundle.getBundleContext().registerService(

+                                    details.serviceType, instance, details.properties);

+                        } else {

+                            log(Level.INFO, "Bundle " + bundle + " does not have the permission to register services of type: " + details.serviceType);

+                        }

+                    } else {

+                        reg = bundle.getBundleContext().registerService(

+                            details.serviceType, instance, details.properties);

+                    }

+

+                    if (reg != null) {

+                        registrations.add(reg);

+                        log(Level.INFO, "Registered service: " + reg);

+                    }

+                }

+

+                activator.registerProviderBundle(details.serviceType, bundle, details.properties);

+                log(Level.INFO, "Registered provider " + details.instanceType + " of service " + details.serviceType + " in bundle " + bundle.getSymbolicName());

+            } catch (Exception e) {

+                log(Level.WARNING,

+                    "Could not load provider " + details.instanceType + " of service " + details.serviceType, e);

+            }

+        }

+

+        return registrations;

+    }

+

+    private List<ServiceDetails> collectServiceDetails(Bundle bundle, List<URL> serviceFileURLs, DiscoveryMode discoveryMode) {

+        List<ServiceDetails> serviceDetails = new ArrayList<>();

+

         for (URL serviceFileURL : serviceFileURLs) {

             log(Level.INFO, "Found SPI resource: " + serviceFileURL);

 

@@ -158,48 +206,30 @@
                             registrationClassName = serviceFile.substring(idx + 1);

                         }

 

-                        if (providedServices.size() > 0 && !providedServices.contains(registrationClassName))

-                            continue;

-

-                        final Class<?> cls = bundle.loadClass(className);

-                        log(Level.INFO, "Loaded SPI provider: " + cls);

-

                         final Hashtable<String, Object> properties;

-                        if (fromSPIProviderHeader)

+                        if (discoveryMode == DiscoveryMode.SPI_PROVIDER_HEADER) {

                             properties = new Hashtable<String, Object>();

-                        else

-                            properties = findServiceRegistrationProperties(bundle.getHeaders(), registrationClassName, className);

+                        }

+                        else if (discoveryMode == DiscoveryMode.AUTO_PROVIDERS_PROPERTY) {

+                            properties = activator.getAutoProviderInstructions().map(

+                                Parameters::stream

+                            ).orElseGet(MapStream::empty).filterKey(

+                                i -> Glob.toPattern(i).asPredicate().test(bundle.getSymbolicName())

+                            ).values().findFirst().map(

+                                Hashtable<String, Object>::new

+                            ).orElseGet(() -> new Hashtable<String, Object>());

+                        }

+                        else {

+                            properties = findServiceRegistrationProperties(bundle, registrationClassName, className);

+                        }

 

                         if (properties != null) {

                             properties.put(SpiFlyConstants.SERVICELOADER_MEDIATOR_PROPERTY, spiBundle.getBundleId());

-                            properties.put(SpiFlyConstants.PROVIDER_IMPLCLASS_PROPERTY, cls.getName());

-

-                            ServiceRegistration reg = null;

-                            Object instance = (properties.containsKey("service.scope") && "prototype".equals(properties.get("service.scope"))) ?

-                                new ProviderPrototypeServiceFactory(cls) :

-                                new ProviderServiceFactory(cls);

-

-                            SecurityManager sm = System.getSecurityManager();

-                            if (sm != null) {

-                                if (bundle.hasPermission(new ServicePermission(registrationClassName, ServicePermission.REGISTER))) {

-                                    reg = bundle.getBundleContext().registerService(

-                                            registrationClassName, instance, properties);

-                                } else {

-                                    log(Level.INFO, "Bundle " + bundle + " does not have the permission to register services of type: " + registrationClassName);

-                                }

-                            } else {

-                                reg = bundle.getBundleContext().registerService(

-                                        registrationClassName, instance, properties);

-                            }

-

-                            if (reg != null) {

-                                registrations.add(reg);

-                                log(Level.INFO, "Registered service: " + reg);

-                            }

+                            properties.put(SpiFlyConstants.PROVIDER_IMPLCLASS_PROPERTY, className);

+                            properties.put(SpiFlyConstants.PROVIDER_DISCOVERY_MODE, discoveryMode.toString());

                         }

 

-                        activator.registerProviderBundle(registrationClassName, bundle, customAttributes);

-                        log(Level.INFO, "Registered provider: " + registrationClassName + " in bundle " + bundle.getSymbolicName());

+                        serviceDetails.add(new ServiceDetails(registrationClassName, className, properties));

                     } catch (Exception e) {

                         log(Level.WARNING,

                                 "Could not load SPI implementation referred from " + serviceFileURL, e);

@@ -210,7 +240,53 @@
             }

         }

 

-        return registrations;

+        return serviceDetails;

+    }

+    private Entry<List<String>, List<URL>> getFromAutoProviderProperty(Bundle bundle, Map<String, Object> customAttributes) {

+        return activator.getAutoProviderInstructions().map(

+            Parameters::stream

+        ).orElseGet(MapStream::empty).filterKey(

+            i -> Glob.toPattern(i).asPredicate().test(bundle.getSymbolicName())

+        ).values().findFirst().map(

+            un -> {

+                List<URL> serviceFileURLs = getServiceFileUrls(bundle);

+

+                List<ServiceDetails> collectServiceDetails = collectServiceDetails(bundle, serviceFileURLs, DiscoveryMode.AUTO_PROVIDERS_PROPERTY);

+

+                collectServiceDetails.stream().map(ServiceDetails::getProperties).filter(Objects::nonNull).forEach(

+                    hashtable -> hashtable.forEach(customAttributes::put)

+                );

+

+                List<String> providedServices = collectServiceDetails.stream().map(ServiceDetails::getServiceType).collect(toList());

+

+                return new AbstractMap.SimpleImmutableEntry<>(providedServices, serviceFileURLs);

+            }

+        ).orElseGet(() -> new AbstractMap.SimpleImmutableEntry<>(null, null));

+    }

+

+    private List<URL> getServiceFileUrls(Bundle bundle) {

+        List<URL> serviceFileURLs = new ArrayList<URL>();

+

+        Enumeration<URL> entries = bundle.findEntries(METAINF_SERVICES, "*", false);

+        if (entries != null) {

+            serviceFileURLs.addAll(Collections.list(entries));

+        }

+

+        Object bcp = bundle.getHeaders().get(Constants.BUNDLE_CLASSPATH);

+        if (bcp instanceof String) {

+            for (String entry : ((String) bcp).split(",")) {

+                entry = entry.trim();

+                if (entry.equals("."))

+                    continue;

+

+                URL url = bundle.getResource(entry);

+                if (url != null) {

+                    serviceFileURLs.addAll(getMetaInfServiceURLsFromJar(url));

+                }

+            }

+        }

+

+        return serviceFileURLs;

     }

 

     private String getHeaderFromBundleOrFragment(Bundle bundle, String headerName) {

@@ -218,9 +294,10 @@
     }

 

     private String getHeaderFromBundleOrFragment(Bundle bundle, String headerName, String matchString) {

-        String val = bundle.getHeaders().get(headerName);

-        if (matches(val, matchString))

-            return val;

+        Parameters headerParameters = new Parameters(bundle.getHeaders().get(headerName));

+        if (matches(headerParameters.toString(), matchString) && !MERGE_HEADERS.contains(headerName)) {

+            return headerParameters.isEmpty() ? null : headerParameters.toString();

+        }

 

         BundleRevision rev = bundle.adapt(BundleRevision.class);

         if (rev != null) {

@@ -228,15 +305,22 @@
             if (wiring != null) {

                 for (BundleWire wire : wiring.getProvidedWires("osgi.wiring.host")) {

                     Bundle fragment = wire.getRequirement().getRevision().getBundle();

-                    val = fragment.getHeaders().get(headerName);

-                    if (matches(val, matchString)) {

-                        return val;

+                    Parameters fragmentParameters = new Parameters(fragment.getHeaders().get(headerName));

+                    if (MERGE_HEADERS.contains(headerName)) {

+                        headerParameters.mergeWith(fragmentParameters, false);

+                    }

+                    else {

+                        headerParameters = fragmentParameters;

+                    }

+

+                    if (matches(headerParameters.toString(), matchString)) {

+                        return headerParameters.toString();

                     }

                 }

             }

         }

 

-        return null;

+        return headerParameters.isEmpty() ? null : headerParameters.toString();

     }

 

     private boolean matches(String val, String matchString) {

@@ -275,7 +359,7 @@
         for (Entry<String, ? extends Map<String, String>> serviceLoaderCapability : ConsumerHeaderProcessor.findAllMetadata(capabilities, SpiFlyConstants.SERVICELOADER_CAPABILITY_NAMESPACE)) {

             for (Entry<String, String> entry : serviceLoaderCapability.getValue().entrySet()) {

                 if (SpiFlyConstants.SERVICELOADER_CAPABILITY_NAMESPACE.equals(entry.getKey())) {

-                    serviceNames.add(entry.getValue().toString());

+                    serviceNames.add(entry.getValue().trim());

                     continue;

                 }

                 if (SpiFlyConstants.REGISTER_DIRECTIVE.equals(entry.getKey()) && entry.getValue().equals("")) {

@@ -290,8 +374,8 @@
 

     // null means don't register,

     // otherwise the return value should be taken as the service registration properties

-    private Hashtable<String, Object> findServiceRegistrationProperties(Dictionary<?,?> headers, String spiName, String implName) {

-        Object capabilityHeader = headers.get(SpiFlyConstants.PROVIDE_CAPABILITY);

+    private Hashtable<String, Object> findServiceRegistrationProperties(Bundle bundle, String spiName, String implName) {

+        Object capabilityHeader = getHeaderFromBundleOrFragment(bundle, SpiFlyConstants.PROVIDE_CAPABILITY);

         if (capabilityHeader == null)

             return null;

 

@@ -354,7 +438,8 @@
 

     @Override

     public void modifiedBundle(Bundle bundle, BundleEvent event, Object registrations) {

-        // should really be doing something here...

+        removedBundle(bundle, event, registrations);

+        addingBundle(bundle, event);

     }

 

     @Override

@@ -378,4 +463,36 @@
     private void log(Level level, String message, Throwable th) {

         activator.log(level, message, th);

     }

+

+    enum DiscoveryMode {

+        SPI_PROVIDER_HEADER,

+        AUTO_PROVIDERS_PROPERTY,

+        SERVICELOADER_CAPABILITIES

+    }

+

+    class ServiceDetails {

+        public ServiceDetails(String serviceType, String instanceType, Hashtable<String, Object> properties) {

+            this.serviceType = serviceType;

+            this.instanceType = instanceType;

+            this.properties = properties;

+        }

+        public String getInstanceType() {

+            return instanceType;

+        }

+        public Hashtable<String, Object> getProperties() {

+            return properties;

+        }

+        public String getServiceType() {

+            return serviceType;

+        }

+        @Override

+        public String toString() {

+            return String.format(

+                "ServiceDetails [serviceType=\"%s\", instanceType=\"%s\", properties=%s]",

+                getServiceType(), getInstanceType(), getProperties());

+        }

+        private final String instanceType;

+        private final Hashtable<String, Object> properties;

+        private final String serviceType;

+    }

 }

diff --git a/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/SpiFlyConstants.java b/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/SpiFlyConstants.java
index 629c2fa..afdf77b 100644
--- a/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/SpiFlyConstants.java
+++ b/spi-fly/spi-fly-core/src/main/java/org/apache/aries/spifly/SpiFlyConstants.java
@@ -41,6 +41,7 @@
     // Service registration property

     String SERVICELOADER_MEDIATOR_PROPERTY = "serviceloader.mediator";

     String PROVIDER_IMPLCLASS_PROPERTY = ".org.apache.aries.spifly.provider.implclass";

+    String PROVIDER_DISCOVERY_MODE = ".org.apache.aries.spifly.provider.discovery.mode";

 

     // The names of the extenders involved

     String PROCESSOR_EXTENDER_NAME = "osgi.serviceloader.processor";

diff --git a/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java b/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java
index d8c6ea0..9740260 100644
--- a/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java
+++ b/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerGenericCapabilityTest.java
@@ -35,6 +35,7 @@
 import java.util.Hashtable;

 import java.util.List;

 import java.util.Map;

+import java.util.Optional;

 import java.util.Set;

 

 import org.apache.aries.mytest.MySPI;

@@ -56,6 +57,8 @@
 import org.osgi.framework.wiring.BundleWire;

 import org.osgi.framework.wiring.BundleWiring;

 

+import aQute.bnd.header.Parameters;

+

 public class ProviderBundleTrackerCustomizerGenericCapabilityTest {

     @Test

     public void testAddingRemovedBundle() throws Exception {

@@ -190,11 +193,109 @@
         assertSame(implBundle, bundles.iterator().next());

 

         Map<String, Object> attrs = activator.getCustomBundleAttributes("org.apache.aries.mytest.MySPI", implBundle);

-        assertEquals(1, attrs.size());

+        assertEquals(4, attrs.size());

         assertEquals("yeah", attrs.get("approval"));

     }

 

     @Test

+    public void testAutoProviderSystemProperty() throws Exception {

+        Bundle mediatorBundle = EasyMock.createMock(Bundle.class);

+        EasyMock.expect(mediatorBundle.getBundleId()).andReturn(42l).anyTimes();

+        EasyMock.replay(mediatorBundle);

+        BaseActivator activator = new BaseActivator() {

+            @Override

+            public void start(BundleContext context) throws Exception {}

+        };

+

+        activator.setAutoProviderInstructions(Optional.of(new Parameters("*")));

+        ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, mediatorBundle);

+

+        @SuppressWarnings("rawtypes")

+        ServiceRegistration sreg = EasyMock.createMock(ServiceRegistration.class);

+        EasyMock.replay(sreg);

+

+        BundleContext implBC = mockSPIBundleContext(sreg);

+        Dictionary<String, String> headers = new Hashtable<String, String>();

+        Bundle implBundle = mockSPIBundle(implBC, headers);

+

+        @SuppressWarnings("rawtypes")

+        List<ServiceRegistration> registrations = customizer.addingBundle(implBundle, null);

+        assertEquals(1, registrations.size());

+        Collection<Bundle> bundles = activator.findProviderBundles("org.apache.aries.mytest.MySPI");

+        assertEquals(1, bundles.size());

+        assertSame(implBundle, bundles.iterator().next());

+

+        Map<String, Object> attrs = activator.getCustomBundleAttributes("org.apache.aries.mytest.MySPI", implBundle);

+        assertEquals(3, attrs.size());

+        assertNull(attrs.get("approval"));

+    }

+

+    @Test

+    public void testAutoProviderSystemPropertyPlusProperty() throws Exception {

+        Bundle mediatorBundle = EasyMock.createMock(Bundle.class);

+        EasyMock.expect(mediatorBundle.getBundleId()).andReturn(42l).anyTimes();

+        EasyMock.replay(mediatorBundle);

+        BaseActivator activator = new BaseActivator() {

+            @Override

+            public void start(BundleContext context) throws Exception {}

+        };

+

+        activator.setAutoProviderInstructions(Optional.of(new Parameters("*;approval=yeah")));

+        ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, mediatorBundle);

+

+        @SuppressWarnings("rawtypes")

+        ServiceRegistration sreg = EasyMock.createMock(ServiceRegistration.class);

+        EasyMock.replay(sreg);

+

+        BundleContext implBC = mockSPIBundleContext(sreg);

+        Dictionary<String, String> headers = new Hashtable<String, String>();

+        Bundle implBundle = mockSPIBundle(implBC, headers);

+

+        @SuppressWarnings("rawtypes")

+        List<ServiceRegistration> registrations = customizer.addingBundle(implBundle, null);

+        assertEquals(1, registrations.size());

+        Collection<Bundle> bundles = activator.findProviderBundles("org.apache.aries.mytest.MySPI");

+        assertEquals(1, bundles.size());

+        assertSame(implBundle, bundles.iterator().next());

+

+        Map<String, Object> attrs = activator.getCustomBundleAttributes("org.apache.aries.mytest.MySPI", implBundle);

+        assertEquals(4, attrs.size());

+        assertEquals("yeah", attrs.get("approval"));

+    }

+

+    @Test

+    public void testAutoProviderSystemPropertyTargetBundle() throws Exception {

+        Bundle mediatorBundle = EasyMock.createMock(Bundle.class);

+        EasyMock.expect(mediatorBundle.getBundleId()).andReturn(42l).anyTimes();

+        EasyMock.replay(mediatorBundle);

+        BaseActivator activator = new BaseActivator() {

+            @Override

+            public void start(BundleContext context) throws Exception {}

+        };

+

+        activator.setAutoProviderInstructions(Optional.of(new Parameters("bsn")));

+        ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, mediatorBundle);

+

+        @SuppressWarnings("rawtypes")

+        ServiceRegistration sreg = EasyMock.createMock(ServiceRegistration.class);

+        EasyMock.replay(sreg);

+

+        BundleContext implBC = mockSPIBundleContext(sreg);

+        Dictionary<String, String> headers = new Hashtable<String, String>();

+        Bundle implBundle = mockSPIBundle(implBC, headers);

+

+        @SuppressWarnings("rawtypes")

+        List<ServiceRegistration> registrations = customizer.addingBundle(implBundle, null);

+        assertEquals(1, registrations.size());

+        Collection<Bundle> bundles = activator.findProviderBundles("org.apache.aries.mytest.MySPI");

+        assertEquals(1, bundles.size());

+        assertSame(implBundle, bundles.iterator().next());

+

+        Map<String, Object> attrs = activator.getCustomBundleAttributes("org.apache.aries.mytest.MySPI", implBundle);

+        assertEquals(3, attrs.size());

+    }

+

+    @Test

     public void testNonServiceRegistryBundle() throws Exception {

         Bundle mediatorBundle = EasyMock.createMock(Bundle.class);

         EasyMock.expect(mediatorBundle.getBundleId()).andReturn(42l).anyTimes();

@@ -501,6 +602,7 @@
         Bundle implBundle = EasyMock.createNiceMock(Bundle.class);

         EasyMock.expect(implBundle.getBundleContext()).andReturn(implBC).anyTimes();

         EasyMock.expect(implBundle.getHeaders()).andReturn(headers).anyTimes();

+        EasyMock.expect(implBundle.getSymbolicName()).andReturn("bsn").anyTimes();

 

         // List the resources found at META-INF/services in the test bundle

         URL dir = getClass().getResource("impl1/META-INF/services");

diff --git a/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest.java b/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest.java
index 0381ad4..ae7a6b5 100644
--- a/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest.java
+++ b/spi-fly/spi-fly-core/src/test/java/org/apache/aries/spifly/ProviderBundleTrackerCustomizerTest.java
@@ -41,15 +41,17 @@
 import org.osgi.framework.ServiceRegistration;

 

 public class ProviderBundleTrackerCustomizerTest {

+

+    private BaseActivator activator = new BaseActivator() {

+        @Override

+        public void start(BundleContext context) throws Exception {}

+    };

+

     @Test

     public void testAddingRemovedBundle() throws Exception {

         Bundle mediatorBundle = EasyMock.createMock(Bundle.class);

         EasyMock.expect(mediatorBundle.getBundleId()).andReturn(42l).anyTimes();

         EasyMock.replay(mediatorBundle);

-        BaseActivator activator = new BaseActivator() {

-            @Override

-            public void start(BundleContext context) throws Exception {}

-        };

 

         ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, mediatorBundle);

 

@@ -82,7 +84,7 @@
         BundleContext implBC = mockSPIBundleContext(EasyMock.createNiceMock(ServiceRegistration.class));

         Bundle spiBundle = mockSPIBundle(implBC);

 

-        ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(EasyMock.createNiceMock(BaseActivator.class), spiBundle);

+        ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, spiBundle);

         assertNull("The SpiFly bundle itself should be ignored", customizer.addingBundle(spiBundle, null));

     }

 

@@ -91,7 +93,7 @@
         BundleContext implBC = mockSPIBundleContext(EasyMock.createNiceMock(ServiceRegistration.class));

         Bundle implBundle = mockSPIBundle(implBC, null);

 

-        ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(EasyMock.createNiceMock(BaseActivator.class), null);

+        ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, null);

         assertNull("Bundle doesn't opt-in so should be ignored", customizer.addingBundle(implBundle, null));

     }

 

@@ -101,10 +103,6 @@
         Bundle mediatorBundle = EasyMock.createMock(Bundle.class);

         EasyMock.expect(mediatorBundle.getBundleId()).andReturn(42l).anyTimes();

         EasyMock.replay(mediatorBundle);

-        BaseActivator activator = new BaseActivator() {

-            @Override

-            public void start(BundleContext context) throws Exception {}

-        };

 

         ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, mediatorBundle);

 

@@ -165,7 +163,7 @@
         EasyMock.expect(spiBundle.getBundleId()).andReturn(25l).anyTimes();

         EasyMock.replay(spiBundle);

 

-        ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(EasyMock.createNiceMock(BaseActivator.class), spiBundle);

+        ProviderBundleTrackerCustomizer customizer = new ProviderBundleTrackerCustomizer(activator, spiBundle);

         assertEquals(2, customizer.addingBundle(implBundle, null).size());

     }

 

diff --git a/spi-fly/spi-fly-dynamic-bundle/pom.xml b/spi-fly/spi-fly-dynamic-bundle/pom.xml
index f0d8f7e..5a596b6 100644
--- a/spi-fly/spi-fly-dynamic-bundle/pom.xml
+++ b/spi-fly/spi-fly-dynamic-bundle/pom.xml
@@ -30,7 +30,7 @@
 
     <groupId>org.apache.aries.spifly</groupId>
     <artifactId>org.apache.aries.spifly.dynamic.bundle</artifactId>
-    <version>1.2.5-SNAPSHOT</version>
+    <version>1.3.0-SNAPSHOT</version>
     <packaging>bundle</packaging>
     <name>Apache Aries SPI Fly Dynamic Weaving Bundle</name>
     <description>
diff --git a/spi-fly/spi-fly-dynamic-bundle/resolve.bndrun b/spi-fly/spi-fly-dynamic-bundle/resolve.bndrun
index cfa422e..5682aa8 100644
--- a/spi-fly/spi-fly-dynamic-bundle/resolve.bndrun
+++ b/spi-fly/spi-fly-dynamic-bundle/resolve.bndrun
@@ -15,4 +15,4 @@
 -runee: JavaSE-1.8
 -runfw: org.apache.felix.framework
 -runrequires: osgi.identity;filter:='(osgi.identity=${project.artifactId})'
--runbundles: org.apache.aries.spifly.dynamic.bundle;version='[1.2.4,1.2.5)'
+-runbundles: org.apache.aries.spifly.dynamic.bundle;version='[1.3.0,1.3.1)'
diff --git a/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java b/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java
index dd9525a..2afd2c8 100644
--- a/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java
+++ b/spi-fly/spi-fly-dynamic-bundle/src/test/java/org/apache/aries/spifly/dynamic/ClientWeavingHookGenericCapabilityTest.java
@@ -39,6 +39,7 @@
 import java.util.Hashtable;

 import java.util.List;

 import java.util.Map;

+import java.util.Optional;

 import java.util.Set;

 import java.util.concurrent.ConcurrentHashMap;

 

@@ -62,6 +63,8 @@
 import org.osgi.framework.wiring.BundleWire;

 import org.osgi.framework.wiring.BundleWiring;

 

+import aQute.bnd.header.Parameters;

+

 public class ClientWeavingHookGenericCapabilityTest {

     DynamicWeavingActivator activator;

 

@@ -78,6 +81,74 @@
     }

 

     @Test

+    public void testAutoConsumerSystemProperty() throws Exception {

+        Dictionary<String, String> consumerHeaders = new Hashtable<String, String>();

+

+        // Register the bundle that provides the SPI implementation.

+        Bundle providerBundle = mockProviderBundle("impl1", 1);

+        activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle, new HashMap<String, Object>());

+

+        Bundle consumerBundle = mockConsumerBundle(consumerHeaders, providerBundle);

+        activator.setAutoConsumerInstructions(Optional.of(new Parameters(consumerBundle.getSymbolicName())));

+        activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY);

+

+        Bundle spiFlyBundle = mockSpiFlyBundle("spifly", Version.parseVersion("1.9.4"), consumerBundle, providerBundle);

+        WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator);

+

+        // Weave the TestClient class.

+        URL clsUrl = getClass().getResource("TestClient.class");

+        Assert.assertNotNull("Precondition", clsUrl);

+

+        String clientClassName = "org.apache.aries.spifly.dynamic.TestClient";

+        WovenClass wc = new MyWovenClass(clsUrl, clientClassName, consumerBundle);

+        Assert.assertEquals("Precondition", 0, wc.getDynamicImports().size());

+        wh.weave(wc);

+        Assert.assertEquals(1, wc.getDynamicImports().size());

+        String di1 = "org.apache.aries.spifly";

+        String di = wc.getDynamicImports().get(0);

+        Assert.assertTrue("Weaving should have added a dynamic import", di1.equals(di));

+

+        // Invoke the woven class and check that it properly sets the TCCL so that the

+        // META-INF/services/org.apache.aries.mytest.MySPI file from impl1 is visible.

+        Class<?> cls = wc.getDefinedClass();

+        Method method = cls.getMethod("test", new Class [] {String.class});

+        Object result = method.invoke(cls.newInstance(), "hello");

+        Assert.assertEquals(Collections.singleton("olleh"), result);

+    }

+

+    @Test

+    public void testAutoConsumerSystemProperty_negative() throws Exception {

+        Dictionary<String, String> consumerHeaders = new Hashtable<String, String>();

+

+        // Register the bundle that provides the SPI implementation.

+        Bundle providerBundle = mockProviderBundle("impl1", 1);

+        activator.registerProviderBundle("org.apache.aries.mytest.MySPI", providerBundle, new HashMap<String, Object>());

+

+        Bundle consumerBundle = mockConsumerBundle(consumerHeaders, providerBundle);

+        activator.addConsumerWeavingData(consumerBundle, SpiFlyConstants.REQUIRE_CAPABILITY);

+

+        Bundle spiFlyBundle = mockSpiFlyBundle("spifly", Version.parseVersion("1.9.4"), consumerBundle, providerBundle);

+        WeavingHook wh = new ClientWeavingHook(spiFlyBundle.getBundleContext(), activator);

+

+        // Weave the TestClient class.

+        URL clsUrl = getClass().getResource("TestClient.class");

+        Assert.assertNotNull("Precondition", clsUrl);

+

+        String clientClassName = "org.apache.aries.spifly.dynamic.TestClient";

+        WovenClass wc = new MyWovenClass(clsUrl, clientClassName, consumerBundle);

+        Assert.assertEquals("Precondition", 0, wc.getDynamicImports().size());

+        wh.weave(wc);

+        Assert.assertEquals(0, wc.getDynamicImports().size());

+

+        // Invoke the woven class and check that it properly sets the TCCL so that the

+        // META-INF/services/org.apache.aries.mytest.MySPI file from impl1 is visible.

+        Class<?> cls = wc.getDefinedClass();

+        Method method = cls.getMethod("test", new Class [] {String.class});

+        Object result = method.invoke(cls.newInstance(), "hello");

+        Assert.assertEquals(Collections.emptySet(), result);

+    }

+

+    @Test

     public void testBasicServiceLoaderUsage() throws Exception {

         Dictionary<String, String> consumerHeaders = new Hashtable<String, String>();

         consumerHeaders.put(SpiFlyConstants.REQUIRE_CAPABILITY, SpiFlyConstants.CLIENT_REQUIREMENT);

diff --git a/spi-fly/spi-fly-dynamic-framework-extension/pom.xml b/spi-fly/spi-fly-dynamic-framework-extension/pom.xml
index 6d27954..97b4680 100644
--- a/spi-fly/spi-fly-dynamic-framework-extension/pom.xml
+++ b/spi-fly/spi-fly-dynamic-framework-extension/pom.xml
@@ -30,7 +30,7 @@
 
     <groupId>org.apache.aries.spifly</groupId>
     <artifactId>org.apache.aries.spifly.dynamic.framework.extension</artifactId>
-    <version>1.2.5-SNAPSHOT</version>
+    <version>1.3.0-SNAPSHOT</version>
     <packaging>bundle</packaging>
     <name>Apache Aries SPI Fly Dynamic Weaving Framework Extension</name>
     <description>
@@ -49,7 +49,7 @@
     <properties>
         <asm.version>7.3.1</asm.version>
         <maven.bundle.plugin.version>4.2.0</maven.bundle.plugin.version>
-        <bnd.version>5.0.0</bnd.version>
+        <bnd.version>5.0.1</bnd.version>
     </properties>
 
     <dependencies>
diff --git a/spi-fly/spi-fly-dynamic-framework-extension/resolve.bndrun b/spi-fly/spi-fly-dynamic-framework-extension/resolve.bndrun
index 4ffeba2..4a7b3c0 100644
--- a/spi-fly/spi-fly-dynamic-framework-extension/resolve.bndrun
+++ b/spi-fly/spi-fly-dynamic-framework-extension/resolve.bndrun
@@ -15,4 +15,4 @@
 -runee: JavaSE-1.8
 -runfw: org.apache.felix.framework
 -runrequires: osgi.identity;filter:='(osgi.identity=${project.artifactId})'
--runbundles: org.apache.aries.spifly.dynamic.framework.extension;version='[1.2.4,1.2.5)'
+-runbundles: org.apache.aries.spifly.dynamic.framework.extension;version='[1.3.0,1.3.1)'
diff --git a/spi-fly/spi-fly-static-bundle/pom.xml b/spi-fly/spi-fly-static-bundle/pom.xml
index c6394fa..8a2fda7 100644
--- a/spi-fly/spi-fly-static-bundle/pom.xml
+++ b/spi-fly/spi-fly-static-bundle/pom.xml
@@ -30,7 +30,7 @@
 
     <groupId>org.apache.aries.spifly</groupId>
     <artifactId>org.apache.aries.spifly.static.bundle</artifactId>
-    <version>1.2.5-SNAPSHOT</version>
+    <version>1.3.0-SNAPSHOT</version>
     <packaging>bundle</packaging>
     <name>Apache Aries SPI Fly Static Weaving Bundle</name>
     <description>
@@ -121,6 +121,7 @@
                             aQute.service.reporter
                         </_conditionalpackage>
                         <_includeresource>
+                            @biz.aQute.bndlib-[0-9.]*.jar!/aQute/bnd/osgi/Instruction*.class,
                             META-INF/LICENSE=LICENSE,
                             META-INF/NOTICE=NOTICE,
                         </_includeresource>
diff --git a/spi-fly/spi-fly-static-bundle/resolve.bndrun b/spi-fly/spi-fly-static-bundle/resolve.bndrun
index 454eae2..3917f05 100644
--- a/spi-fly/spi-fly-static-bundle/resolve.bndrun
+++ b/spi-fly/spi-fly-static-bundle/resolve.bndrun
@@ -15,4 +15,4 @@
 -runee: JavaSE-1.8
 -runfw: org.apache.felix.framework
 -runrequires: osgi.identity;filter:='(osgi.identity=${project.artifactId})'
--runbundles: org.apache.aries.spifly.static.bundle;version='[1.2.4,1.2.5)'
+-runbundles: org.apache.aries.spifly.static.bundle;version='[1.3.0,1.3.1)'
diff --git a/spi-fly/spi-fly-static-tool/pom.xml b/spi-fly/spi-fly-static-tool/pom.xml
index 49bc43a..e1c6c1f 100644
--- a/spi-fly/spi-fly-static-tool/pom.xml
+++ b/spi-fly/spi-fly-static-tool/pom.xml
@@ -30,7 +30,7 @@
 
     <groupId>org.apache.aries.spifly</groupId>
     <artifactId>org.apache.aries.spifly.static.tool</artifactId>
-    <version>1.2.5-SNAPSHOT</version>
+    <version>1.3.0-SNAPSHOT</version>
     <packaging>jar</packaging>
     <name>Apache Aries SPI Fly Static Weaving Tool</name>
     <description>
diff --git a/spi-fly/spi-fly-weaver/pom.xml b/spi-fly/spi-fly-weaver/pom.xml
index b963749..5f46d3c 100644
--- a/spi-fly/spi-fly-weaver/pom.xml
+++ b/spi-fly/spi-fly-weaver/pom.xml
@@ -30,7 +30,7 @@
 
     <groupId>org.apache.aries.spifly</groupId>
     <artifactId>org.apache.aries.spifly.weaver-internal</artifactId>
-    <version>1.2.5-SNAPSHOT</version>
+    <version>1.3.0-SNAPSHOT</version>
     <packaging>jar</packaging>
     <name>Apache Aries SPI Fly Weaver (internal module)</name>