SLING-7968: Use FeatureProvider instead of ArtifactManager.
diff --git a/src/main/java/org/apache/sling/feature/modelconverter/FeatureToProvisioning.java b/src/main/java/org/apache/sling/feature/modelconverter/FeatureToProvisioning.java
index 24fc3b7..47b089c 100644
--- a/src/main/java/org/apache/sling/feature/modelconverter/FeatureToProvisioning.java
+++ b/src/main/java/org/apache/sling/feature/modelconverter/FeatureToProvisioning.java
@@ -17,17 +17,22 @@
 package org.apache.sling.feature.modelconverter;
 
 import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.FileReader;
+import java.io.FileInputStream;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
 import java.io.StringReader;
+import java.io.UncheckedIOException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Enumeration;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 import javax.json.Json;
 import javax.json.JsonArray;
@@ -46,8 +51,6 @@
 import org.apache.sling.feature.builder.BuilderContext;
 import org.apache.sling.feature.builder.FeatureBuilder;
 import org.apache.sling.feature.builder.FeatureProvider;
-import org.apache.sling.feature.io.file.ArtifactHandler;
-import org.apache.sling.feature.io.file.ArtifactManager;
 import org.apache.sling.feature.io.json.FeatureJSONReader;
 import org.apache.sling.provisioning.model.Artifact;
 import org.apache.sling.provisioning.model.Configuration;
@@ -65,7 +68,7 @@
     static final String PROVISIONING_MODEL_NAME_VARIABLE = "provisioning.model.name";
     static final String PROVISIONING_RUNMODES = "provisioning.runmodes";
 
-    public static void convert(File inputFile, File outputFile, ArtifactManager am, File ... additionalInputFiles) throws IOException {
+    public static void convert(File inputFile, File outputFile, FeatureProvider fp, File ... additionalInputFiles) throws UncheckedIOException {
         if (outputFile.exists()) {
             if (outputFile.lastModified() > inputFile.lastModified()) {
                 LOGGER.debug("Skipping the generation of {} as this file already exists and is newer.", outputFile);
@@ -73,9 +76,9 @@
             }
         }
 
-        org.apache.sling.feature.Feature feature = getFeature(inputFile.getAbsolutePath(), am);
+        org.apache.sling.feature.Feature feature = getFeature(inputFile);
         if (feature.getInclude() != null) {
-            feature = handleIncludes(feature, additionalInputFiles, am);
+            feature = handleIncludes(feature, additionalInputFiles, fp);
         }
 
         Object featureNameVar = feature.getVariables().remove(PROVISIONING_MODEL_NAME_VARIABLE);
@@ -97,49 +100,23 @@
                 feature.getFrameworkProperties(), feature.getExtensions(), outputFile.getAbsolutePath(), runModes);
     }
 
