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

[sling-org-apache-sling-installer-factory-feature] 03/32: Support multiple features per archive and allow for selection through pattern

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

davidb pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-installer-factory-feature.git

commit fed02f0bd831f2bb44b2b3641da21d47b4f985d6
Author: Carsten Ziegeler <cz...@apache.org>
AuthorDate: Sun Feb 23 17:34:30 2020 +0100

    Support multiple features per archive and allow for selection through pattern
---
 pom.xml                                            |  11 ++
 .../model/impl/FeatureModelInstallerPlugin.java    | 121 +++++++++++++-----
 .../factory/model/impl/InstallContext.java         |  50 ++++++++
 .../model/impl/InstallFeatureModelTask.java        | 141 +++++++++------------
 .../model/impl/UninstallFeatureModelTask.java      |   7 -
 .../impl/FeatureModelInstallerPluginTest.java      |  70 ++++++++++
 6 files changed, 278 insertions(+), 122 deletions(-)

diff --git a/pom.xml b/pom.xml
index 815d242..5991eae 100644
--- a/pom.xml
+++ b/pom.xml
@@ -115,6 +115,17 @@
             <version>1.3.0-SNAPSHOT</version>
             <scope>provided</scope>
         </dependency>
+   <!-- Testing -->
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>2.15.0</version>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
 </project>
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/FeatureModelInstallerPlugin.java b/src/main/java/org/apache/sling/installer/factory/model/impl/FeatureModelInstallerPlugin.java
index 69df21f..3023feb 100644
--- a/src/main/java/org/apache/sling/installer/factory/model/impl/FeatureModelInstallerPlugin.java
+++ b/src/main/java/org/apache/sling/installer/factory/model/impl/FeatureModelInstallerPlugin.java
@@ -19,11 +19,13 @@
 package org.apache.sling.installer.factory.model.impl;
 
 import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.io.Reader;
 import java.io.StringWriter;
+import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -32,6 +34,8 @@ import java.util.Map;
 import java.util.regex.Pattern;
 
 import org.apache.sling.feature.Feature;
+import org.apache.sling.feature.builder.BuilderContext;
+import org.apache.sling.feature.builder.FeatureBuilder;
 import org.apache.sling.feature.io.archive.ArchiveReader;
 import org.apache.sling.feature.io.artifacts.ArtifactManager;
 import org.apache.sling.feature.io.artifacts.ArtifactManagerConfig;
@@ -78,7 +82,8 @@ public class FeatureModelInstallerPlugin implements InstallTaskFactory, Resource
         @AttributeDefinition(name = "Repository URLs", description = "Additional repository URLs to fetch artifacts")
         String[] repositories();
 
-        @AttributeDefinition(name = "Classifier Patterns", description = "Patterns for selecting the features to handle based on the classifier. Without a configuration all features are handled.")
+        @AttributeDefinition(name = "Classifier Patterns", description = "Patterns for selecting the features to handle based on the classifier. Without a configuration all features are handled."
+                + " The patterns can use an asteriks to match any characters in the classifier. The special token ':' can be used to match the empty classifier.")
         String[] classifierPatterns();
     }
 
@@ -88,8 +93,6 @@ public class FeatureModelInstallerPlugin implements InstallTaskFactory, Resource
 
     public static final String ATTR_MODEL = "feature";
 
-    public static final String ATTR_BASE_PATH = "path";
-
     public static final String ATTR_ID = "featureId";
 
     /** Logger. */
@@ -108,27 +111,38 @@ public class FeatureModelInstallerPlugin implements InstallTaskFactory, Resource
 
     private final ArtifactManager artifactManager;
 
