Move services to unpack extension
diff --git a/src/main/java/org/apache/sling/feature/spi/context/package-info.java b/src/main/java/org/apache/sling/feature/spi/context/package-info.java
new file mode 100644
index 0000000..8d0d1e1
--- /dev/null
+++ b/src/main/java/org/apache/sling/feature/spi/context/package-info.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+@org.osgi.annotation.versioning.ProviderType
+@org.osgi.annotation.versioning.Version("1.0.0")
+package org.apache.sling.feature.spi.context;
+
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/BinaryArtifactExtensionHandler.java b/src/main/java/org/apache/sling/installer/factory/model/impl/BinaryArtifactExtensionHandler.java
deleted file mode 100644
index 077c271..0000000
--- a/src/main/java/org/apache/sling/installer/factory/model/impl/BinaryArtifactExtensionHandler.java
+++ /dev/null
@@ -1,89 +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.installer.factory.model.impl;
-
-import org.apache.felix.utils.manifest.Clause;
-import org.apache.felix.utils.manifest.Directive;
-import org.apache.felix.utils.manifest.Parser;
-import org.apache.sling.feature.Artifact;
-import org.apache.sling.feature.Extension;
-import org.apache.sling.feature.ExtensionType;
-import org.apache.sling.feature.Feature;
-import org.apache.sling.feature.spi.context.ExtensionHandler;
-import org.apache.sling.feature.spi.context.ExtensionHandlerContext;
-import org.osgi.framework.BundleContext;
-import org.osgi.service.component.annotations.Activate;
-import org.osgi.service.component.annotations.Component;
-
-import java.net.URL;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Hashtable;
-import java.util.Map;
-
-@Component
-public class BinaryArtifactExtensionHandler implements ExtensionHandler {
-    private static final String BINARY_EXTENSIONS_PROP = "org.apache.sling.feature.binary.extensions";
-    private static final String UNZIP_ARTIFACT_MARKER = "Unzip-Artifact";
-
-    private final Map<String, Map<String, String>> binaryExtensions;
-
-    @Activate
-    public BinaryArtifactExtensionHandler(BundleContext bc) {
-        Map<String, Map<String, String>> be = new HashMap<>();
-
-        // Syntax: system-fonts;dir:=abc;overwrite:=true,customer-fonts;dir:=eft
-        Clause[] extClauses = Parser.parseHeader(bc.getProperty(BINARY_EXTENSIONS_PROP));
-        for (Clause c : extClauses) {
-            Map<String,String> cfg = new HashMap<>();
-
-            for (Directive d : c.getDirectives()) {
-                cfg.put(d.getName(), d.getValue());
-            }
-            be.put(c.getName(), Collections.unmodifiableMap(cfg));
-        }
-
-        binaryExtensions = Collections.unmodifiableMap(be);
-    }
-
-    @Override
-    public boolean handle(ExtensionHandlerContext context, Extension extension, Feature feature) throws Exception {
-        if (extension.getType() != ExtensionType.ARTIFACTS ||
-                binaryExtensions.get(extension.getName()) == null) {
-            return false;
-        } else {
-            Map<String,String> extensionConfig = binaryExtensions.getOrDefault(extension.getName(),
-                    Collections.emptyMap());
-
-            for (Artifact art : extension.getArtifacts()) {
-                URL artifact = context.getArtifactProvider().provide(art.getId());
-
-                Hashtable<String,Object> props = new Hashtable<>();
-                props.put(UNZIP_ARTIFACT_MARKER, Boolean.TRUE);
-                // the props.computeIfAbsent() ensures that entries aren't put in map if they have no value
-                props.computeIfAbsent("dir", v -> extensionConfig.get("dir"));
-                props.computeIfAbsent("overwrite", v -> extensionConfig.get("overwrite"));
-
-                context.addInstallableArtifact(art.getId(), artifact, props);
-            }
-            return true;
-        }
-    }
-}
-
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/BinaryPackageInstallerPlugin.java b/src/main/java/org/apache/sling/installer/factory/model/impl/BinaryPackageInstallerPlugin.java
deleted file mode 100644
index f56f829..0000000
--- a/src/main/java/org/apache/sling/installer/factory/model/impl/BinaryPackageInstallerPlugin.java
+++ /dev/null
@@ -1,58 +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.installer.factory.model.impl;
-
-import org.apache.sling.installer.api.tasks.InstallTask;
-import org.apache.sling.installer.api.tasks.InstallTaskFactory;
-import org.apache.sling.installer.api.tasks.RegisteredResource;
-import org.apache.sling.installer.api.tasks.ResourceTransformer;
-import org.apache.sling.installer.api.tasks.TaskResourceGroup;
-import org.apache.sling.installer.api.tasks.TransformationResult;
-import org.osgi.service.component.annotations.Component;
-import org.osgi.service.metatype.annotations.Designate;
-import org.osgi.service.metatype.annotations.ObjectClassDefinition;
-
-@Component(service = { InstallTaskFactory.class, ResourceTransformer.class })
-@Designate(ocd = BinaryPackageInstallerPlugin.Config.class)
-public class BinaryPackageInstallerPlugin implements InstallTaskFactory, ResourceTransformer {
-
-    @ObjectClassDefinition(name = "Binary Package Installer",
-            description = "This component supports installing binary packages into the OSGi installer")
-    public @interface Config {
-        String directory();
-
-        String overwrite() default "true";
-    }
-
-    @Override
-    public TransformationResult[] transform(RegisteredResource resource) {
-
-        if (Boolean.TRUE.equals(resource.getDictionary().get("Unzip-Artifact"))) {
-            return new TransformationResult[] {};
-        }
-        return null;
-    }
-
-    @Override
-    public InstallTask createTask(TaskResourceGroup group) {
-        // TODO Auto-generated method stub
-        return null;
-    }
-
-}
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/InstallFeatureModelTask.java b/src/main/java/org/apache/sling/installer/factory/model/impl/InstallFeatureModelTask.java
index 719a29f..3b4a60d 100644
--- a/src/main/java/org/apache/sling/installer/factory/model/impl/InstallFeatureModelTask.java
+++ b/src/main/java/org/apache/sling/installer/factory/model/impl/InstallFeatureModelTask.java
@@ -132,6 +132,33 @@
                     cfg.getConfigurationProperties(), null, InstallableResource.TYPE_CONFIG, null));
         }
 