-    static org.apache.sling.feature.Feature getFeature(final String url, final ArtifactManager am) throws FileNotFoundException, IOException {
-        final ArtifactHandler featureArtifact = am.getArtifactHandler(url);
-        try (final FileReader r = new FileReader(featureArtifact.getFile())) {
-            final org.apache.sling.feature.Feature f = FeatureJSONReader.read(r, featureArtifact.getUrl());
-            return f;
+    static org.apache.sling.feature.Feature getFeature(final File file) throws UncheckedIOException {
+        try (final Reader r = new InputStreamReader(new FileInputStream(file), "UTF-8")) {
+            return FeatureJSONReader.read(r, file.toURI().toURL().toString());
+        } catch (IOException ex) {
+            throw new UncheckedIOException(ex);
         }
 
     }
 
-    private static org.apache.sling.feature.Feature handleIncludes(org.apache.sling.feature.Feature feature, File[] additionalFiles, ArtifactManager am) throws IOException {
-        Map<ArtifactId, org.apache.sling.feature.Feature> features = new HashMap<>();
+    private static org.apache.sling.feature.Feature handleIncludes(org.apache.sling.feature.Feature feature, File[] additionalFiles, FeatureProvider fp) throws UncheckedIOException {
+        Map<ArtifactId, org.apache.sling.feature.Feature> features =
+            Stream.of(additionalFiles)
+                .map(FeatureToProvisioning::getFeature)
+                .collect(Collectors.toMap(org.apache.sling.feature.Feature::getId, Function.identity()));
 
-        for (File f : additionalFiles) {
-            org.apache.sling.feature.Feature af = getFeature(f.getAbsolutePath(), am);
-            features.put(af.getId(), af);
-        }
+        BuilderContext bc = new BuilderContext(id -> features.containsKey(id) ? features.get(id) : fp.provide(id));
 
-        BuilderContext bc = new BuilderContext(new FeatureProvider() {
-            @Override
-            public org.apache.sling.feature.Feature provide(ArtifactId id) {
-                // Check first if the feature is part of the provided context
-                org.apache.sling.feature.Feature f = features.get(id);
-                if (f != null) {
-                    return f;
-                }
-
-                // If not, see if it is known to Maven
-                try {
-                    ArtifactHandler ah = am.getArtifactHandler(id.toMvnUrl());
-                    if (ah != null) {
-                        org.apache.sling.feature.Feature feat = getFeature(ah.getUrl(), am);
-                        if (feat != null) {
-                            // Cache it
-                            features.put(feat.getId(), feat);
-                        }
-                        return feat;
-                    }
-                } catch (IOException e) {
-                    throw new RuntimeException(e);
-                }
-                return null;
-            }
-        });
         return FeatureBuilder.assemble(feature, bc);
     }
 
diff --git a/src/test/java/org/apache/sling/feature/modelconverter/ModelConverterTest.java b/src/test/java/org/apache/sling/feature/modelconverter/ModelConverterTest.java
index 27e9262..50cfe88 100644
--- a/src/test/java/org/apache/sling/feature/modelconverter/ModelConverterTest.java
+++ b/src/test/java/org/apache/sling/feature/modelconverter/ModelConverterTest.java
@@ -25,6 +25,8 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
+import java.io.Reader;
+import java.io.UncheckedIOException;
 import java.net.URISyntaxException;
 import java.nio.file.Files;
 import java.nio.file.Path;
@@ -42,14 +44,17 @@
 import java.util.List;
 import java.util.Map;
 
+import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Bundles;
 import org.apache.sling.feature.Configurations;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.Extensions;
+import org.apache.sling.feature.builder.FeatureProvider;
 import org.apache.sling.feature.io.file.ArtifactHandler;
 import org.apache.sling.feature.io.file.ArtifactManager;
 import org.apache.sling.feature.io.file.ArtifactManagerConfig;
+import org.apache.sling.feature.io.json.FeatureJSONReader;
 import org.apache.sling.provisioning.model.Artifact;
 import org.apache.sling.provisioning.model.ArtifactGroup;
 import org.apache.sling.provisioning.model.Configuration;
@@ -74,6 +79,7 @@
 public class ModelConverterTest {
     private Path tempDir;
     private ArtifactManager artifactManager;
+    private FeatureProvider featureProvider;
 
     @Before
     public void setup() throws Exception {
@@ -86,6 +92,17 @@
         }
         artifactManager = ArtifactManager.getArtifactManager(
                 new ArtifactManagerConfig());
+        featureProvider =
+            id -> {
+                try {
+                    File file = artifactManager.getArtifactHandler(id.toMvnUrl()).getFile();
+                    try (Reader reader = new FileReader(file)) {
+                        return FeatureJSONReader.read(reader, file.toURI().toURL().toString());
+                    }
+                } catch (IOException e) {
+                    throw new UncheckedIOException(e);
+                }
+            };
     }
 
     @After
@@ -250,9 +267,9 @@
         assertEquals("The testing code expects a single output file here", 1, files.size());
         File outFile = files.get(0);
 
-        String expectedFile = new File(getClass().getResource(expectedJSON).toURI()).getAbsolutePath();
-        org.apache.sling.feature.Feature expected = FeatureToProvisioning.getFeature(expectedFile, artifactManager);
-        org.apache.sling.feature.Feature actual = FeatureToProvisioning.getFeature(outFile.getAbsolutePath(), artifactManager);
+        File expectedFile = new File(getClass().getResource(expectedJSON).toURI());
+        org.apache.sling.feature.Feature expected = FeatureToProvisioning.getFeature(expectedFile);
+        org.apache.sling.feature.Feature actual = FeatureToProvisioning.getFeature(outFile);
         assertFeaturesEqual(expected, actual);
     }
 
@@ -264,7 +281,7 @@
         File inFile = new File(getClass().getResource(originalJSON).toURI());
         File outFile = new File(tempDir.toFile(), expectedProvModel + ".generated");
 
-        FeatureToProvisioning.convert(inFile, outFile, artifactManager);
+        FeatureToProvisioning.convert(inFile, outFile, featureProvider);
         List<String> orgLines = Files.readAllLines(outFile.toPath());
         assertNotEquals("Test precondition", "modified!", orgLines.get(orgLines.size() - 1));
 
@@ -272,14 +289,14 @@
         Files.write(outFile.toPath(), "\nmodified!".getBytes(), StandardOpenOption.APPEND);
 
         // Convert again and see that the output file is not modified
-        FeatureToProvisioning.convert(inFile, outFile, artifactManager);
+        FeatureToProvisioning.convert(inFile, outFile, featureProvider);
 
         List<String> lines = Files.readAllLines(outFile.toPath());
         assertEquals("modified!", lines.get(lines.size() - 1));
 
         // Modify the modification time of the generated file to be older than the input file
         outFile.setLastModified(inFile.lastModified() - 100000);
-        FeatureToProvisioning.convert(inFile, outFile, artifactManager);
+        FeatureToProvisioning.convert(inFile, outFile, featureProvider);
 
         List<String> owLines = Files.readAllLines(outFile.toPath());
         assertEquals("The file should have been overwritten since the source has modified since it's edit timestamp",
@@ -333,7 +350,7 @@
             assertFalse("File name cannot contain a colon", baseName.contains(":"));
             File genFile = new File(tempDir.toFile(), baseName + ".txt");
             allGenerateProvisioningModelFiles.add(genFile);
-            FeatureToProvisioning.convert(f, genFile, artifactManager);
+            FeatureToProvisioning.convert(f, genFile, featureProvider);
         }
 
         Model expected = readProvisioningModel(orgProvModel);
@@ -348,9 +365,9 @@
         assertEquals("The testing code expects a single output file here", 1, files.size());
         File outFile = files.get(0);
 
-        String expectedFile = new File(getClass().getResource(expectedJSON).toURI()).getAbsolutePath();
-        org.apache.sling.feature.Feature expected = FeatureToProvisioning.getFeature(expectedFile, artifactManager);
-        org.apache.sling.feature.Feature actual = FeatureToProvisioning.getFeature(outFile.getAbsolutePath(), artifactManager);
+        File expectedFile = new File(getClass().getResource(expectedJSON).toURI());
+        org.apache.sling.feature.Feature expected = FeatureToProvisioning.getFeature(expectedFile);
+        org.apache.sling.feature.Feature actual = FeatureToProvisioning.getFeature(outFile);
         assertFeaturesEqual(expected, actual);
     }
 
@@ -362,7 +379,7 @@
             addFiles.add(new File(getClass().getResource(af).toURI()));
         }
 
-        FeatureToProvisioning.convert(inFile, outFile, artifactManager, addFiles.toArray(new File[] {}));
+        FeatureToProvisioning.convert(inFile, outFile, featureProvider, addFiles.toArray(new File[] {}));
 
         File expectedFile = new File(getClass().getResource(expectedProvModel).toURI());
         Model expected = readProvisioningModel(expectedFile);
@@ -404,7 +421,7 @@
                 outFile = new File(tempDir.toFile(), inFile.getName() + (counter++) + ".txt.generated");
             } while (outFile.exists());
 
-            FeatureToProvisioning.convert(inFile, outFile, artifactManager);
+            FeatureToProvisioning.convert(inFile, outFile, featureProvider);
             generatedFiles.add(outFile);
         }
         return generatedFiles;