-    private final List<String> classifierPatterns = new ArrayList<>();
+    private final List<Pattern> classifierPatterns = new ArrayList<>();
+
+    private final File storageDirectory;
 
     @Activate
     public FeatureModelInstallerPlugin(final BundleContext ctx, final Config config) throws IOException {
         this.bundleContext = ctx;
+        this.storageDirectory = this.bundleContext.getDataFile("repository");
         final ArtifactManagerConfig amCfg = new ArtifactManagerConfig();
         amCfg.setUseMvn(config.useMvn());
+        final List<String> repos = new ArrayList<>(Arrays.asList(amCfg.getRepositoryUrls()));
+        if (this.storageDirectory != null) {
+            repos.add(this.storageDirectory.toURI().toURL().toExternalForm());
+        }
         if (config.repositories() != null && config.repositories().length > 0) {
-            final List<String> repos = new ArrayList<>(Arrays.asList(amCfg.getRepositoryUrls()));
             for (final String r : config.repositories()) {
                 if (!r.trim().isEmpty()) {
                     repos.add(r);
                 }
             }
-            amCfg.setRepositoryUrls(repos.toArray(new String[repos.size()]));
         }
+        amCfg.setRepositoryUrls(repos.toArray(new String[repos.size()]));
+
         this.artifactManager = ArtifactManager.getArtifactManager(amCfg);
         if (config.classifierPatterns() != null) {
             for (final String text : config.classifierPatterns()) {
                 if (text != null && !text.trim().isEmpty()) {
-                    classifierPatterns.add(text.trim());
+                    if (":".equals(text.trim())) {
+                        classifierPatterns.add(Pattern.compile("^$"));
+                    } else {
+                        classifierPatterns.add(Pattern.compile(toRegexPattern(text.trim())));
+                    }
                 }
             }
         }
@@ -137,7 +151,6 @@ public class FeatureModelInstallerPlugin implements InstallTaskFactory, Resource
     @Override
     public TransformationResult[] transform(final RegisteredResource resource) {
         final List<Feature> features = new ArrayList<>();
-        File baseDir = null;
         if (resource.getType().equals(InstallableResource.TYPE_FILE) && resource.getURL().endsWith(FILE_EXTENSION)) {
             try (final Reader reader = new InputStreamReader(resource.getInputStream(), "UTF-8")) {
                 features.add(FeatureJSONReader.read(reader, resource.getURL()));
@@ -146,7 +159,6 @@ public class FeatureModelInstallerPlugin implements InstallTaskFactory, Resource
             }
         }
         if (resource.getType().equals(InstallableResource.TYPE_FILE) && resource.getURL().endsWith(".zip")) {
-            baseDir = this.bundleContext.getDataFile("");
             try (final InputStream is = resource.getInputStream()) {
                 features.addAll(ArchiveReader.read(is, null));
             } catch (final IOException ioe) {
@@ -154,28 +166,36 @@ public class FeatureModelInstallerPlugin implements InstallTaskFactory, Resource
             }
         }
         if (!features.isEmpty()) {
-            boolean error = false;
-            final List<TransformationResult> result = new ArrayList<>();
-            for (final Feature feature : features) {
-                boolean select = this.classifierPatterns.isEmpty();
-                if (!select) {
-                    for (final String pattern : this.classifierPatterns) {
-                        if (":".equals(pattern)) {
-                            select = feature.getId().getClassifier() == null;
-                        } else if (feature.getId().getClassifier() != null) {
-                            select = Pattern.compile(pattern).matcher(feature.getId().getClassifier()).matches();
-                        }
-
-                        if (select) {
-                            break;
+            // persist all features to the file system
+            if (this.storageDirectory != null) {
+                for (Feature feature : features) {
+                    final File featureFile = new File(this.storageDirectory,
+                            feature.getId().toMvnPath().replace('/', File.separatorChar));
+                    if (!featureFile.exists()) {
+                        featureFile.getParentFile().mkdirs();
+                        try (final Writer writer = new FileWriter(featureFile)) {
+                            FeatureJSONWriter.write(writer, feature);
+                        } catch (final IOException ioe) {
+                            logger.error("Unable to write feature to " + featureFile + ":" + ioe.getMessage(), ioe);
                         }
                     }
                 }
+            }
 
-                if (!select) {
+            boolean error = false;
+            final List<TransformationResult> result = new ArrayList<>();
+            for (Feature feature : features) {
+                if (!classifierMatches(feature.getId().getClassifier())) {
                     continue;
                 }
 
+                // assemble feature now
+                if (!feature.isAssembled()) {
+                    final BuilderContext ctx = new BuilderContext(this.artifactManager.toFeatureProvider());
+                    ctx.setArtifactProvider(this.artifactManager);
+                    feature = FeatureBuilder.assemble(feature, ctx);
+                }
+
                 String featureJson = null;
                 try (final StringWriter sw = new StringWriter()) {
                     FeatureJSONWriter.write(sw, feature);
@@ -193,10 +213,7 @@ public class FeatureModelInstallerPlugin implements InstallTaskFactory, Resource
                     final Map<String, Object> attributes = new HashMap<>();
                     attributes.put(ATTR_MODEL, featureJson);
                     attributes.put(ATTR_ID, feature.getId().toMvnId());
-                    if (baseDir != null) {
-                        final File dir = new File(baseDir, feature.getId().toMvnName());
-                        attributes.put(ATTR_BASE_PATH, dir.getAbsolutePath());
-                    }
+
                     tr.setAttributes(attributes);
 
                     result.add(tr);
@@ -221,10 +238,50 @@ public class FeatureModelInstallerPlugin implements InstallTaskFactory, Resource
         if (rsrc.getState() == ResourceState.UNINSTALL ) {
             return new UninstallFeatureModelTask(group, bundleContext);
         }
+        final InstallContext ctx = new InstallContext(this.repository, this.repoInitProcessor, this.repoInitParser,
+                this.artifactManager, this.storageDirectory);
         return new InstallFeatureModelTask(group,
-                this.repository,
-                this.repoInitProcessor,
-                this.repoInitParser,
-                this.bundleContext, this.artifactManager);
+                ctx, this.bundleContext);
+    }
+
+    boolean classifierMatches(String classifier) {
+        boolean select = this.classifierPatterns.isEmpty();
+        if (!select) {
+            if (classifier == null) {
+                classifier = "";
+            }
+            for (final Pattern pattern : this.classifierPatterns) {
+                select = pattern.matcher(classifier).matches();
+
+                if (select) {
+                    break;
+                }
+            }
+        }
+        return select;
+    }
+
+    private static String toRegexPattern(String pattern) {
+        StringBuilder stringBuilder = new StringBuilder("^");
+        int index = 0;
+        while (index < pattern.length()) {
+            char currentChar = pattern.charAt(index++);
+            switch (currentChar) {
+            case '*':
+                stringBuilder.append("[^/]*");
+                break;
+            default:
+                if (isRegexMeta(currentChar)) {
+                    stringBuilder.append(Pattern.quote(Character.toString(currentChar)));
+                } else {
+                    stringBuilder.append(currentChar);
+                }
+            }
+        }
+        return stringBuilder.append('$').toString();
+    }
+
+    private static boolean isRegexMeta(char character) {
+        return "<([{\\^-=$!|]})?*+.>".indexOf(character) != -1;
     }
 }
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/InstallContext.java b/src/main/java/org/apache/sling/installer/factory/model/impl/InstallContext.java
new file mode 100644
index 0000000..0af42fa
--- /dev/null
+++ b/src/main/java/org/apache/sling/installer/factory/model/impl/InstallContext.java
@@ -0,0 +1,50 @@
+/*
+ * 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 java.io.File;
+
+import org.apache.sling.feature.io.artifacts.ArtifactManager;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.apache.sling.jcr.repoinit.JcrRepoInitOpsProcessor;
+import org.apache.sling.repoinit.parser.RepoInitParser;
+
+public class InstallContext {
+
+    public final SlingRepository repository;
+
+    public final JcrRepoInitOpsProcessor repoInitProcessor;
+
+    public final RepoInitParser repoInitParser;
+
+    public final ArtifactManager artifactManager;
+
+    public final File storageDirectory;
+
+    public InstallContext(final SlingRepository repository,
+            final JcrRepoInitOpsProcessor repoInitProcessor,
+            final RepoInitParser repoInitParser,
+            final ArtifactManager artifactManager, final File storageDirectory) {
+        this.repository = repository;
+        this.repoInitProcessor = repoInitProcessor;
+        this.repoInitParser = repoInitParser;
+        this.artifactManager = artifactManager;
+        this.storageDirectory = storageDirectory;
+    }
+}
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 3dde406..34572c4 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
@@ -43,7 +43,6 @@ import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.io.archive.ArchiveReader;
 import org.apache.sling.feature.io.artifacts.ArtifactHandler;
-import org.apache.sling.feature.io.artifacts.ArtifactManager;
 import org.apache.sling.feature.io.json.FeatureJSONReader;
 import org.apache.sling.installer.api.InstallableResource;
 import org.apache.sling.installer.api.OsgiInstaller;
@@ -51,9 +50,6 @@ import org.apache.sling.installer.api.tasks.InstallationContext;
 import org.apache.sling.installer.api.tasks.ResourceState;
 import org.apache.sling.installer.api.tasks.TaskResource;
 import org.apache.sling.installer.api.tasks.TaskResourceGroup;
-import org.apache.sling.jcr.api.SlingRepository;
-import org.apache.sling.jcr.repoinit.JcrRepoInitOpsProcessor;
-import org.apache.sling.repoinit.parser.RepoInitParser;
 import org.apache.sling.repoinit.parser.RepoInitParsingException;
 import org.apache.sling.repoinit.parser.operations.Operation;
 import org.osgi.framework.BundleContext;
@@ -63,24 +59,12 @@ import org.osgi.framework.BundleContext;
  */
 public class InstallFeatureModelTask extends AbstractFeatureModelTask {
 
-    private final SlingRepository repository;
-
-    private final JcrRepoInitOpsProcessor repoInitProcessor;
-
-    private final RepoInitParser repoInitParser;
-
-    private final ArtifactManager artifactManager;
+    private final InstallContext installContext;
 
     public InstallFeatureModelTask(final TaskResourceGroup group,
-            final SlingRepository repository,
-            final JcrRepoInitOpsProcessor repoInitProcessor,
-            final RepoInitParser repoInitParser,
-            final BundleContext bundleContext, final ArtifactManager artifactManager) {
+            final InstallContext installContext, final BundleContext bundleContext) {
         super(group, bundleContext);
-        this.repository = repository;
-        this.repoInitProcessor = repoInitProcessor;
-        this.repoInitParser = repoInitParser;
-        this.artifactManager = artifactManager;
+        this.installContext = installContext;
     }
 
     @SuppressWarnings("deprecation")
@@ -94,68 +78,59 @@ public class InstallFeatureModelTask extends AbstractFeatureModelTask {
                 ctx.log("Unable to install feature model resource {} : no model found", resource);
                 this.getResourceGroup().setFinishState(ResourceState.IGNORED);
             } else {
-                final String path = (String) resource.getAttribute(FeatureModelInstallerPlugin.ATTR_BASE_PATH);
-                final File baseDir = (path == null ? null : new File(path));
-
                 boolean success = false;
-                try {
-                    final Result result = this.transform(featureJson, resource, baseDir);
-                    if ( result == null ) {
-                        ctx.log("Unable to install feature model resource {} : unable to create resources", resource);
-                        this.getResourceGroup().setFinishState(ResourceState.IGNORED);
-                    } else {
-                        // repo init first
-                        if ( result.repoinit != null ) {
-                            List<Operation> ops = null;
-                            try ( final Reader r = new StringReader(result.repoinit) ) {
-                                ops = this.repoInitParser.parse(r);
-                            } catch (final IOException | RepoInitParsingException e) {
-                                logger.error("Unable to parse repoinit text.", e);
-                                ctx.log("Unable to install feature model resource {} : unable parse repoinit text.",
-                                        resource);
-                                this.getResourceGroup().setFinishState(ResourceState.IGNORED);
-                                return;
-                            }
+                final Result result = this.transform(featureJson, resource);
+                if (result == null) {
+                    ctx.log("Unable to install feature model resource {} : unable to create resources", resource);
+                    this.getResourceGroup().setFinishState(ResourceState.IGNORED);
+                } else {
+                    // repo init first
+                    if (result.repoinit != null) {
+                        List<Operation> ops = null;
+                        try (final Reader r = new StringReader(result.repoinit)) {
+                            ops = this.installContext.repoInitParser.parse(r);
+                        } catch (final IOException | RepoInitParsingException e) {
+                            logger.error("Unable to parse repoinit text.", e);
+                            ctx.log("Unable to install feature model resource {} : unable parse repoinit text.",
+                                    resource);
+                            this.getResourceGroup().setFinishState(ResourceState.IGNORED);
+                            return;
+                        }
 
-                            // login admin is required for repo init
-                            Session session = null;
-                            try {
-                                session = this.repository.loginAdministrative(null);
-                                this.repoInitProcessor.apply(session, ops);
-                                session.save();
-                            } catch ( final RepositoryException re) {
-                                logger.error("Unable to process repoinit text.", re);
-                                ctx.log("Unable to install feature model resource {} : unable to process repoinit text.",
-                                        resource);
-                                this.getResourceGroup().setFinishState(ResourceState.IGNORED);
-                                return;
+                        // login admin is required for repo init
+                        Session session = null;
+                        try {
+                            session = this.installContext.repository.loginAdministrative(null);
+                            this.installContext.repoInitProcessor.apply(session, ops);
+                            session.save();
+                        } catch (final RepositoryException re) {
+                            logger.error("Unable to process repoinit text.", re);
+                            ctx.log("Unable to install feature model resource {} : unable to process repoinit text.",
+                                    resource);
+                            this.getResourceGroup().setFinishState(ResourceState.IGNORED);
+                            return;
 
-                            } finally {
-                                if ( session != null ) {
-                                    session.logout();
-                                }
+                        } finally {
+                            if (session != null) {
+                                session.logout();
                             }
                         }
-                        if ( !result.resources.isEmpty() ) {
-                            final OsgiInstaller installer = this.getService(OsgiInstaller.class);
-                            if ( installer != null ) {
-                                installer.registerResources(
-                                        "model-" + resource.getAttribute(FeatureModelInstallerPlugin.ATTR_ID),
-                                        result.resources.toArray(new InstallableResource[result.resources.size()]));
-                            } else {
-                                ctx.log("Unable to install feature model resource {} : unable to get OSGi installer",
-                                        resource);
-                                this.getResourceGroup().setFinishState(ResourceState.IGNORED);
-                                return;
-                            }
-                        }
-                        this.getResourceGroup().setFinishState(ResourceState.INSTALLED);
-                        success = true;
                     }
-                } finally {
-                    if ( !success && baseDir != null ) {
-                        this.deleteDirectory(baseDir);
+                    if (!result.resources.isEmpty()) {
+                        final OsgiInstaller installer = this.getService(OsgiInstaller.class);
+                        if (installer != null) {
+                            installer.registerResources(
+                                    "model-" + resource.getAttribute(FeatureModelInstallerPlugin.ATTR_ID),
+                                    result.resources.toArray(new InstallableResource[result.resources.size()]));
+                        } else {
+                            ctx.log("Unable to install feature model resource {} : unable to get OSGi installer",
+                                    resource);
+                            this.getResourceGroup().setFinishState(ResourceState.IGNORED);
+                            return;
+                        }
                     }
+                    this.getResourceGroup().setFinishState(ResourceState.INSTALLED);
+                    success = true;
                 }
                 if ( success ) {
                     ctx.log("Installed {}", resource.getEntityId());
@@ -176,8 +151,7 @@ public class InstallFeatureModelTask extends AbstractFeatureModelTask {
     }
 
     private Result transform(final String featureJson,
-            final TaskResource rsrc,
-            final File baseDir) {
+            final TaskResource rsrc) {
         Feature feature = null;
         try (final Reader reader = new StringReader(featureJson)) {
             feature = FeatureJSONReader.read(reader, null);
@@ -188,7 +162,7 @@ public class InstallFeatureModelTask extends AbstractFeatureModelTask {
             return null;
         }
 
-        if ( baseDir != null ) {
+        if (this.installContext.storageDirectory != null) {
             // extract artifacts
             final byte[] buffer = new byte[1024*1024*256];
 
@@ -197,7 +171,7 @@ public class InstallFeatureModelTask extends AbstractFeatureModelTask {
 
                     @Override
                     public void consume(final ArtifactId id, final InputStream is) throws IOException {
-                        final File artifactFile = getArtifactFile(baseDir, id);
+                        final File artifactFile = getArtifactFile(installContext.storageDirectory, id);
                         if (!artifactFile.exists()) {
                             artifactFile.getParentFile().mkdirs();
                             try (final OutputStream os = new FileOutputStream(artifactFile)) {
@@ -218,14 +192,14 @@ public class InstallFeatureModelTask extends AbstractFeatureModelTask {
 
         final Result result = new Result();
         for (final Artifact bundle : feature.getBundles()) {
-            if (!addArtifact(baseDir, bundle, result)) {
+            if (!addArtifact(bundle, result)) {
                 return null;
             }
         }
         final Extension ext = feature.getExtensions().getByName(Extension.EXTENSION_NAME_CONTENT_PACKAGES);
         if (ext != null && ext.getType() == ExtensionType.ARTIFACTS) {
             for (final Artifact artifact : ext.getArtifacts()) {
-                addArtifact(baseDir, artifact, result);
+                addArtifact(artifact, result);
             }
         }
 
@@ -241,13 +215,14 @@ public class InstallFeatureModelTask extends AbstractFeatureModelTask {
         return result;
     }
 
-    private boolean addArtifact(final File baseDir, final Artifact artifact,
+    private boolean addArtifact(final Artifact artifact,
             final Result result) {
-        File artifactFile = (baseDir == null ? null : getArtifactFile(baseDir, artifact.getId()));
+        File artifactFile = (this.installContext.storageDirectory == null ? null
+                : getArtifactFile(this.installContext.storageDirectory, artifact.getId()));
         ArtifactHandler handler;
         if (artifactFile == null || !artifactFile.exists()) {
             try {
-                handler = this.artifactManager.getArtifactHandler(artifact.getId().toMvnUrl());
+                handler = this.installContext.artifactManager.getArtifactHandler(artifact.getId().toMvnUrl());
             } catch (final IOException ignore) {
                 return false;
             }
diff --git a/src/main/java/org/apache/sling/installer/factory/model/impl/UninstallFeatureModelTask.java b/src/main/java/org/apache/sling/installer/factory/model/impl/UninstallFeatureModelTask.java
index b584e6d..d734e4a 100644
--- a/src/main/java/org/apache/sling/installer/factory/model/impl/UninstallFeatureModelTask.java
+++ b/src/main/java/org/apache/sling/installer/factory/model/impl/UninstallFeatureModelTask.java
@@ -18,8 +18,6 @@
  */
 package org.apache.sling.installer.factory.model.impl;
 
-import java.io.File;
-
 import org.apache.sling.installer.api.OsgiInstaller;
 import org.apache.sling.installer.api.tasks.InstallationContext;
 import org.apache.sling.installer.api.tasks.ResourceState;
@@ -48,11 +46,6 @@ public class UninstallFeatureModelTask extends AbstractFeatureModelTask {
                 ctx.log("Uninstalling {}", resource.getEntityId());
                 installer.registerResources("model-" + resource.getAttribute(FeatureModelInstallerPlugin.ATTR_ID),
                         null);
-                final String path = (String) resource.getAttribute(FeatureModelInstallerPlugin.ATTR_BASE_PATH);
-                if ( path != null ) {
-                    final File dir = new File(path);
-                    deleteDirectory(dir);
-                }
                 this.getResourceGroup().setFinishState(ResourceState.UNINSTALLED);
                 ctx.log("Uninstalled {}", resource.getEntityId());
             }
diff --git a/src/test/java/org/apache/sling/installer/factory/model/impl/FeatureModelInstallerPluginTest.java b/src/test/java/org/apache/sling/installer/factory/model/impl/FeatureModelInstallerPluginTest.java
new file mode 100644
index 0000000..04c7eda
--- /dev/null
+++ b/src/test/java/org/apache/sling/installer/factory/model/impl/FeatureModelInstallerPluginTest.java
@@ -0,0 +1,70 @@
+/*
+ * 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 static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+import org.osgi.framework.BundleContext;
+
+public class FeatureModelInstallerPluginTest {
+
+    @Test
+    public void testNoPatterns() throws Exception {
+        final FeatureModelInstallerPlugin.Config config = Mockito.mock(FeatureModelInstallerPlugin.Config.class);
+        Mockito.when(config.classifierPatterns()).thenReturn(null);
+
+        final FeatureModelInstallerPlugin plugin = new FeatureModelInstallerPlugin(Mockito.mock(BundleContext.class),
+                config);
+        assertTrue(plugin.classifierMatches("foo"));
+        assertTrue(plugin.classifierMatches(null));
+    }
+
+    @Test
+    public void testNoClassifierPattern() throws Exception {
+        final FeatureModelInstallerPlugin.Config config = Mockito.mock(FeatureModelInstallerPlugin.Config.class);
+        Mockito.when(config.classifierPatterns()).thenReturn(new String[] { ":" });
+
+        final FeatureModelInstallerPlugin plugin = new FeatureModelInstallerPlugin(Mockito.mock(BundleContext.class),
+                config);
+        assertFalse(plugin.classifierMatches("foo"));
+        assertTrue(plugin.classifierMatches(null));
+    }
+
+    @Test
+    public void tesClassifierPatterns() throws Exception {
+        final FeatureModelInstallerPlugin.Config config = Mockito.mock(FeatureModelInstallerPlugin.Config.class);
+        Mockito.when(config.classifierPatterns()).thenReturn(new String[] { ":", "*devfar", "*prodfar", "*special*" });
+
+        final FeatureModelInstallerPlugin plugin = new FeatureModelInstallerPlugin(Mockito.mock(BundleContext.class),
+                config);
+
+        assertTrue(plugin.classifierMatches(null));
+        assertTrue(plugin.classifierMatches("mydevfar"));
+        assertTrue(plugin.classifierMatches("myprodfar"));
+        assertTrue(plugin.classifierMatches("superspecialfar"));
+        assertTrue(plugin.classifierMatches("evenmorespecial"));
+
+        assertFalse(plugin.classifierMatches("foo"));
+        assertFalse(plugin.classifierMatches("devmyfar"));
+        assertFalse(plugin.classifierMatches("prodmfar"));
+    }
+}