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 2018/04/30 13:00:20 UTC

[sling-slingstart-maven-plugin] branch master updated: SLING-7535 Make slingstart-maven-plugin understand sling feature model files

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-slingstart-maven-plugin.git


The following commit(s) were added to refs/heads/master by this push:
     new 55d84e8  SLING-7535 Make slingstart-maven-plugin understand sling feature model files
55d84e8 is described below

commit 55d84e85f29567b847fd96c5e10690350c65a2ec
Author: David Bosschaert <da...@gmail.com>
AuthorDate: Mon Apr 30 13:57:49 2018 +0100

    SLING-7535 Make slingstart-maven-plugin understand sling feature model files
    
    This commit converts the feature model files (from src/main/features)
    into provisioning model files and uses those.
---
 pom.xml                                            |  42 +++++++++
 .../slingstart/DependencyLifecycleParticipant.java |  15 +--
 .../maven/slingstart/FeatureModelConverter.java    |  87 ++++++++++++++++++
 .../maven/slingstart/GenerateResourcesMojo.java    | 101 ++++++++++++++++++++
 .../sling/maven/slingstart/ModelPreprocessor.java  |  53 +++++++----
 src/main/resources/META-INF/plexus/components.xml  |   2 +
 src/main/resources/features1/simple.json           |   9 ++
 .../features2/src/main/features/boot_gav.json      | 100 ++++++++++++++++++++
 .../slingstart/FeatureModelConverterTest.java      |  97 ++++++++++++++++++++
 .../slingstart/GenerateResourcesMojoTest.java      | 102 +++++++++++++++++++++
 src/test/resources/johnzon-feature.json            |   4 +
 src/test/resources/launchpad-feature.json          |   4 +
 12 files changed, 593 insertions(+), 23 deletions(-)

diff --git a/pom.xml b/pom.xml
index c47ead9..5a57a47 100644
--- a/pom.xml
+++ b/pom.xml
@@ -160,6 +160,26 @@
             <version>1.8.4</version>
         </dependency>
         <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.analyser</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.modelconverter</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.io</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature.resolver</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
+        <dependency>
             <groupId>org.apache.maven</groupId>
             <artifactId>maven-core</artifactId>
             <version>${maven.version}</version>
@@ -254,6 +274,12 @@
             so these dependencies ensures that they are in the .m2 directory prior to executing the tests.
             Whenever you modify these, you must also modify the references in org.apache.sling.maven.slingstart.PreparePackageMojoTest! -->
         <dependency>
+            <groupId>org.apache.felix</groupId>
+            <artifactId>org.apache.felix.configurator</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.sling</groupId>
             <artifactId>org.apache.sling.commons.classloader</artifactId>
             <version>1.3.2</version>
@@ -283,6 +309,22 @@
             <version>3.2.0</version>
             <scope>test</scope>
         </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-simple</artifactId>
+            <scope>test</scope>
+        </dependency>
     </dependencies>
 
     <reporting>
diff --git a/src/main/java/org/apache/sling/maven/slingstart/DependencyLifecycleParticipant.java b/src/main/java/org/apache/sling/maven/slingstart/DependencyLifecycleParticipant.java
index 3a11d9a..96ba69a 100644
--- a/src/main/java/org/apache/sling/maven/slingstart/DependencyLifecycleParticipant.java
+++ b/src/main/java/org/apache/sling/maven/slingstart/DependencyLifecycleParticipant.java
@@ -16,10 +16,6 @@
  */
 package org.apache.sling.maven.slingstart;
 
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.Properties;
-
 import org.apache.maven.AbstractMavenLifecycleParticipant;
 import org.apache.maven.MavenExecutionException;
 import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
@@ -33,6 +29,10 @@ import org.codehaus.plexus.component.annotations.Component;
 import org.codehaus.plexus.component.annotations.Requirement;
 import org.codehaus.plexus.logging.Logger;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Properties;
+
 /**
  * Maven lifecycle participant which adds the artifacts of the model to the dependencies.
  * This cannot happen as part of a regular Mojo (as there the dependencies have already been calculated)
@@ -46,7 +46,7 @@ public class DependencyLifecycleParticipant extends AbstractMavenLifecyclePartic
 
     private static final String GROUP_ID = "org.apache.sling";
     private static final String ARTIFACT_ID = "slingstart-maven-plugin";
-    
+
     /**
      *  the plugin ID consists of <code>groupId:artifactId</code>, see {@link Plugin#constructKey(String, String)}
      */
