You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by pa...@apache.org on 2020/01/16 17:02:36 UTC

[sling-org-apache-sling-feature-extension-apiregions] branch master updated: SLING-9007: Base the region calculation on the feature-origin and add… (#3)

This is an automated email from the ASF dual-hosted git repository.

pauls pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-extension-apiregions.git


The following commit(s) were added to refs/heads/master by this push:
     new 2d85df8  SLING-9007: Base the region calculation on the feature-origin and add… (#3)
2d85df8 is described below

commit 2d85df8c592362c5cd556507ef2a441612107ce5
Author: Karl Pauls <pa...@apache.org>
AuthorDate: Thu Jan 16 18:02:30 2020 +0100

    SLING-9007: Base the region calculation on the feature-origin and add… (#3)
    
    * SLING-9007: Base the region calculation on the feature-origin and add a feature-origin to apiregions during merge
    
    * SLING-9007: Base the region calculation on the feature-origin and add a feature-origin to apiregions during merge
    
    * SLING-9007: Base the region calculation on the feature-origin and add a feature-origin to apiregions during merge
    
    * SLING-9007: Base the region calculation on the feature-origin and add a feature-origin to apiregions during merge
---
 pom.xml                                            |  12 +-
 .../apiregions/APIRegionMergeHandler.java          |  78 ++--------
 .../extension/apiregions/AbstractHandler.java      |  95 ------------
 .../apiregions/BundleArtifactFeatureHandler.java   | 123 ----------------
 .../extension/apiregions/BundleMappingHandler.java |  65 ---------
 .../analyser/AbstractApiRegionsAnalyserTask.java   |   1 -
 .../CheckApiRegionsBundleExportsImports.java       | 134 ++++-------------
 .../apiregions/analyser/CheckApiRegionsOrder.java  |   1 -
 .../extension/apiregions/api/ApiRegion.java        |  55 ++++---
 .../extension/apiregions/api/ApiRegions.java       |  25 +++-
 .../apiregions/APIRegionMergeHandlerTest.java      | 110 ++------------
 .../BundleArtifactFeatureHandlerTest.java          | 152 --------------------
 .../apiregions/BundleMappingHandlerTest.java       | 160 ---------------------
 .../AbstractApiRegionsAnalyserTaskTest.java        |  14 +-
 .../CheckApiRegionsBundleExportsImportsTest.java   |  78 +++++-----
 .../analyser/CheckApiRegionsDependenciesTest.java  |   7 +-
 .../analyser/CheckApiRegionsDuplicatesTest.java    |   7 +-
 .../apiregions/analyser/CheckApiRegionsTest.java   |   5 +-
 .../extension/apiregions/api/TestApiRegions.java   |  11 +-
 .../f_f_1/bundleOrigins.properties                 |   2 -
 .../f_f_1/regionOrigins.properties                 |   2 -
 .../f_f_1/bundleOrigins.properties                 |   2 -
 .../f_f_1/regionOrigins.properties                 |   2 -
 .../f_f_1/bundleOrigins.properties                 |   2 -
 .../f_f_1/regionOrigins.properties                 |   2 -
 .../f_f_1/bundleOrigins.properties                 |   2 -
 .../f_f_1/regionOrigins.properties                 |   1 -
 .../f_f_1/bundleOrigins.properties                 |   2 -
 .../f_f_1/regionOrigins.properties                 |   2 -
 .../f_f_2/bundleOrigins.properties                 |   4 -
 .../f_f_2/regionOrigins.properties                 |   3 -
 31 files changed, 174 insertions(+), 985 deletions(-)

diff --git a/pom.xml b/pom.xml
index c051ab4..9debc0b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -62,13 +62,13 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature</artifactId>
-            <version>1.1.0</version>
+            <version>1.1.1-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.analyser</artifactId>
-            <version>1.1.0</version>
+            <version>1.2.3-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
         <dependency>
@@ -99,8 +99,14 @@
         <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.feature.io</artifactId>
-            <version>1.0.4</version>
+            <version>1.2.1-SNAPSHOT</version>
             <scope>compile</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.utils</artifactId>
+            <version>1.11.2</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 </project>
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandler.java b/src/main/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandler.java
index feb37b9..2ad9647 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandler.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandler.java
@@ -16,18 +16,12 @@
  */
 package org.apache.sling.feature.extension.apiregions;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.util.Arrays;
-import java.util.List;
-import java.util.Properties;
-import java.util.stream.Collectors;
-
+import java.util.LinkedHashSet;
 import javax.json.JsonArray;
 
-import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.builder.HandlerContext;
@@ -53,13 +47,9 @@ public class APIRegionMergeHandler implements MergeHandler {
         if (targetEx != null && !targetEx.getName().equals(ApiRegions.EXTENSION_NAME))
             return;
 
-        storeBundleOrigins(context, source, target);
-
         try {
             final ApiRegions srcRegions = ApiRegions.parse((JsonArray) sourceEx.getJSONStructure());
 
-            storeRegionOrigins(context, source, target, srcRegions);
-
             final ApiRegions targetRegions;
             if (targetEx != null) {
                 targetRegions = ApiRegions.parse((JsonArray) targetEx.getJSONStructure());
@@ -79,11 +69,17 @@ public class APIRegionMergeHandler implements MergeHandler {
                             targetRegion.add(srcExp);
                         }
                     }
+                    LinkedHashSet<ArtifactId> origins = new LinkedHashSet<>(Arrays.asList(targetRegion.getFeatureOrigins()));
+                    origins.add(source.getId());
+                    targetRegion.setFeatureOrigins(origins.toArray(new ArtifactId[0]));
                 }
             }
 
             // If there are any remaining regions in the src extension, process them now
             for (final ApiRegion r : srcRegions.listRegions()) {
+                LinkedHashSet<ArtifactId> origins = new LinkedHashSet<>(Arrays.asList(r.getFeatureOrigins()));
+                origins.add(source.getId());
+                r.setFeatureOrigins(origins.toArray(new ArtifactId[0]));
                 if (!targetRegions.add(r)) {
                     throw new IllegalStateException("Duplicate region " + r.getName());
                 }
@@ -95,62 +91,4 @@ public class APIRegionMergeHandler implements MergeHandler {
             throw new RuntimeException(e);
         }
     }
-
-    private void storeRegionOrigins(HandlerContext context, Feature source, Feature target, ApiRegions regions) {
-        try {
-            File f = AbstractHandler.getFeatureDataFile(context, target, "regionOrigins.properties");
-
-            Properties p = new Properties();
-            if (f.isFile()) {
-                try (FileInputStream fis = new FileInputStream(f)) {
-                    p.load(fis);
-                }
-            }
-
-            String fid = source.getId().toMvnId();
-            p.put(fid, regions.listRegions().stream().map(region -> region.getName()).collect(Collectors.joining(",")));
-
-            try (FileOutputStream fos = new FileOutputStream(f)) {
-                p.store(fos, "Mapping from feature ID to regions that the feature is a member of");
-            }
-        } catch (IOException e) {
-            throw new IllegalStateException("Problem storing region origin information", e);
-        }
-    }
-
-    private void storeBundleOrigins(HandlerContext context, Feature source, Feature target) {
-        try {
-            File f = AbstractHandler.getFeatureDataFile(context, target, "bundleOrigins.properties");
-
-            String featureId = source.getId().toMvnId();
-            Properties p = new Properties();
-            if (f.isFile()) {
-                try (FileInputStream fis = new FileInputStream(f)) {
-                    p.load(fis);
-                }
-            }
-
-            for (Artifact b : source.getBundles()) {
-                String bundleId = b.getId().toMvnId();
-                String org = p.getProperty(bundleId);
-                String newVal;
-                if (org != null) {
-                    List<String> l = Arrays.asList(org.split(","));
-                    if (!l.contains(featureId))
-                        newVal = org + "," + featureId;
-                    else
-                        newVal = org;
-                } else {
-                    newVal = featureId;
-                }
-                p.setProperty(bundleId, newVal);
-            }
-
-            try (FileOutputStream fos = new FileOutputStream(f)) {
-                p.store(fos, "Mapping from bundle artifact IDs to features that contained the bundle.");
-            }
-        } catch (IOException e) {
-            throw new IllegalStateException("Problem storing bundle origin information", e);
-        }
-    }
 }
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/AbstractHandler.java b/src/main/java/org/apache/sling/feature/extension/apiregions/AbstractHandler.java
deleted file mode 100644
index 78ddb25..0000000
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/AbstractHandler.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.sling.feature.extension.apiregions;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.Date;
-import java.util.Properties;
-
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.HandlerContext;
-
-class AbstractHandler {
-    static final String GLOBAL_NAME = "global";
-
-    static final String NAME_KEY = "name";
-    static final String EXPORTS_KEY = "exports";
-
-    static final String FILE_PREFIX = "apiregions.";
-    static final String FILE_STORAGE_DIR_KEY = "fileStorage";
-
-    static final String PROPERTIES_RESOURCE_PREFIX = "sling.feature.apiregions.resource.";
-
-    protected static File getFeatureDataFile(HandlerContext context, Feature target, String fileName) throws IOException {
-        String featureName = target.getId().toMvnId().replaceAll("[^a-zA-Z0-9\\.\\-]", "_");
-        File f = AbstractHandler.getDataFile(context, featureName, fileName);
-        f.getParentFile().mkdirs();
-        return f;
-    }
-
-    protected static File getDataFile(HandlerContext context, String directory, String name) throws IOException {
-        String stg = context.getConfiguration().get(FILE_STORAGE_DIR_KEY);
-        File f;
-        if (stg != null) {
-            File dir;
-            if (directory != null) {
-                dir = new File(stg, directory);
-                dir.mkdirs();
-            } else {
-                dir = new File(stg);
-            }
-            f = new File(dir, name);
-        } else {
-            // If we store in the temp space we don't use the directory
-            Path p = Files.createTempFile(FILE_PREFIX, name);
-            f = p.toFile();
-            f.deleteOnExit();
-        }
-
-        // The feature launcher runtime picks the data file up from this system property
-        System.setProperty(PROPERTIES_RESOURCE_PREFIX + name, f.getCanonicalPath());
-        return f;
-    }
-
-    protected static File getDataFile(HandlerContext context, String name) throws IOException {
-        return getDataFile(context, null, name);
-    }
-
-    protected Properties loadProperties(File file) throws IOException, FileNotFoundException {
-        Properties map = new Properties();
-        if (file.exists()) {
-            try (InputStream is = new FileInputStream(file)) {
-                map.load(is);
-            }
-        }
-        return map;
-    }
-
-    protected void storeProperties(Properties properties, File file) throws IOException, FileNotFoundException {
-        try (OutputStream os = new FileOutputStream(file)) {
-            properties.store(os, "Generated at " + new Date());
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandler.java b/src/main/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandler.java
deleted file mode 100644
index a3df8ff..0000000
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandler.java
+++ /dev/null
@@ -1,123 +0,0 @@
-/*
- * 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.sling.feature.extension.apiregions;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.StringReader;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Properties;
-import java.util.Set;
-import java.util.stream.Collectors;
-
-import javax.json.Json;
-import javax.json.JsonArray;
-import javax.json.JsonObject;
-import javax.json.JsonReader;
-import javax.json.JsonValue;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.HandlerContext;
-import org.apache.sling.feature.builder.PostProcessHandler;
-import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
-
-public class BundleArtifactFeatureHandler extends AbstractHandler implements PostProcessHandler {
-    @Override
-    public void postProcess(HandlerContext context, Feature feature, Extension extension) {
-        if (!ApiRegions.EXTENSION_NAME.equals(extension.getName()))
-            return;
-
-        try {
-            writeBundleToFeatureMap(context, feature);
-            writeFeatureToRegionAndPackageMap(context, feature, extension);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-
-    private void writeBundleToFeatureMap(HandlerContext context, Feature feature) throws IOException {
-        File bundlesFile = getFeatureDataFile(context, feature, "bundles.properties");
-        Properties map = loadProperties(bundlesFile);
-
-        for (Artifact b : feature.getBundles()) {
-            String id = b.getId().toMvnId().trim();
-            String fid = feature.getId().toMvnId().trim();
-
-            String m = map.getProperty(id);
-            if (m != null) {
-                List<String> l = Arrays.asList(m.split(","));
-                if (!l.contains(fid))
-                    m = m.trim() + "," + fid;
-            } else {
-                m = fid;
-            }
-            map.put(id, m);
-        }
-
-        storeProperties(map, bundlesFile);
-    }
-
-    private void writeFeatureToRegionAndPackageMap(HandlerContext context, Feature feature, Extension extension) throws IOException {
-        try (JsonReader jr = Json.createReader(new StringReader(extension.getJSON()))) {
-            JsonArray ja = jr.readArray();
-
-            File featuresFile = getFeatureDataFile(context, feature, "features.properties");
-            File regionsFile = getFeatureDataFile(context, feature, "regions.properties");
-            Properties frMap = loadProperties(featuresFile);
-            Properties rpMap = loadProperties(regionsFile);
-
-            for (JsonValue jv : ja) {
-                if (jv instanceof JsonObject) {
-                    JsonObject jo = (JsonObject) jv;
-                    String fid = feature.getId().toMvnId();
-
-                    // Regions are ordered, so need to use a linked hash set
-                    Set<String> regionSet = new LinkedHashSet<>();
-                    String regions = frMap.getProperty(fid);
-                    if (regions != null) {
-                        regionSet.addAll(Arrays.asList(regions.split(",")));
-                    }
-                    String region = jo.getString(NAME_KEY);
-                    regionSet.add(region);
-
-                    frMap.put(fid, regionSet.stream().collect(Collectors.joining(",")));
-
-                    Set<String> packageSet = new HashSet<>();
-                    String packages = rpMap.getProperty(region);
-                    if (packages != null) {
-                        packageSet.addAll(Arrays.asList(packages.split(",")));
-                    }
-                    if (jo.containsKey(EXPORTS_KEY)) {
-                        JsonArray eja = jo.getJsonArray(EXPORTS_KEY);
-                        for (int i=0; i < eja.size(); i++) {
-                            packageSet.add(eja.getString(i));
-                        }
-                    }
-                    rpMap.put(region, packageSet.stream().collect(Collectors.joining(",")));
-                }
-            }
-
-            storeProperties(frMap, featuresFile);
-            storeProperties(rpMap, regionsFile);
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandler.java b/src/main/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandler.java
deleted file mode 100644
index 88ca215..0000000
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandler.java
+++ /dev/null
@@ -1,65 +0,0 @@
-/*
- * 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.sling.feature.extension.apiregions;
-
-import java.io.File;
-import java.io.IOException;
-import java.net.URL;
-import java.util.Properties;
-import java.util.jar.JarFile;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.HandlerContext;
-import org.apache.sling.feature.builder.PostProcessHandler;
-import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
-import org.apache.sling.feature.io.IOUtils;
-import org.osgi.framework.Constants;
-
-public class BundleMappingHandler extends AbstractHandler implements PostProcessHandler {
-    @Override
-    public void postProcess(HandlerContext context, Feature feature, Extension extension) {
-        if (!ApiRegions.EXTENSION_NAME.equals(extension.getName()))
-            return;
-
-        try {
-            File idBSNFile = getFeatureDataFile(context, feature, "idbsnver.properties");
-            Properties map = loadProperties(idBSNFile);
-
-            for (Artifact b : feature.getBundles()) {
-                URL f = context.getArtifactProvider().provide(b.getId());
-
-                try (JarFile jf = IOUtils.getJarFileFromURL(f, true, null)) {
-                    String bsn = jf.getManifest().getMainAttributes().getValue(Constants.BUNDLE_SYMBOLICNAME);
-                    if (bsn == null)
-                        continue;
-
-                    String ver = jf.getManifest().getMainAttributes().getValue(Constants.BUNDLE_VERSION);
-                    if (ver == null)
-                        ver = "0.0.0";
-
-                    map.put(b.getId().toMvnId(), bsn.trim() + "~" + ver.trim());
-                }
-            }
-
-            storeProperties(map, idBSNFile);
-        } catch (IOException e) {
-            throw new RuntimeException(e);
-        }
-    }
-}
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/AbstractApiRegionsAnalyserTask.java b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/AbstractApiRegionsAnalyserTask.java
index 916e865..adb244f 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/AbstractApiRegionsAnalyserTask.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/AbstractApiRegionsAnalyserTask.java
@@ -17,7 +17,6 @@
 package org.apache.sling.feature.extension.apiregions.analyser;
 
 import java.io.IOException;
-
 import javax.json.JsonArray;
 
 import org.apache.sling.feature.Extension;
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsBundleExportsImports.java b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsBundleExportsImports.java
index 2b1a884..d11e2a5 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsBundleExportsImports.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsBundleExportsImports.java
@@ -18,32 +18,28 @@
  */
 package org.apache.sling.feature.extension.apiregions.analyser;
 
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
-import java.io.InputStream;
 import java.util.AbstractMap;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
-import java.util.Properties;
 import java.util.Set;
 import java.util.SortedMap;
 import java.util.TreeMap;
 import java.util.stream.Collectors;
-
+import java.util.stream.Stream;
 import javax.json.JsonArray;
 
+import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.Extensions;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.analyser.task.AnalyserTask;
 import org.apache.sling.feature.analyser.task.AnalyserTaskContext;
+import org.apache.sling.feature.extension.apiregions.api.ApiRegion;
 import org.apache.sling.feature.extension.apiregions.api.ApiRegions;
 import org.apache.sling.feature.scanner.BundleDescriptor;
 import org.apache.sling.feature.scanner.PackageInfo;
@@ -51,7 +47,6 @@ import org.osgi.framework.Version;
 
 public class CheckApiRegionsBundleExportsImports implements AnalyserTask {
 
-    private static final String FILE_STORAGE_CONFIG_KEY = "fileStorage";
     private static final String IGNORE_API_REGIONS_CONFIG_KEY = "ignoreAPIRegions";
     private static final String GLOBAL_REGION = "global";
     private static final String NO_REGION = " __NO_REGION__ ";
@@ -146,31 +141,21 @@ public class CheckApiRegionsBundleExportsImports implements AnalyserTask {
         if ( ctx.getFrameworkDescriptor() != null ) {
             exportingBundles.add(ctx.getFrameworkDescriptor());
         }
-
-        Map<String, Set<String>> bundleToOriginalFeatures;
-        Map<String, Set<String>> featureToOriginalRegions;
         ApiRegions apiRegions = new ApiRegions(); // Empty API Regions;
 
-        if (ignoreAPIRegions) {
-            bundleToOriginalFeatures = Collections.emptyMap();
-            featureToOriginalRegions = Collections.emptyMap();
-        } else {
-            bundleToOriginalFeatures = readBundleOrigins(ctx);
-            featureToOriginalRegions = readRegionOrigins(ctx);
-            Feature feature = ctx.getFeature();
-
-            // extract and check the api-regions
-
-            Extensions extensions = feature.getExtensions();
-            Extension apiRegionsExtension = extensions.getByName(ApiRegions.EXTENSION_NAME);
-            if (apiRegionsExtension != null && apiRegionsExtension.getJSONStructure() != null) {
-                try {
-                    apiRegions = ApiRegions.parse((JsonArray) apiRegionsExtension.getJSONStructure());
-                } catch (IOException e) {
-                    ctx.reportError("API Regions '" + apiRegionsExtension.getJSON()
-                            + "' does not represent a valid JSON 'api-regions': " + e.getMessage());
-                    return;
-                }
+        Feature feature = ctx.getFeature();
+
+        // extract and check the api-regions
+
+        Extensions extensions = feature.getExtensions();
+        Extension apiRegionsExtension = extensions.getByName(ApiRegions.EXTENSION_NAME);
+        if (apiRegionsExtension != null && apiRegionsExtension.getJSONStructure() != null) {
+            try {
+                apiRegions = ApiRegions.parse((JsonArray) apiRegionsExtension.getJSONStructure());
+            } catch (IOException e) {
+                ctx.reportError("API Regions '" + apiRegionsExtension.getJSON()
+                        + "' does not represent a valid JSON 'api-regions': " + e.getMessage());
+                return;
             }
         }
 
@@ -186,8 +171,7 @@ public class CheckApiRegionsBundleExportsImports implements AnalyserTask {
                 if ( info.getImportedPackages() != null ) {
                     for(final PackageInfo pck : info.getImportedPackages() ) {
                         final Map<BundleDescriptor, Set<String>> candidates =
-                                getCandidates(exportingBundles, pck, info,
-                                        bundleToOriginalFeatures, featureToOriginalRegions, apiRegions);
+                                getCandidates(exportingBundles, pck, info, apiRegions, ignoreAPIRegions);
                         if ( candidates.isEmpty() ) {
                             if ( pck.isOptional() ) {
                                 getReport(reports, info).missingExportsForOptional.add(pck);
@@ -221,7 +205,7 @@ public class CheckApiRegionsBundleExportsImports implements AnalyserTask {
 
                                     // Find out what regions the importing bundle is in
                                     Set<String> imRegions =
-                                            getBundleRegions(info, bundleToOriginalFeatures, featureToOriginalRegions);
+                                            getBundleRegions(info, apiRegions, ignoreAPIRegions);
 
                                     // Record the exporting and importing regions for diagnostics
                                     exportingRegions.addAll(exRegions);
@@ -291,21 +275,12 @@ public class CheckApiRegionsBundleExportsImports implements AnalyserTask {
         }
     }
 
-    private Set<String> getBundleRegions(BundleDescriptor info, Map<String, Set<String>> bundleToOriginalFeatures,
-            Map<String, Set<String>> featureToOriginalRegions) {
-        Set<String> result = new HashSet<>();
+    private Set<String> getBundleRegions(BundleDescriptor info, ApiRegions regions, boolean ignoreAPIRegions) {
+        Set<String> result = ignoreAPIRegions ? Collections.emptySet() : Stream.of(info.getArtifact().getFeatureOrigins())
+            .map(regions::getRegionsByFeature).flatMap(Stream::of).map(ApiRegion::getName).collect(Collectors.toSet());
 
-        Set<String> originFeatures = bundleToOriginalFeatures.get(info.getArtifact().getId().toMvnId());
-        if (originFeatures != null) {
-            for (String feature : originFeatures) {
-                Set<String> originRegions = featureToOriginalRegions.get(feature);
-                if (originRegions != null) {
-                    result.addAll(originRegions);
-                }
-            }
-        }
-
-        if (result.size() == 0) {
+        if (result.isEmpty()) {
+            result = new HashSet<>();
             result.add(NO_REGION);
         }
         return result;
@@ -343,22 +318,15 @@ public class CheckApiRegionsBundleExportsImports implements AnalyserTask {
             final List<BundleDescriptor> exportingBundles,
             final PackageInfo pck,
             final BundleDescriptor requestingBundle,
-            final Map<String, Set<String>> bundleToOriginalFeatures,
-            final Map<String, Set<String>> featureToOriginalRegions,
-            final ApiRegions apiRegions) throws IOException {
-        Set<String> rf = bundleToOriginalFeatures.get(
-                requestingBundle.getArtifact().getId().toMvnId());
-        if (rf == null)
-            rf = Collections.emptySet();
+            final ApiRegions apiRegions, boolean ignoreAPIRegions) {
+        Set<String> rf = ignoreAPIRegions ? Collections.emptySet() : Stream.of(requestingBundle.getArtifact().getFeatureOrigins()).map(ArtifactId::toMvnId).collect(Collectors.toSet());
+
         final Set<String> requestingFeatures = rf;
 
         final Map<BundleDescriptor, Set<String>> candidates = new HashMap<>();
         for(final BundleDescriptor info : exportingBundles) {
             if ( info.isExportingPackage(pck.getName()) ) {
-                Set<String> providingFeatures = bundleToOriginalFeatures.get(
-                        info.getArtifact().getId().toMvnId());
-                if (providingFeatures == null)
-                    providingFeatures = Collections.emptySet();
+                Set<String> providingFeatures = ignoreAPIRegions ? Collections.emptySet() : Stream.of(info.getArtifact().getFeatureOrigins()).map(ArtifactId::toMvnId).collect(Collectors.toSet());
 
                 // Compute the intersection without modifying the sets
                 Set<String> intersection = providingFeatures.stream().filter(
@@ -369,7 +337,7 @@ public class CheckApiRegionsBundleExportsImports implements AnalyserTask {
                     continue;
                 }
 
-                for (String region : getBundleRegions(info, bundleToOriginalFeatures, featureToOriginalRegions)) {
+                for (String region : getBundleRegions(info, apiRegions, ignoreAPIRegions)) {
                     if (!NO_REGION.equals(region) &&
                             (apiRegions.getRegionByName(region) == null
                                     || apiRegions.getRegionByName(region).getExportByName(pck.getName()) == null))
@@ -386,50 +354,4 @@ public class CheckApiRegionsBundleExportsImports implements AnalyserTask {
         }
         return candidates;
     }
-
-    private Map<String, Set<String>> readBundleOrigins(AnalyserTaskContext ctx) throws IOException {
-        return readOrigins(ctx, "bundleOrigins.properties");
-    }
-
-    private Map<String, Set<String>> readRegionOrigins(AnalyserTaskContext ctx) throws IOException {
-        return readOrigins(ctx, "regionOrigins.properties");
-    }
-
-    private Map<String, Set<String>> readOrigins(AnalyserTaskContext ctx, String fileName) throws IOException, FileNotFoundException {
-        Feature feature = ctx.getFeature();
-        Extension APIRegionExtension = feature.getExtensions().getByName(ApiRegions.EXTENSION_NAME);
-
-        String fileStorage = ctx.getConfiguration().get(FILE_STORAGE_CONFIG_KEY);
-        if (fileStorage == null) {
-            if (APIRegionExtension != null) {
-                throw new IllegalStateException("Feature " + feature.getId() +
-                        " has API regions defined, but no storage is configured for origin information files. "
-                        + "Please configure the " + FILE_STORAGE_CONFIG_KEY + " configuration item.");
-            }
-            return Collections.emptyMap();
-        }
-
-        String featureName = feature.getId().toMvnId().replaceAll("[^a-zA-Z0-9\\.\\-]", "_");
-        File file = new File(fileStorage, featureName + File.separator + fileName);
-        if (!file.exists()) {
-            if (APIRegionExtension != null)
-                throw new IllegalStateException("Feature " + feature.getId() +
-                        " has API regions defined but no file with origin information can be found " + file +
-                        " Configure the org.apache.sling.feature.extension.apiregions appropriately to write this information");
-            return Collections.emptyMap();
-        }
-
-        Properties p = new Properties();
-        try (InputStream is = new FileInputStream(file)) {
-            p.load(is);
-        }
-
-        Map<String, Set<String>> m = new HashMap<>();
-        for (String key : p.stringPropertyNames()) {
-            String val = p.getProperty(key);
-            String[] features = val.split(",");
-            m.put(key, new HashSet<>(Arrays.asList(features)));
-        }
-        return m;
-    }
 }
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsOrder.java b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsOrder.java
index 4396de8..bbfff43 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsOrder.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsOrder.java
@@ -19,7 +19,6 @@ package org.apache.sling.feature.extension.apiregions.analyser;
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
-
 import javax.json.JsonArray;
 
 import org.apache.sling.feature.Extension;
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegion.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegion.java
index c602a5e..e7da0b2 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegion.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegion.java
@@ -17,11 +17,17 @@
 package org.apache.sling.feature.extension.apiregions.api;
 
 import java.util.ArrayList;
+import java.util.Arrays;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.sling.feature.ArtifactId;
 
 /**
  * Describes an api region
@@ -33,6 +39,8 @@ public class ApiRegion {
 
     private final List<ApiExport> exports = new ArrayList<>();
 
+    private final List<ArtifactId> origins = new ArrayList<>();
+
     private final Map<String, String> properties = new HashMap<>();
 
     private final String name;
@@ -59,6 +67,17 @@ public class ApiRegion {
         return name;
     }
 
+    public ArtifactId[] getFeatureOrigins() {
+        return origins.toArray(new ArtifactId[0]);
+    }
+
+    public void setFeatureOrigins(ArtifactId... featureOrigins) {
+        origins.clear();
+        if (featureOrigins != null) {
+            origins.addAll(Stream.of(featureOrigins).filter(Objects::nonNull).distinct().collect(Collectors.toList()));
+        }
+    }
+
     /**
      * Add the export. The export is only added if there isn't already a export with
      * the same name
@@ -183,34 +202,26 @@ public class ApiRegion {
         int result = 1;
         result = prime * result + ((exports == null) ? 0 : exports.hashCode());
         result = prime * result + ((name == null) ? 0 : name.hashCode());
+        result = prime * result + Arrays.hashCode(getFeatureOrigins());
         result = prime * result + ((properties == null) ? 0 : properties.hashCode());
         return result;
     }
 
     @Override
-    public boolean equals(Object obj) {
-        if (this == obj)
+    public boolean equals(Object o)
+    {
+        if (this == o)
+        {
             return true;
-        if (obj == null)
-            return false;
-        if (getClass() != obj.getClass())
-            return false;
-        ApiRegion other = (ApiRegion) obj;
-        if (exports == null) {
-            if (other.exports != null)
-                return false;
-        } else if (!exports.equals(other.exports))
-            return false;
-        if (name == null) {
-            if (other.name != null)
-                return false;
-        } else if (!name.equals(other.name))
-            return false;
-        if (properties == null) {
-            if (other.properties != null)
-                return false;
-        } else if (!properties.equals(other.properties))
+        }
+        if (o == null || getClass() != o.getClass())
+        {
             return false;
-        return true;
+        }
+        ApiRegion region = (ApiRegion) o;
+        return exports.equals(region.exports) &&
+            origins.equals(region.origins) &&
+            properties.equals(region.properties) &&
+            Objects.equals(name, region.name);
     }
 }
diff --git a/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegions.java b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegions.java
index 17b86b4..238b116 100644
--- a/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegions.java
+++ b/src/main/java/org/apache/sling/feature/extension/apiregions/api/ApiRegions.java
@@ -21,9 +21,11 @@ import java.io.StringReader;
 import java.io.StringWriter;
 import java.util.ArrayList;
 import java.util.Collections;
+import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
-
+import java.util.Set;
+import java.util.stream.Stream;
 import javax.json.Json;
 import javax.json.JsonArray;
 import javax.json.JsonArrayBuilder;
@@ -36,6 +38,7 @@ import javax.json.JsonValue;
 import javax.json.JsonValue.ValueType;
 import javax.json.JsonWriter;
 
+import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 
 /**
@@ -155,6 +158,12 @@ public class ApiRegions {
         return found;
     }
 
+    public ApiRegion[] getRegionsByFeature(final ArtifactId featureId) {
+        return this.regions.stream().filter(
+            region -> Stream.of(region.getFeatureOrigins()).anyMatch(featureId::equals)
+        ).toArray(ApiRegion[]::new);
+    }
+
     /**
      * Get the names of the regions
      *
@@ -204,6 +213,14 @@ public class ApiRegions {
 
                 regionBuilder.add(EXPORTS_KEY, expArrayBuilder);
             }
+            ArtifactId[] origins = region.getFeatureOrigins();
+            if (origins.length > 0) {
+                final JsonArrayBuilder originBuilder = Json.createArrayBuilder();
+                for (ArtifactId origin : origins) {
+                    originBuilder.add(origin.toMvnId());
+                }
+                regionBuilder.add(Artifact.KEY_FEATURE_ORIGINS, originBuilder);
+            }
             for (final Map.Entry<String, String> entry : region.getProperties().entrySet()) {
                 regionBuilder.add(entry.getKey(), entry.getValue());
             }
@@ -296,6 +313,12 @@ public class ApiRegions {
                                 }
                             }
                         }
+                    } else if (entry.getKey().equals(Artifact.KEY_FEATURE_ORIGINS)) {
+                        Set<ArtifactId> origins = new LinkedHashSet<>();
+                        for (final JsonValue origin : (JsonArray) entry.getValue()) {
+                            origins.add(ArtifactId.fromMvnId(((JsonString) origin).getString()));
+                        }
+                        region.setFeatureOrigins(origins.toArray(new ArtifactId[0]));
                     } else {
                         region.getProperties().put(entry.getKey(), ((JsonString) entry.getValue()).getString());
                     }
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandlerTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandlerTest.java
index 04e967b..15c0bc6 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandlerTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/APIRegionMergeHandlerTest.java
@@ -16,25 +16,16 @@
  */
 package org.apache.sling.feature.extension.apiregions;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.StringReader;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.Collections;
 import java.util.Comparator;
-import java.util.Properties;
-
 import javax.json.Json;
 import javax.json.JsonArray;
 import javax.json.JsonReader;
 
-import org.apache.sling.feature.Artifact;
 import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionState;
@@ -48,6 +39,9 @@ import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.Mockito;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 public class APIRegionMergeHandlerTest {
     private Path tempDir;
@@ -104,6 +98,7 @@ public class APIRegionMergeHandlerTest {
         global.add(new ApiExport("a.b.c"));
         global.add(new ApiExport("d.e.f"));
         global.add(new ApiExport("test"));
+        global.setFeatureOrigins(sf.getId());
         expected.add(global);
 
         ApiRegion internal = new ApiRegion("internal");
@@ -114,6 +109,7 @@ public class APIRegionMergeHandlerTest {
         ApiRegion something = new ApiRegion("something");
         something.add(new ApiExport("a.ha"));
         something.getProperties().put("my-key", "my-val");
+        something.setFeatureOrigins(sf.getId());
         expected.add(something);
 
         ApiRegions created = ApiRegions.parse((JsonArray) tgEx.getJSONStructure());
@@ -144,10 +140,10 @@ public class APIRegionMergeHandlerTest {
 
         Extension tgEx = tf.getExtensions().iterator().next();
 
-        String expectedJSON = "[{\"name\":\"global\",\"exports\":[\"a.b.c\",\"d.e.f\"]},"
-                + "{\"name\":\"deprecated\",\"exports\":[\"klm\",\"qrs\"]},"
-                + "{\"name\":\"internal\",\"exports\":[\"xyz\"]},"
-                + "{\"name\":\"forbidden\",\"exports\":[\"abc\",\"klm\"]}]";
+        String expectedJSON = "[{\"name\":\"global\",\"exports\":[\"a.b.c\",\"d.e.f\"],\"feature-origins\":[\"y:s:2\"]},"
+                + "{\"name\":\"deprecated\",\"exports\":[\"klm\",\"qrs\"],\"feature-origins\":[\"y:s:2\"]},"
+                + "{\"name\":\"internal\",\"exports\":[\"xyz\"],\"feature-origins\":[\"y:s:2\"]},"
+                + "{\"name\":\"forbidden\",\"exports\":[\"abc\",\"klm\"],\"feature-origins\":[\"y:s:2\"]}]";
         JsonReader er = Json.createReader(new StringReader(expectedJSON));
         JsonReader ar = Json.createReader(new StringReader(tgEx.getJSON()));
         JsonArray ea = er.readArray();
@@ -155,92 +151,4 @@ public class APIRegionMergeHandlerTest {
 
         assertEquals(ea, aa);
     }
-
-    @Test
-    public void testStoreBundleOrigins() throws Exception {
-        HandlerContext hc = Mockito.mock(HandlerContext.class);
-        Mockito.when(hc.getConfiguration()).thenReturn(
-                Collections.singletonMap(AbstractHandler.FILE_STORAGE_DIR_KEY,
-                        tempDir.toString()));
-
-        APIRegionMergeHandler armh = new APIRegionMergeHandler();
-
-        Feature tf = new Feature(ArtifactId.fromMvnId("g:t:1"));
-        Feature sf1 = new Feature(ArtifactId.fromMvnId("g:s1:1"));
-        Extension sf1Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
-        sf1Ex.setJSON("[]");
-
-        sf1.getBundles().add(new Artifact(ArtifactId.fromMvnId("a:b1:1")));
-        sf1.getBundles().add(new Artifact(ArtifactId.fromMvnId("a:b2:1")));
-
-        armh.merge(hc, tf, sf1, null, sf1Ex);
-
-        Feature sf2 = new Feature(ArtifactId.fromMvnId("g:s2:1"));
-        Extension sf2Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
-        sf2Ex.setJSON("[]");
-
-        sf2.getBundles().add(new Artifact(ArtifactId.fromMvnId("a:b2:1")));
-        sf2.getBundles().add(new Artifact(ArtifactId.fromMvnId("a:b3:1")));
-        sf2.getBundles().add(new Artifact(ArtifactId.fromMvnId("a:b2:1")));
-
-        armh.merge(hc, tf, sf2, tf.getExtensions().getByName("api-regions"), sf2Ex);
-
-        Feature sf3 = new Feature(ArtifactId.fromMvnId("g:s3:1"));
-        Extension sf3Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
-        sf3Ex.setJSON("[]");
-
-        sf3.getBundles().add(new Artifact(ArtifactId.fromMvnId("a:b2:1")));
-
-        armh.merge(hc, tf, sf3, tf.getExtensions().getByName("api-regions"), sf3Ex);
-
-        Properties bo = new Properties();
-        bo.load(new FileInputStream(new File(tempDir.toFile(), "g_t_1/bundleOrigins.properties")));
-        assertEquals(3, bo.size());
-
-        assertEquals("g:s1:1", bo.get("a:b1:1"));
-        assertEquals("g:s1:1,g:s2:1,g:s3:1", bo.get("a:b2:1"));
-        assertEquals("g:s2:1", bo.get("a:b3:1"));
-    }
-
-    @Test
-    public void testStoreRegionOrigins() throws Exception {
-        HandlerContext hc = Mockito.mock(HandlerContext.class);
-        Mockito.when(hc.getConfiguration()).thenReturn(
-                Collections.singletonMap(AbstractHandler.FILE_STORAGE_DIR_KEY,
-                        tempDir.toString()));
-
-        APIRegionMergeHandler armh = new APIRegionMergeHandler();
-
-        Feature tf = new Feature(ArtifactId.fromMvnId("x:t:1"));
-        Feature sf1 = new Feature(ArtifactId.fromMvnId("y:s:2"));
-
-        Extension sr1Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
-        sr1Ex.setJSON("[{\"name\":\"global\","
-                + "\"exports\": [\"a.b.c\",\"d.e.f\"]},"
-                + "{\"name\":\"deprecated\","
-                + "\"exports\":[\"klm\",\"#ignored\",\"qrs\"]},"
-                + "{\"name\":\"internal\","
-                + "\"exports\":[\"xyz\"]},"
-                + "{\"name\":\"forbidden\","
-                + "\"exports\":[\"abc\",\"klm\"]}]");
-
-        armh.merge(hc, tf, sf1, null, sr1Ex);
-
-        Feature sf2 = new Feature(ArtifactId.fromMvnId("z:s:1"));
-
-        Extension sr2Ex = new Extension(ExtensionType.JSON, "api-regions", ExtensionState.OPTIONAL);
-        sr2Ex.setJSON("[{\"name\":\"global\","
-                + "\"exports\": [\"g.h.i\"]},"
-                + "{\"name\":\"internal\","
-                + "\"exports\":[]},"
-                + "{\"name\":\"somethingelse\","
-                + "\"exports\":[\"qqq\"]}]");
-        armh.merge(hc, tf, sf2, tf.getExtensions().getByName("api-regions"), sr2Ex);
-
-        Properties ro = new Properties();
-        ro.load(new FileInputStream(new File(tempDir.toFile(), "x_t_1/regionOrigins.properties")));
-        assertEquals(2, ro.size());
-        assertEquals("global,deprecated,internal,forbidden", ro.get("y:s:2"));
-        assertEquals("global,internal,somethingelse", ro.get("z:s:1"));
-    }
 }
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandlerTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandlerTest.java
deleted file mode 100644
index a4f465e..0000000
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/BundleArtifactFeatureHandlerTest.java
+++ /dev/null
@@ -1,152 +0,0 @@
-/*
- * 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.sling.feature.extension.apiregions;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.ArtifactProvider;
-import org.apache.sling.feature.builder.HandlerContext;
-import org.junit.Test;
-
-import java.io.FileReader;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Properties;
-
-import static org.junit.Assert.assertEquals;
-
-public class BundleArtifactFeatureHandlerTest {
-    @Test
-    public void testBundleToFeatureMap() throws Exception {
-        BundleArtifactFeatureHandler bafh = new BundleArtifactFeatureHandler();
-
-        Feature f = new Feature(ArtifactId.fromMvnId("org.sling:something:1.2.3:slingosgifeature:myclassifier"));
-        Artifact b1 = new Artifact(ArtifactId.fromMvnId("org.sling:b1:1"));
-        Artifact b2 = new Artifact(ArtifactId.fromMvnId("org.sling:b2:1"));
-        Artifact b3a = new Artifact(ArtifactId.fromMvnId("org.sling:b3:1"));
-        Artifact b3b = new Artifact(ArtifactId.fromMvnId("org.sling:b3:1"));
-        f.getBundles().addAll(Arrays.asList(b1, b2, b3a, b3b));
-
-        Extension ex = new Extension(ExtensionType.JSON, "api-regions", false);
-        ex.setJSON("[]");
-        bafh.postProcess(new TestHandlerContextImpl(), f, ex);
-
-        String p = System.getProperty("sling.feature.apiregions.resource.bundles.properties");
-        Properties actual = new Properties();
-        actual.load(new FileReader(p));
-
-        Properties expected = new Properties();
-        expected.put("org.sling:b1:1", "org.sling:something:1.2.3:slingosgifeature:myclassifier");
-        expected.put("org.sling:b2:1", "org.sling:something:1.2.3:slingosgifeature:myclassifier");
-        expected.put("org.sling:b3:1", "org.sling:something:1.2.3:slingosgifeature:myclassifier");
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    public void testFeatureToRegionMap() throws Exception {
-        BundleArtifactFeatureHandler bafh = new BundleArtifactFeatureHandler();
-
-        Feature f = new Feature(ArtifactId.fromMvnId("org.sling:something:1.2.3"));
-        Extension ex = new Extension(ExtensionType.JSON, "api-regions", false);
-        ex.setJSON("[{\"name\":\"global\","
-                + "\"exports\": [\"a.b.c\",\"d.e.f\"]},"
-                + "{\"name\":\"internal\","
-                + "\"exports\":[\"xyz\"]},"
-                + "{\"name\":\"global\","
-                + "\"exports\":[\"test\"]}]");
-
-        bafh.postProcess(new TestHandlerContextImpl(), f, ex);
-
-        String p = System.getProperty("sling.feature.apiregions.resource.features.properties");
-        Properties actual = new Properties();
-        actual.load(new FileReader(p));
-
-        Properties expected = new Properties();
-        expected.put("org.sling:something:1.2.3", "internal,global");
-
-        String[] al = ((String) actual.remove("org.sling:something:1.2.3")).split(",");
-        String[] el = ((String) expected.remove("org.sling:something:1.2.3")).split(",");
-        assertEquals(new HashSet<>(Arrays.asList(el)), new HashSet<>(Arrays.asList(al)));
-        assertEquals(expected, actual);
-
-        String p2 = System.getProperty("sling.feature.apiregions.resource.regions.properties");
-        Properties actual2 = new Properties();
-        actual2.load(new FileReader(p2));
-
-        Properties expected2 = new Properties();
-        expected2.put("internal", "xyz");
-        expected2.put("global", "test,a.b.c,d.e.f");
-
-        String[] agl2 = ((String) actual2.remove("global")).split(",");
-        String[] egl2 = ((String) expected2.remove("global")).split(",");
-        assertEquals(new HashSet<>(Arrays.asList(egl2)), new HashSet<>(Arrays.asList(agl2)));
-        assertEquals(expected2, actual2);
-    }
-
-    @Test
-    public void testRegionOrdering() throws Exception {
-        BundleArtifactFeatureHandler bafh = new BundleArtifactFeatureHandler();
-
-        Feature f = new Feature(ArtifactId.fromMvnId("org.sling:somethingelse:1.0.0"));
-        Extension ex = new Extension(ExtensionType.JSON, "api-regions", false);
-        ex.setJSON("[{\"name\":\"global\","
-                + "\"exports\": []},"
-                + "{\"name\":\"internal\","
-                + "\"exports\":[]},"
-                + "{\"name\":\"private\","
-                + "\"exports\":[]},"
-                + "{\"name\":\"forbidden\","
-                + "\"exports\":[]}]");
-
-        bafh.postProcess(new TestHandlerContextImpl(), f, ex);
-
-        String p = System.getProperty("sling.feature.apiregions.resource.features.properties");
-        Properties actual = new Properties();
-        actual.load(new FileReader(p));
-
-        Properties expected = new Properties();
-        expected.put("org.sling:somethingelse:1.0.0", "global,internal,private,forbidden");
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    public void testUnrelatedExtension() {
-        BundleArtifactFeatureHandler bafh = new BundleArtifactFeatureHandler();
-        Extension ex = new Extension(ExtensionType.JSON, "foobar", false);
-        bafh.postProcess(null, null, ex);
-        // Should not do anything and definitely not throw an exception
-    }
-
-    private static class TestHandlerContextImpl implements HandlerContext {
-        private final Map<String, String> cfg = new HashMap<>();
-
-        @Override
-        public ArtifactProvider getArtifactProvider() {
-            return null;
-        }
-
-        @Override
-        public Map<String, String> getConfiguration() {
-            return cfg;
-        }
-    }
-}
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandlerTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandlerTest.java
deleted file mode 100644
index 99c2042..0000000
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/BundleMappingHandlerTest.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * 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.sling.feature.extension.apiregions;
-
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.ArtifactId;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.builder.ArtifactProvider;
-import org.apache.sling.feature.builder.HandlerContext;
-import org.junit.Test;
-
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileReader;
-import java.io.IOException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Properties;
-
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertTrue;
-
-public class BundleMappingHandlerTest {
-    @Test
-    public void testHandler() throws IOException {
-        ArtifactProvider ap = new ArtifactProvider() {
-            @Override
-            public URL provide(ArtifactId id) {
-                switch(id.toMvnId()) {
-                case "g:b1:1":
-                    return getResourceFile("b1/b1.jar");
-                case "g:b2:1.2.3":
-                    return getResourceFile("b2/b2.jar");
-                case "g:b3:0":
-                    return getResourceFile("b3/b3.jar");
-                default: return null;
-                }
-            }
-        };
-
-        BundleMappingHandler bmh = new BundleMappingHandler();
-
-        Extension ex = new Extension(ExtensionType.JSON, "api-regions", false);
-        Feature f = new Feature(ArtifactId.fromMvnId("foo:bar:123"));
-        Artifact b1 = new Artifact(ArtifactId.fromMvnId("g:b1:1"));
-        f.getBundles().add(b1);
-        Artifact b2 = new Artifact(ArtifactId.fromMvnId("g:b2:1.2.3"));
-        f.getBundles().add(b2);
-        Artifact b3 = new Artifact(ArtifactId.fromMvnId("g:b3:0"));
-        f.getBundles().add(b3);
-        bmh.postProcess(new TestHandlerContext(ap), f, ex);
-
-        String p = System.getProperty("sling.feature.apiregions.resource.idbsnver.properties");
-        Properties actual = new Properties();
-        actual.load(new FileReader(p));
-
-        Properties expected = new Properties();
-        expected.put("g:b1:1", "b1~1.0.0");
-        expected.put("g:b2:1.2.3", "b2~1.2.3");
-        assertEquals(expected, actual);
-    }
-
-    @Test
-    public void testSpecificDirectory() throws Exception {
-        Path tempDir = Files.createTempDirectory(getClass().getSimpleName());
-
-        try {
-            ArtifactProvider ap = new ArtifactProvider() {
-                @Override
-                public URL provide(ArtifactId id) {
-                    switch(id.toMvnId()) {
-                    case "g:b1:1":
-                        return getResourceFile("b1/b1.jar");
-                    default: return null;
-                    }
-                }
-            };
-
-            Extension ex = new Extension(ExtensionType.JSON, "api-regions", false);
-            Feature f = new Feature(ArtifactId.fromMvnId("foo:bar:123"));
-            Artifact b1 = new Artifact(ArtifactId.fromMvnId("g:b1:1"));
-            f.getBundles().add(b1);
-
-            BundleMappingHandler bmh = new BundleMappingHandler();
-            final Map<String, String> kvm = new HashMap<>();
-            kvm.put("fileStorage", tempDir.toString());
-            bmh.postProcess(new TestHandlerContext(ap,
-                    kvm), f, ex);
-
-            File expectedFile = new File(tempDir.toFile(), "foo_bar_123/idbsnver.properties");
-            assertTrue(expectedFile.exists());
-            Properties p = new Properties();
-            p.load(new FileInputStream(expectedFile));
-
-            Properties ep = new Properties();
-            ep.put("g:b1:1", "b1~1.0.0");
-            assertEquals(ep, p);
-        } finally {
-            for (File f : tempDir.toFile().listFiles()) {
-                f.delete();
-            }
-            tempDir.toFile().delete();
-        }
-    }
-
-    @Test
-    public void testUnrelatedExtension() {
-        BundleMappingHandler bmh = new BundleMappingHandler();
-        Extension ex = new Extension(ExtensionType.JSON, "foobar", false);
-        bmh.postProcess(null, null, ex);
-        // Should not do anything and definitely not throw an exception
-    }
-
-    private URL getResourceFile(String filename) {
-        return getClass().getClassLoader().getResource(filename);
-    }
-
-    private class TestHandlerContext implements HandlerContext {
-        private final ArtifactProvider artifactProvider;
-        private final Map<String, String> config;
-
-        private TestHandlerContext(ArtifactProvider ap, Map<String, String> cfg) {
-            artifactProvider = ap;
-            config = cfg;
-        }
-
-        public TestHandlerContext(ArtifactProvider ap) {
-            this(ap, new HashMap<>());
-        }
-
-        @Override
-        public ArtifactProvider getArtifactProvider() {
-            return artifactProvider;
-        }
-
-        @Override
-        public Map<String, String> getConfiguration() {
-            return config;
-        }
-    }
-}
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/AbstractApiRegionsAnalyserTaskTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/AbstractApiRegionsAnalyserTaskTest.java
index 1483e34..e8decf5 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/AbstractApiRegionsAnalyserTaskTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/AbstractApiRegionsAnalyserTaskTest.java
@@ -16,13 +16,6 @@
  */
 package org.apache.sling.feature.extension.apiregions.analyser;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.mockito.ArgumentMatchers.anyString;
-import static org.mockito.Mockito.doAnswer;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
 import java.io.StringReader;
 import java.net.URL;
 import java.util.Collections;
@@ -30,7 +23,6 @@ import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.jar.Manifest;
-
 import javax.json.Json;
 import javax.json.JsonArray;
 import javax.json.JsonReader;
@@ -53,6 +45,12 @@ import org.junit.Test;
 import org.mockito.Mockito;
 import org.mockito.invocation.InvocationOnMock;
 import org.mockito.stubbing.Answer;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
 
 public abstract class AbstractApiRegionsAnalyserTaskTest<T extends AbstractApiRegionsAnalyserTask> {
 
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsBundleExportsImportsTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsBundleExportsImportsTest.java
index 4600dfc..a0492fa 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsBundleExportsImportsTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsBundleExportsImportsTest.java
@@ -18,8 +18,6 @@
  */
 package org.apache.sling.feature.extension.apiregions.analyser;
 
-import static org.junit.Assert.assertEquals;
-
 import java.io.File;
 import java.io.IOException;
 import java.util.Collections;
@@ -41,6 +39,7 @@ import org.apache.sling.feature.scanner.impl.FeatureDescriptorImpl;
 import org.junit.BeforeClass;
 import org.junit.Test;
 import org.mockito.Mockito;
+import static org.junit.Assert.assertEquals;
 
 public class CheckApiRegionsBundleExportsImportsTest {
     private static File resourceRoot;
@@ -131,7 +130,7 @@ public class CheckApiRegionsBundleExportsImportsTest {
      * different region, bundle 2 is in no region.
      */
     public void testImportExportWithRegionsMissing() throws Exception {
-        String exJson = "[{\"name\": \"something\", \"exports\": [\"org.foo.b\"]}]";
+        String exJson = "[{\"name\": \"something\", \"exports\": [\"org.foo.b\"],\"feature-origins\":[\"f:f:1\"]}]";
 
         CheckApiRegionsBundleExportsImports t = new CheckApiRegionsBundleExportsImports();
 
@@ -142,15 +141,12 @@ public class CheckApiRegionsBundleExportsImportsTest {
 
         FeatureDescriptor fd = new FeatureDescriptorImpl(f);
 
-        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar");
-        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar");
+        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar", f.getId());
+        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar", ArtifactId.fromMvnId("f:f2:1"));
 
         AnalyserTaskContext ctx = Mockito.mock(AnalyserTaskContext.class);
         Mockito.when(ctx.getFeature()).thenReturn(f);
         Mockito.when(ctx.getFeatureDescriptor()).thenReturn(fd);
-        Mockito.when(ctx.getConfiguration()).thenReturn(
-                Collections.singletonMap("fileStorage",
-                        resourceRoot + "/origins/testImportExportWithRegionsMissing"));
         t.execute(ctx);
 
         Mockito.verify(ctx).reportError(Mockito.contains("org.foo.b"));
@@ -164,7 +160,12 @@ public class CheckApiRegionsBundleExportsImportsTest {
      * region, bundle 1 is in something region, and bundle 2 is in somethingelse region.
      */
     public void testImportExportWithRegionMismatch() throws Exception {
-        String exJson = "[{\"name\": \"something\", \"exports\": [\"org.foo.b\"]}]";
+        String exJson =
+            "["
+                + "{\"name\": \"something\", \"exports\": [\"org.foo.b\"],\"feature-origins\":[\"f:f:1\"]},"
+                + "{\"name\": \"someotherthing\", \"exports\": [],\"feature-origins\":[\"f:f:1\"]},"
+                + "{\"name\": \"somethingelse\", \"exports\": [],\"feature-origins\":[\"f:f2:1\"]}"
+            + "]";
 
         CheckApiRegionsBundleExportsImports t = new CheckApiRegionsBundleExportsImports();
 
@@ -175,11 +176,10 @@ public class CheckApiRegionsBundleExportsImportsTest {
 
         FeatureDescriptor fd = new FeatureDescriptorImpl(f);
 
-        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar");
-        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar");
+        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar", fd.getFeature().getId());
+        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar", ArtifactId.fromMvnId("f:f2:1"));
 
         Map<String, String> cfgMap = new HashMap<String, String>();
-        cfgMap.put("fileStorage", resourceRoot + "/origins/testImportExportWithRegionMismatch");
         cfgMap.put("ignoreAPIRegions", "false");
 
         AnalyserTaskContext ctx = Mockito.mock(AnalyserTaskContext.class);
@@ -202,7 +202,12 @@ public class CheckApiRegionsBundleExportsImportsTest {
      * However this should still pass as the analyzer is configured to ignore regions.
      */
     public void testImportExportWithRegionMismatchIgnoreRegions() throws Exception {
-        String exJson = "[{\"name\": \"something\", \"exports\": [\"org.foo.b\"]}]";
+        String exJson =
+            "["
+                + "{\"name\": \"something\", \"exports\": [\"org.foo.b\"],\"feature-origins\":[\"f:f:1\"]},"
+                + "{\"name\": \"someotherthing\", \"exports\": [],\"feature-origins\":[\"f:f:1\"]},"
+                + "{\"name\": \"somethingelse\", \"exports\": [],\"feature-origins\":[\"f:f2:1\"]}"
+                + "]";
 
         CheckApiRegionsBundleExportsImports t = new CheckApiRegionsBundleExportsImports();
 
@@ -213,11 +218,10 @@ public class CheckApiRegionsBundleExportsImportsTest {
 
         FeatureDescriptor fd = new FeatureDescriptorImpl(f);
 
-        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar");
-        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar");
+        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar", fd.getFeature().getId());
+        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar", ArtifactId.fromMvnId("f:f2:1"));
 
         Map<String, String> cfgMap = new HashMap<String, String>();
-        cfgMap.put("fileStorage", resourceRoot + "/origins/testImportExportWithRegionMismatch");
         cfgMap.put("ignoreAPIRegions", "true");
 
         AnalyserTaskContext ctx = Mockito.mock(AnalyserTaskContext.class);
@@ -237,7 +241,12 @@ public class CheckApiRegionsBundleExportsImportsTest {
      * all these bundles are in the same feature they can all see each other.
      */
     public void testImportFromOtherBundleInSameFeature() throws Exception {
-        String exJson = "[{\"name\": \"blah\"}]"; // no exports
+        String exJson = "[{\"name\": \"blah\",\"feature-origins\":[\"f:f:2:1\",\"f:f3:1\"]}" +
+            ",{\"name\": \"something\",\"feature-origins\":[\"f:f:1\"]}" +
+            ",{\"name\": \"someotherthing\",\"feature-origins\":[\"f:f:1\"]}" +
+            ",{\"name\": \"abc\",\"feature-origins\":[\"f:2:1\"]}" +
+            ",{\"name\": \"xyz\",\"feature-origins\":[\"f:f2:1\"]}" +
+            "]"; // no exports
 
         CheckApiRegionsBundleExportsImports t = new CheckApiRegionsBundleExportsImports();
 
@@ -248,16 +257,13 @@ public class CheckApiRegionsBundleExportsImportsTest {
 
         FeatureDescriptor fd = new FeatureDescriptorImpl(f);
 
-        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar");
-        fdAddBundle(fd, "g:b3:1", "test-bundle3.jar");
-        fdAddBundle(fd, "g:b4:1", "test-bundle4.jar");
+        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar", ArtifactId.fromMvnId("f:f3:1"));
+        fdAddBundle(fd, "g:b3:1", "test-bundle3.jar", ArtifactId.fromMvnId("f:f3:1"));
+        fdAddBundle(fd, "g:b4:1", "test-bundle4.jar", ArtifactId.fromMvnId("f:f3:1"));
 
         AnalyserTaskContext ctx = Mockito.mock(AnalyserTaskContext.class);
         Mockito.when(ctx.getFeature()).thenReturn(f);
         Mockito.when(ctx.getFeatureDescriptor()).thenReturn(fd);
-        Mockito.when(ctx.getConfiguration()).thenReturn(
-                Collections.singletonMap("fileStorage",
-                        resourceRoot + "/origins/testImportFromOtherBundleInSameFeature"));
         t.execute(ctx);
 
         Mockito.verify(ctx, Mockito.never()).reportError(Mockito.anyString());
@@ -270,7 +276,8 @@ public class CheckApiRegionsBundleExportsImportsTest {
      * and bundle 2 imports it in the something region, so this succeeds.
      */
     public void testImportExportWithMatchingRegion() throws Exception {
-        String exJson = "[{\"name\": \"something\", \"exports\": [\"org.foo.b\"]}]";
+        String exJson = "[{\"name\": \"something\", \"exports\": [\"org.foo.b\"],\"feature-origins\":[\"f:f:1\",\"f:f2:1\"]}" +
+            ",{\"name\": \"someotherthing\", \"exports\": [\"org.foo.b\"],\"feature-origins\":[\"f:f:1\"]}]";
 
         CheckApiRegionsBundleExportsImports t = new CheckApiRegionsBundleExportsImports();
 
@@ -281,15 +288,12 @@ public class CheckApiRegionsBundleExportsImportsTest {
 
         FeatureDescriptor fd = new FeatureDescriptorImpl(f);
 
-        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar");
-        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar");
+        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar", f.getId());
+        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar", ArtifactId.fromMvnId("f:f2:1"));
 
         AnalyserTaskContext ctx = Mockito.mock(AnalyserTaskContext.class);
         Mockito.when(ctx.getFeature()).thenReturn(f);
         Mockito.when(ctx.getFeatureDescriptor()).thenReturn(fd);
-        Mockito.when(ctx.getConfiguration()).thenReturn(
-                Collections.singletonMap("fileStorage",
-                        resourceRoot + "/origins/testImportExportWithMatchingRegion"));
         t.execute(ctx);
 
         Mockito.verify(ctx, Mockito.never()).reportError(Mockito.anyString());
@@ -302,7 +306,11 @@ public class CheckApiRegionsBundleExportsImportsTest {
      * Bundle 2 is not explicitly part of the global region, but can still see it
      */
     public void testImportFromGlobalAlwaysSucceeds() throws Exception {
-        String exJson = "[{\"name\": \"global\", \"exports\": [\"org.foo.b\"]}]";
+        String exJson = "[{\"name\": \"global\", \"exports\": [\"org.foo.b\"],\"feature-origins\":[\"f:f:1\"]}" +
+            ",{\"name\": \"something\", \"exports\": [],\"feature-origins\":[\"f:f2:1\"]}" +
+            ",{\"name\": \"lalala\", \"exports\": [],\"feature-origins\":[\"f:f:1\"]}" +
+            ",{\"name\": \"someotherthing\", \"exports\": [],\"feature-origins\":[\"f:f:1\"]}]"
+            ;
 
         CheckApiRegionsBundleExportsImports t = new CheckApiRegionsBundleExportsImports();
 
@@ -313,8 +321,8 @@ public class CheckApiRegionsBundleExportsImportsTest {
 
         FeatureDescriptor fd = new FeatureDescriptorImpl(f);
 
-        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar");
-        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar");
+        fdAddBundle(fd, "g:b1:1", "test-bundle1.jar",f.getId());
+        fdAddBundle(fd, "g:b2:1", "test-bundle2.jar",ArtifactId.fromMvnId("f:f2:1"));
 
         AnalyserTaskContext ctx = Mockito.mock(AnalyserTaskContext.class);
         Mockito.when(ctx.getFeature()).thenReturn(f);
@@ -328,9 +336,11 @@ public class CheckApiRegionsBundleExportsImportsTest {
         Mockito.verify(ctx, Mockito.never()).reportWarning(Mockito.anyString());
     }
 
-    private void fdAddBundle(FeatureDescriptor fd, String id, String file) throws IOException {
+    private void fdAddBundle(FeatureDescriptor fd, String id, String file, ArtifactId... origins) throws IOException {
+        Artifact artifact = new Artifact(ArtifactId.fromMvnId(id));
+        artifact.setFeatureOrigins(origins);
         BundleDescriptor bd1 = new BundleDescriptorImpl(
-                new Artifact(ArtifactId.fromMvnId(id)), new File(resourceRoot, file).toURI().toURL(), 0);
+                artifact, new File(resourceRoot, file).toURI().toURL(), 0);
         fd.getBundleDescriptors().add(bd1);
     }
 }
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsDependenciesTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsDependenciesTest.java
index e3cc401..bde744d 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsDependenciesTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsDependenciesTest.java
@@ -16,13 +16,12 @@
  */
 package org.apache.sling.feature.extension.apiregions.analyser;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.util.List;
 
 import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 public class CheckApiRegionsDependenciesTest extends AbstractApiRegionsAnalyserTaskTest<CheckApiRegionsDependencies> {
 
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsDuplicatesTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsDuplicatesTest.java
index 72b5158..66cc33b 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsDuplicatesTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsDuplicatesTest.java
@@ -16,13 +16,12 @@
  */
 package org.apache.sling.feature.extension.apiregions.analyser;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.util.List;
 
 import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 public class CheckApiRegionsDuplicatesTest extends AbstractApiRegionsAnalyserTaskTest<CheckApiRegionsDuplicates> {
 
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsTest.java b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsTest.java
index 9d9a5d8..ba2a26a 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsTest.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/analyser/CheckApiRegionsTest.java
@@ -16,12 +16,11 @@
  */
 package org.apache.sling.feature.extension.apiregions.analyser;
 
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-
 import java.util.List;
 
 import org.junit.Test;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
 
 public class CheckApiRegionsTest extends AbstractApiRegionsAnalyserTaskTest<CheckApiRegions> {
 
diff --git a/src/test/java/org/apache/sling/feature/extension/apiregions/api/TestApiRegions.java b/src/test/java/org/apache/sling/feature/extension/apiregions/api/TestApiRegions.java
index 3db71ae..ee152a2 100644
--- a/src/test/java/org/apache/sling/feature/extension/apiregions/api/TestApiRegions.java
+++ b/src/test/java/org/apache/sling/feature/extension/apiregions/api/TestApiRegions.java
@@ -16,12 +16,6 @@
  */
 package org.apache.sling.feature.extension.apiregions.api;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertNull;
-import static org.junit.Assert.assertTrue;
-
 import java.io.IOException;
 import java.io.InputStreamReader;
 import java.io.Reader;
@@ -29,6 +23,11 @@ import java.io.StringWriter;
 import java.io.Writer;
 
 import org.junit.Test;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
 
 public class TestApiRegions {
 
diff --git a/src/test/resources/origins/testImportExportWithMatchingRegion/f_f_1/bundleOrigins.properties b/src/test/resources/origins/testImportExportWithMatchingRegion/f_f_1/bundleOrigins.properties
deleted file mode 100644
index 788c4fa..0000000
--- a/src/test/resources/origins/testImportExportWithMatchingRegion/f_f_1/bundleOrigins.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-g\:b1\:1=f\:f1\:1
-g\:b2\:1=f\:f2\:1
diff --git a/src/test/resources/origins/testImportExportWithMatchingRegion/f_f_1/regionOrigins.properties b/src/test/resources/origins/testImportExportWithMatchingRegion/f_f_1/regionOrigins.properties
deleted file mode 100644
index ceb9131..0000000
--- a/src/test/resources/origins/testImportExportWithMatchingRegion/f_f_1/regionOrigins.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-f\:f1\:1=something,someotherthing
-f\:f2\:1=something
diff --git a/src/test/resources/origins/testImportExportWithRegionMismatch/f_f_1/bundleOrigins.properties b/src/test/resources/origins/testImportExportWithRegionMismatch/f_f_1/bundleOrigins.properties
deleted file mode 100644
index 788c4fa..0000000
--- a/src/test/resources/origins/testImportExportWithRegionMismatch/f_f_1/bundleOrigins.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-g\:b1\:1=f\:f1\:1
-g\:b2\:1=f\:f2\:1
diff --git a/src/test/resources/origins/testImportExportWithRegionMismatch/f_f_1/regionOrigins.properties b/src/test/resources/origins/testImportExportWithRegionMismatch/f_f_1/regionOrigins.properties
deleted file mode 100644
index 3134d99..0000000
--- a/src/test/resources/origins/testImportExportWithRegionMismatch/f_f_1/regionOrigins.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-f\:f1\:1=something,someotherthing
-f\:f2\:1=somethingelse
diff --git a/src/test/resources/origins/testImportExportWithRegionMismatchIgnoreRegions/f_f_1/bundleOrigins.properties b/src/test/resources/origins/testImportExportWithRegionMismatchIgnoreRegions/f_f_1/bundleOrigins.properties
deleted file mode 100644
index 788c4fa..0000000
--- a/src/test/resources/origins/testImportExportWithRegionMismatchIgnoreRegions/f_f_1/bundleOrigins.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-g\:b1\:1=f\:f1\:1
-g\:b2\:1=f\:f2\:1
diff --git a/src/test/resources/origins/testImportExportWithRegionMismatchIgnoreRegions/f_f_1/regionOrigins.properties b/src/test/resources/origins/testImportExportWithRegionMismatchIgnoreRegions/f_f_1/regionOrigins.properties
deleted file mode 100644
index 3134d99..0000000
--- a/src/test/resources/origins/testImportExportWithRegionMismatchIgnoreRegions/f_f_1/regionOrigins.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-f\:f1\:1=something,someotherthing
-f\:f2\:1=somethingelse
diff --git a/src/test/resources/origins/testImportExportWithRegionsMissing/f_f_1/bundleOrigins.properties b/src/test/resources/origins/testImportExportWithRegionsMissing/f_f_1/bundleOrigins.properties
deleted file mode 100644
index 788c4fa..0000000
--- a/src/test/resources/origins/testImportExportWithRegionsMissing/f_f_1/bundleOrigins.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-g\:b1\:1=f\:f1\:1
-g\:b2\:1=f\:f2\:1
diff --git a/src/test/resources/origins/testImportExportWithRegionsMissing/f_f_1/regionOrigins.properties b/src/test/resources/origins/testImportExportWithRegionsMissing/f_f_1/regionOrigins.properties
deleted file mode 100644
index 019a972..0000000
--- a/src/test/resources/origins/testImportExportWithRegionsMissing/f_f_1/regionOrigins.properties
+++ /dev/null
@@ -1 +0,0 @@
-f\:f1\:1=something
diff --git a/src/test/resources/origins/testImportFromGlobalAlwaysSucceeds/f_f_1/bundleOrigins.properties b/src/test/resources/origins/testImportFromGlobalAlwaysSucceeds/f_f_1/bundleOrigins.properties
deleted file mode 100644
index 788c4fa..0000000
--- a/src/test/resources/origins/testImportFromGlobalAlwaysSucceeds/f_f_1/bundleOrigins.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-g\:b1\:1=f\:f1\:1
-g\:b2\:1=f\:f2\:1
diff --git a/src/test/resources/origins/testImportFromGlobalAlwaysSucceeds/f_f_1/regionOrigins.properties b/src/test/resources/origins/testImportFromGlobalAlwaysSucceeds/f_f_1/regionOrigins.properties
deleted file mode 100644
index a94edd0..0000000
--- a/src/test/resources/origins/testImportFromGlobalAlwaysSucceeds/f_f_1/regionOrigins.properties
+++ /dev/null
@@ -1,2 +0,0 @@
-f\:f1\:1=someotherthing,global,lalala
-f\:f2\:1=something
diff --git a/src/test/resources/origins/testImportFromOtherBundleInSameFeature/f_f_2/bundleOrigins.properties b/src/test/resources/origins/testImportFromOtherBundleInSameFeature/f_f_2/bundleOrigins.properties
deleted file mode 100644
index 7910020..0000000
--- a/src/test/resources/origins/testImportFromOtherBundleInSameFeature/f_f_2/bundleOrigins.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-g\:b1\:1=f\:f3\:1
-g\:b2\:1=f\:f3\:1
-g\:b3\:1=f\:f3\:1
-g\:b4\:1=f\:f3\:1
diff --git a/src/test/resources/origins/testImportFromOtherBundleInSameFeature/f_f_2/regionOrigins.properties b/src/test/resources/origins/testImportFromOtherBundleInSameFeature/f_f_2/regionOrigins.properties
deleted file mode 100644
index 3488a06..0000000
--- a/src/test/resources/origins/testImportFromOtherBundleInSameFeature/f_f_2/regionOrigins.properties
+++ /dev/null
@@ -1,3 +0,0 @@
-f\:f1\:1=something,someotherthing
-f\:f2\:1=abc,xyz
-f\:f3\:1=blah