+        // extract artifacts
+        if (this.installContext.storageDirectory != null) {
+            final byte[] buffer = new byte[1024*1024*256];
+
+            try ( final InputStream is = rsrc.getInputStream() ) {
+                ArchiveReader.read(is, new ArchiveReader.ArtifactConsumer() {
+
+                    @Override
+                    public void consume(final ArtifactId id, final InputStream is) throws IOException {
+                        final File artifactFile = getArtifactFile(installContext.storageDirectory, id);
+                        if (!artifactFile.exists()) {
+                            artifactFile.getParentFile().mkdirs();
+                            try (final OutputStream os = new FileOutputStream(artifactFile)) {
+                                int l = 0;
+                                while ((l = is.read(buffer)) > 0) {
+                                    os.write(buffer, 0, l);
+                                }
+                            }
+                        }
+                    }
+                });
+            } catch ( final IOException ioe) {
+                logger.warn("Unable to extract artifacts from feature model " + feature.getId().toMvnId(), ioe);
+                return null;
+            }
+        }
+
         ExtensionHandlerContext context = new ContextImpl(result);
 
         for (Extension ext : feature.getExtensions()) {
@@ -170,33 +197,6 @@
         }
         */
 
-        // extract artifacts
-        if (this.installContext.storageDirectory != null) {
-            final byte[] buffer = new byte[1024*1024*256];
-
-            try ( final InputStream is = rsrc.getInputStream() ) {
-                ArchiveReader.read(is, new ArchiveReader.ArtifactConsumer() {
-
-                    @Override
-                    public void consume(final ArtifactId id, final InputStream is) throws IOException {
-                        final File artifactFile = getArtifactFile(installContext.storageDirectory, id);
-                        if (!artifactFile.exists()) {
-                            artifactFile.getParentFile().mkdirs();
-                            try (final OutputStream os = new FileOutputStream(artifactFile)) {
-                                int l = 0;
-                                while ((l = is.read(buffer)) > 0) {
-                                    os.write(buffer, 0, l);
-                                }
-                            }
-                        }
-                    }
-                });
-            } catch ( final IOException ioe) {
-                logger.warn("Unable to extract artifacts from feature model " + feature.getId().toMvnId(), ioe);
-                return null;
-            }
-        }
-
         /* done by APIRegionsExtensionHandler
         // api regions
         final Extension regionExt = feature.getExtensions().getByName(ApiRegions.EXTENSION_NAME);
@@ -283,11 +283,41 @@
         }
         return true;
     }
+
     @Override
     public String getSortKey() {
         return "30-" + getResource().getAttribute(FeatureModelInstallerPlugin.ATTR_ID);
     }
 
+    private ArtifactProvider getLocalArtifactProvider() {
+        // TODO share with addArtifact()
+        return new ArtifactProvider() {
+            @Override
+            public URL provide(ArtifactId id) {
+                File artifactFile = (installContext.storageDirectory == null ? null
+                        : getArtifactFile(installContext.storageDirectory, id));
+                ArtifactHandler handler;
+                if (artifactFile == null || !artifactFile.exists()) {
+                    try {
+                        handler = installContext.artifactManager.getArtifactHandler(id.toMvnUrl());
+                    } catch (final IOException ignore) {
+                        return null;
+                    }
+                } else {
+                    try {
+                        handler = new ArtifactHandler(artifactFile);
+                    } catch (final MalformedURLException e) {
+                        return null;
+                    }
+                }
+                if (handler == null) {
+                    return null;
+                }
+                return handler.getLocalURL();
+            }
+        };
+    }
+
     private class ContextImpl implements ExtensionHandlerContext {
         private final List<InstallableResource> results;
 
@@ -325,7 +355,7 @@
 
         @Override
         public ArtifactProvider getArtifactProvider() {
-            return installContext.artifactManager;
+            return getLocalArtifactProvider();
         }
     }
 }