@@ -97,12 +97,13 @@ public class DependencyLifecycleParticipant extends AbstractMavenLifecyclePartic
             }
         }
 
+        new FeatureModelConverter().convert(session, env);
         new ModelPreprocessor().addDependencies(env);
     }
-    
+
     /**
      * Retrieves the version of the encapsulating Mojo by evaluating the {@code pom.properties} loaded via the extension classloader
-     * @throws IOException 
+     * @throws IOException
      * @see <a href="https://maven.apache.org/shared/maven-archiver/#pom-properties-content">Maven Archiver - pom.properties</a>
      */
     static final String getCurrentPluginVersion() throws IOException {
diff --git a/src/main/java/org/apache/sling/maven/slingstart/FeatureModelConverter.java b/src/main/java/org/apache/sling/maven/slingstart/FeatureModelConverter.java
new file mode 100644
index 0000000..e39ad45
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/slingstart/FeatureModelConverter.java
@@ -0,0 +1,87 @@
+/*
+ * 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.maven.slingstart;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.maven.MavenExecutionException;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.project.MavenProject;
+import org.apache.sling.feature.io.ArtifactManager;
+import org.apache.sling.feature.io.ArtifactManagerConfig;
+import org.apache.sling.feature.modelconverter.impl.FeatureToProvisioning;
+import org.apache.sling.maven.slingstart.ModelPreprocessor.Environment;
+import org.apache.sling.maven.slingstart.ModelPreprocessor.ProjectInfo;
+
+public class FeatureModelConverter {
+    static final String BUILD_DIR = "provisioning/converted";
+
+    public void convert(MavenSession session, Environment env) throws MavenExecutionException {
+        Map<String, ProjectInfo> projs = env.modelProjects;
+        for (ProjectInfo pi : projs.values()) {
+            convert(session, pi.project);
+        }
+    }
+
+    private void convert(MavenSession session, MavenProject project) throws MavenExecutionException {
+        File featuresDir = new File(project.getBasedir(), "src/main/features");
+
+        File[] files = featuresDir.listFiles();
+        List<File> featureFiles;
+        if (files != null) {
+            featureFiles = Arrays.asList(files);
+        } else {
+            featureFiles = Collections.emptyList();
+        }
+
+        if (featureFiles.size() == 0)
+            return;
+
+        File targetDir = new File(project.getBuild().getDirectory(), BUILD_DIR);
+        targetDir.mkdirs();
+
+        try {
+            ArtifactManager am = getArtifactManager(project, session);
+            for (File f : files) {
+                String fn = targetDir.getAbsolutePath() + "/" + f.getName() + ".txt";
+                FeatureToProvisioning.convert(f, fn, am);
+            }
+        } catch (Exception e) {
+            throw new MavenExecutionException("Cannot convert feature files to provisioning model", e);
+        }
+    }
+
+    private ArtifactManager getArtifactManager(MavenProject project, MavenSession session)
+            throws IOException {
+        List<String> repos = new ArrayList<>();
+        repos.add(session.getLocalRepository().getUrl());
+        for (ArtifactRepository ar : project.getRemoteArtifactRepositories()) {
+            repos.add(ar.getUrl());
+        }
+
+        final ArtifactManagerConfig amConfig = new ArtifactManagerConfig();
+        amConfig.setRepositoryUrls(repos.toArray(new String[] {}));
+        return ArtifactManager.getArtifactManager(amConfig);
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/slingstart/GenerateResourcesMojo.java b/src/main/java/org/apache/sling/maven/slingstart/GenerateResourcesMojo.java
new file mode 100644
index 0000000..b0e4c47
--- /dev/null
+++ b/src/main/java/org/apache/sling/maven/slingstart/GenerateResourcesMojo.java
@@ -0,0 +1,101 @@
+/*
+ * 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.maven.slingstart;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.maven.artifact.handler.manager.ArtifactHandlerManager;
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.artifact.resolver.ArtifactResolver;
+import org.apache.maven.plugin.MojoExecution;
+import org.apache.maven.plugin.MojoExecutionException;
+import org.apache.maven.plugin.MojoFailureException;
+import org.apache.maven.plugins.annotations.Component;
+import org.apache.maven.plugins.annotations.LifecyclePhase;
+import org.apache.maven.plugins.annotations.Mojo;
+import org.apache.maven.plugins.annotations.Parameter;
+import org.apache.maven.plugins.annotations.ResolutionScope;
+import org.apache.sling.feature.io.ArtifactManager;
+import org.apache.sling.feature.io.ArtifactManagerConfig;
+import org.apache.sling.feature.modelconverter.impl.FeatureToProvisioning;
+import org.codehaus.plexus.archiver.manager.ArchiverManager;
+
+@Mojo(
+        name = "generate-resources",
+        defaultPhase = LifecyclePhase.GENERATE_RESOURCES,
+        requiresDependencyResolution = ResolutionScope.TEST,
+        threadSafe = true)
+public class GenerateResourcesMojo extends AbstractSlingStartMojo {
+    @Parameter(defaultValue="${basedir}/src/main/features")
+    private File featuresDirectory;
+
+    /**
+     * To look up Archiver/UnArchiver implementations
+     */
+    @Component
+    private ArchiverManager archiverManager;
+
+    @Component
+    private ArtifactHandlerManager artifactHandlerManager;
+
+    /**
+     * Used to look up Artifacts in the remote repository.
+     *
+     */
+    @Component
+    private ArtifactResolver resolver;
+
+    @Parameter(defaultValue = "${mojoExecution}", readonly = true, required = true)
+    protected MojoExecution mojoExecution;
+
+    @Override
+    public void execute() throws MojoExecutionException, MojoFailureException {
+        File[] featureFiles = featuresDirectory.listFiles();
+        if (featureFiles == null)
+            return;
+
+        File targetDir = new File(project.getBuild().getDirectory(), FeatureModelConverter.BUILD_DIR);
+        targetDir.mkdirs();
+
+        try {
+            ArtifactManager am = getArtifactManager();
+            List<File> files = Arrays.asList(featureFiles);
+            for (File f : files) {
+                String fn = targetDir.getAbsolutePath() + "/" + f.getName() + ".txt";
+                FeatureToProvisioning.convert(f, fn, am);
+            }
+        } catch (Exception e) {
+            throw new MojoExecutionException("Cannot convert feature files to provisioning model", e);
+        }
+    }
+
+    private ArtifactManager getArtifactManager() throws IOException {
+        List<String> repos = new ArrayList<>();
+        repos.add(mavenSession.getLocalRepository().getUrl());
+        for (ArtifactRepository ar : project.getRemoteArtifactRepositories()) {
+            repos.add(ar.getUrl());
+        }
+
+        final ArtifactManagerConfig amConfig = new ArtifactManagerConfig();
+        amConfig.setRepositoryUrls(repos.toArray(new String[] {}));
+        return ArtifactManager.getArtifactManager(amConfig);
+    }
+}
diff --git a/src/main/java/org/apache/sling/maven/slingstart/ModelPreprocessor.java b/src/main/java/org/apache/sling/maven/slingstart/ModelPreprocessor.java
index 7e0be34..41b3475 100644
--- a/src/main/java/org/apache/sling/maven/slingstart/ModelPreprocessor.java
+++ b/src/main/java/org/apache/sling/maven/slingstart/ModelPreprocessor.java
@@ -16,20 +16,6 @@
  */
 package org.apache.sling.maven.slingstart;
 
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.io.Reader;
-import java.io.StringReader;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
 import org.apache.commons.io.IOUtils;
 import org.apache.maven.MavenExecutionException;
 import org.apache.maven.artifact.Artifact;
@@ -57,6 +43,21 @@ import org.apache.sling.provisioning.model.io.ModelReader;
 import org.codehaus.plexus.logging.Logger;
 import org.codehaus.plexus.util.xml.Xpp3Dom;
 
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.Reader;
+import java.io.StringReader;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
 public class ModelPreprocessor {
 
     public static final class ProjectInfo {
@@ -122,10 +123,30 @@ public class ModelPreprocessor {
             } else {
                 // use multiple fallbacks here only in case the default model directory is not explicitly set
                 File defaultModelDirectory = new File(info.project.getBasedir(), "src/main/provisioning");
-                if (defaultModelDirectory.exists()) {
+                File defaultConvertedModelDirectory = new File(info.project.getBuild().getDirectory() + "/" + FeatureModelConverter.BUILD_DIR);
+
+                if (defaultModelDirectory.exists() && defaultConvertedModelDirectory.exists()) {
+                    // The model is partially converted, partially explicitly defined. Copy the defined ones in with the converted ones
+                    for (File f : defaultModelDirectory.listFiles()) {
+                        File targetFile = new File(defaultConvertedModelDirectory, f.getName());
+                        if (targetFile.exists()) {
+                            env.logger.debug("File already exists. Skipping: " + targetFile);
+                        } else {
+                            env.logger.debug("Copying " + f + " to " + targetFile);
+                            Files.copy(f.toPath(), targetFile.toPath());
+                        }
+                    }
+                } else {
                     env.logger.debug("Try to extract model from default provisioning directory " + defaultModelDirectory.getAbsolutePath());
                     info.localModel = readLocalModel(info.project, inlinedModel, defaultModelDirectory, pattern, env.logger);
-                } else {
+                }
+
+                if (defaultConvertedModelDirectory.exists()) {
+                    env.logger.debug("Try to extract model from generated provisioning model directory " + defaultConvertedModelDirectory.getAbsolutePath());
+                    info.localModel = readLocalModel(info.project, inlinedModel, defaultConvertedModelDirectory, pattern, env.logger);
+                }
+
+                if (info.localModel == null) {
                     File defaultModelDirectoryInTest = new File(info.project.getBasedir(), "src/test/provisioning");
                     env.logger.debug("Try to extract model from default test provisioning directory " + defaultModelDirectoryInTest.getAbsolutePath());
                     info.localModel = readLocalModel(info.project, inlinedModel, defaultModelDirectoryInTest, pattern, env.logger);
diff --git a/src/main/resources/META-INF/plexus/components.xml b/src/main/resources/META-INF/plexus/components.xml
index 6d60143..c05d45e 100644
--- a/src/main/resources/META-INF/plexus/components.xml
+++ b/src/main/resources/META-INF/plexus/components.xml
@@ -27,6 +27,7 @@
           <lifecycle>
             <id>default</id>
             <phases>
+              <generate-resources>org.apache.sling:slingstart-maven-plugin:generate-resources</generate-resources>
               <package>org.apache.sling:slingstart-maven-plugin:attach-slingfeature</package>
               <install>org.apache.maven.plugins:maven-install-plugin:install</install>
               <deploy>org.apache.maven.plugins:maven-deploy-plugin:deploy</deploy>
@@ -44,6 +45,7 @@
           <lifecycle>
             <id>default</id>
             <phases>
+              <generate-resources>org.apache.sling:slingstart-maven-plugin:generate-resources</generate-resources>
               <process-resources>org.apache.maven.plugins:maven-resources-plugin:resources</process-resources>
               <compile>org.apache.maven.plugins:maven-compiler-plugin:compile</compile>
               <process-test-resources>
diff --git a/src/main/resources/features1/simple.json b/src/main/resources/features1/simple.json
new file mode 100644
index 0000000..4cc130a
--- /dev/null
+++ b/src/main/resources/features1/simple.json
@@ -0,0 +1,9 @@
+{
+  "id":"generated:simple:1.0.0",
+  "bundles":[
+    {
+      "id":"org.apache.aries:org.apache.aries.util:1.1.3",
+      "start-level":"20"
+    }
+  ]
+}
diff --git a/src/main/resources/features2/src/main/features/boot_gav.json b/src/main/resources/features2/src/main/features/boot_gav.json
new file mode 100644
index 0000000..1d997f8
--- /dev/null
+++ b/src/main/resources/features2/src/main/features/boot_gav.json
@@ -0,0 +1,100 @@
+{
+    "#": "The model version defaults to 1 if not specified",
+    "model-version": "1",
+    
+    "id": "testing123/boot/4.5.6",
+    "variables": {
+        "slf4j.version": "1.7.25",
+        
+        "#": "The model name when transformed to the provisioning model",
+        "provisioning.model.name": ":boot"
+    },
+    "bundles": [
+        {
+            "id": "org.slf4j/slf4j-api/${slf4j.version}",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.commons.log/5.1.0",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.commons.logservice/1.0.6",
+            "start-level": 1
+        },
+        {
+            "id": "org.slf4j/jcl-over-slf4j/${slf4j.version}",
+            "start-level": 1
+        },
+        {
+            "id": "org.slf4j/log4j-over-slf4j/${slf4j.version}",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.settings/1.3.8",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.fragment.xml/1.0.2",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.fragment.transaction/1.0.0",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.javax.activation/0.1.0",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.fragment.ws/1.0.2",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.launchpad.installer/1.2.2",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.installer.core/3.8.12",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.installer.provider.file/1.1.0",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.sling/org.apache.sling.installer.factory.configuration/1.1.2",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.felix/org.apache.felix.configadmin/1.8.16",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.felix/org.apache.felix.eventadmin/1.4.10",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.aries/org.apache.aries.util/1.1.3",
+            "start-level": 1
+        },
+        {
+            "id": "org.apache.geronimo.specs/geronimo-atinject_1.0_spec/1.0",
+            "start-level": 1
+        }],
+        
+    "framework-properties": {
+        "# oak_tar and oak_mongo run modes are mutually exclusive":
+        "# and cannot be changed after the first startup",
+    
+        "sling.run.mode.install.options": "oak_tar,oak_mongo",
+        "repository.home": "${sling.home}/repository",
+        "localIndexDir": "${sling.home}/repository/index",
+        
+        "# we need runmodes here too...": "",
+        "something.runmodes:a": "else",
+        "special.runmodes::b": "true", 
+        
+        "#": "${sling.home} needs to be provided at launch time"
+    }
+}
diff --git a/src/test/java/org/apache/sling/maven/slingstart/FeatureModelConverterTest.java b/src/test/java/org/apache/sling/maven/slingstart/FeatureModelConverterTest.java
new file mode 100644
index 0000000..45a8680
--- /dev/null
+++ b/src/test/java/org/apache/sling/maven/slingstart/FeatureModelConverterTest.java
@@ -0,0 +1,97 @@
+/*
+ * 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.maven.slingstart;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.URL;
+
+import com.google.common.io.Files;
+
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Build;
+import org.apache.maven.project.MavenProject;
+import org.apache.sling.maven.slingstart.ModelPreprocessor.Environment;
+import org.apache.sling.maven.slingstart.ModelPreprocessor.ProjectInfo;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class FeatureModelConverterTest {
+    private File tempDir;
+
+    @Before
+    public void setup() throws Exception {
+        tempDir = Files.createTempDir();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Delete the temp dir again
+        delTree(tempDir);
+        tempDir = null;
+    }
+
+    private void delTree(File f) throws IOException {
+        if (f.isDirectory()) {
+            for (File c : f.listFiles())
+                delTree(c);
+        }
+        if (!f.delete()) {
+            throw new FileNotFoundException("Cannot delete: " + f);
+        }
+    }
+
+    @Test
+    public void testConvert() throws Exception {
+        File f = new File(System.getProperty("user.home") + "/.m2");
+        ArtifactRepository localRepo = Mockito.mock(ArtifactRepository.class);
+        Mockito.when(localRepo.getUrl()).thenReturn(f.toURI().toURL().toString());
+
+        MavenSession session = Mockito.mock(MavenSession.class);
+        Mockito.when(session.getLocalRepository()).thenReturn(localRepo);
+
+        URL url = getClass().getResource("/features2/src/main/features/boot_gav.json");
+        File projBaseDir = new File(url.toURI())
+                .getParentFile().getParentFile().getParentFile().getParentFile();
+
+        Build build = Mockito.mock(Build.class);
+        Mockito.when(build.getDirectory()).thenReturn(tempDir.getAbsolutePath());
+
+        MavenProject proj = Mockito.mock(MavenProject.class);
+        Mockito.when(proj.getBasedir()).thenReturn(projBaseDir);
+        Mockito.when(proj.getBuild()).thenReturn(build);
+
+        ProjectInfo pi = new ProjectInfo();
+        pi.project = proj;
+
+        Environment env = new Environment();
+        env.modelProjects.put("xyz", pi);
+
+        FeatureModelConverter fmc = new FeatureModelConverter();
+        fmc.convert(session, env);
+
+        File expectedFile = new File(tempDir, "/provisioning/converted/boot_gav.json.txt");
+        assertTrue(expectedFile.exists());
+        assertTrue(expectedFile.length() > 0);
+    }
+}
diff --git a/src/test/java/org/apache/sling/maven/slingstart/GenerateResourcesMojoTest.java b/src/test/java/org/apache/sling/maven/slingstart/GenerateResourcesMojoTest.java
new file mode 100644
index 0000000..c2ca84f
--- /dev/null
+++ b/src/test/java/org/apache/sling/maven/slingstart/GenerateResourcesMojoTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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.maven.slingstart;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Field;
+import java.net.URL;
+
+import com.google.common.io.Files;
+
+import org.apache.maven.artifact.repository.ArtifactRepository;
+import org.apache.maven.execution.MavenSession;
+import org.apache.maven.model.Build;
+import org.apache.maven.project.MavenProject;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+public class GenerateResourcesMojoTest {
+    private File tempDir;
+
+    @Before
+    public void setup() throws Exception {
+        tempDir = Files.createTempDir();
+    }
+
+    @After
+    public void tearDown() throws Exception {
+        // Delete the temp dir again
+        delTree(tempDir);
+        tempDir = null;
+    }
+
+    private void delTree(File f) throws IOException {
+        if (f.isDirectory()) {
+            for (File c : f.listFiles())
+                delTree(c);
+        }
+        if (!f.delete()) {
+            throw new FileNotFoundException("Cannot delete: " + f);
+        }
+    }
+
+    @Test
+    public void testExecute() throws Exception {
+        URL url = getClass().getResource("/features1/simple.json");
+        File featureDir = new File(url.toURI()).getParentFile();
+
+        Build build = Mockito.mock(Build.class);
+        Mockito.when(build.getDirectory()).thenReturn(tempDir.getAbsolutePath());
+
+        MavenProject proj = Mockito.mock(MavenProject.class);
+        Mockito.when(proj.getBuild()).thenReturn(build);
+
+        File f = new File(System.getProperty("user.home") + "/.m2");
+        ArtifactRepository localRepo = Mockito.mock(ArtifactRepository.class);
+        Mockito.when(localRepo.getUrl()).thenReturn(f.toURI().toURL().toString());
+
+        MavenSession session = Mockito.mock(MavenSession.class);
+        Mockito.when(session.getLocalRepository()).thenReturn(localRepo);
+
+        GenerateResourcesMojo grm = new GenerateResourcesMojo();
+        setPrivateField(grm, "featuresDirectory", featureDir);
+        setPrivateField(AbstractSlingStartMojo.class, grm, "project", proj);
+        setPrivateField(AbstractSlingStartMojo.class, grm, "mavenSession", session);
+
+        grm.execute();
+
+        File expectedFile = new File(tempDir, FeatureModelConverter.BUILD_DIR + "/simple.json.txt");
+        assertTrue(expectedFile.exists());
+        assertTrue(expectedFile.length() > 0);
+    }
+
+    private void setPrivateField(Object obj, String name, Object val) throws Exception {
+        setPrivateField(obj.getClass(), obj, name, val);
+    }
+
+    private void setPrivateField(Class<?> cls, Object obj, String name, Object val) throws Exception {
+        Field field = cls.getDeclaredField(name);
+        field.setAccessible(true);
+        field.set(obj, val);
+    }
+}
diff --git a/src/test/resources/johnzon-feature.json b/src/test/resources/johnzon-feature.json
new file mode 100644
index 0000000..f70f8f4
--- /dev/null
+++ b/src/test/resources/johnzon-feature.json
@@ -0,0 +1,4 @@
+{
+    "id": "org.apache.sling.test.features/johnzon-feature/1.0.0",
+    "bundles": ["org.apache.sling/org.apache.sling.commons.johnzon/1.0.0"] 
+}
\ No newline at end of file
diff --git a/src/test/resources/launchpad-feature.json b/src/test/resources/launchpad-feature.json
new file mode 100644
index 0000000..26a43c7
--- /dev/null
+++ b/src/test/resources/launchpad-feature.json
@@ -0,0 +1,4 @@
+{
+    "id": "org.apache.sling.test.features/launchpad-feature/1.0.0",
+    "bundles": ["org.apache.sling/org.apache.sling.commons.classloader/1.3.2"] 
+}
\ No newline at end of file

-- 
To stop receiving notification emails like this one, please contact
davidb@apache.org.