SLING-6068 allow to build and start a quickstart even for non "slingstart" packaging projects.

This closes #176

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1763607 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/maven/slingstart/AbstractSlingStartMojo.java b/src/main/java/org/apache/sling/maven/slingstart/AbstractSlingStartMojo.java
index f880a26..056b969 100644
--- a/src/main/java/org/apache/sling/maven/slingstart/AbstractSlingStartMojo.java
+++ b/src/main/java/org/apache/sling/maven/slingstart/AbstractSlingStartMojo.java
@@ -32,22 +32,29 @@
 public abstract class AbstractSlingStartMojo extends AbstractMojo {
 
     /**
-     * The model directory
-     * This parameter is evaluated in the DependencyLifecycleParticipant
+     * The model directory containing the provision models.
+     * This parameter is evaluated in the {@link DependencyLifecycleParticipant}.
+     * As default first <code>${basedir}/src/main/provisioning</code> and then
+     * <code>${basedir}/src/test/provisioning</code> is used 
+     * (in case the former does not exist).
      */
     @Parameter(defaultValue="${basedir}/src/main/provisioning")
     private File modelDirectory;
 
     /**
-     * The model file name pattern
-     * This parameter is evaluated in the DependencyLifecycleParticipant
+     * The model file name pattern to consider.
+     * This parameter is evaluated in the {@link DependencyLifecycleParticipant}.
      */
-    @Parameter
+    @Parameter(defaultValue=DEFAULT_MODEL_PATTERN)
     private String modelPattern;
+    
+    public static final String DEFAULT_MODEL_PATTERN = "((.*)\\.txt|(.*)\\.model)";
 
     /**
-     * Inlined model, supported since version 1.3.
-     * This parameter is evaluated in the DependencyLifecycleParticipant
+     * Inlined model. Is processed first and afterwards merged with any model found in {@link #modelDirectory}.
+     * This parameter is evaluated in the {@link DependencyLifecycleParticipant}.
+     * @since 1.3
+     * @see <a href="https://issues.apache.org/jira/browse/SLING-4912">SLING-4912</a>
      */
     @Parameter
     private String model;
@@ -61,6 +68,9 @@
     @Parameter(property = "session", readonly = true, required = true)
     protected MavenSession mavenSession;
 
+    /**
+     * If set to {@code true} creates a WAR artifact in addition to the standalone JAR from the model.
+     */
     @Parameter(defaultValue="false")
     protected boolean createWebapp;
 
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 f6993b3..7335f9f 100644
--- a/src/main/java/org/apache/sling/maven/slingstart/DependencyLifecycleParticipant.java
+++ b/src/main/java/org/apache/sling/maven/slingstart/DependencyLifecycleParticipant.java
@@ -35,7 +35,10 @@
 @Component(role = AbstractMavenLifecycleParticipant.class)
 public class DependencyLifecycleParticipant extends AbstractMavenLifecycleParticipant {
 
-    private static final String PLUGIN_ID = "slingstart-maven-plugin";
+    /**
+     *  the plugin ID consists of <code>groupId:artifactId</code>, see {@link Plugin#constructKey(String, String)}
+     */
+    private static final String PLUGIN_ID = "org.apache.sling:slingstart-maven-plugin";
 
     @Requirement
     private Logger logger;
@@ -58,20 +61,15 @@
         env.logger = logger;
         env.session = session;
 
-        logger.debug("Searching for " + BuildConstants.PACKAGING_SLINGSTART + "/" + BuildConstants.PACKAGING_PARTIAL_SYSTEM + " projects...");
+        logger.debug("Searching for project leveraging plugin '" + PLUGIN_ID + "'...");
 
         for (final MavenProject project : session.getProjects()) {
-            if ( project.getPackaging().equals(BuildConstants.PACKAGING_SLINGSTART)
-                 || project.getPackaging().equals(BuildConstants.PACKAGING_PARTIAL_SYSTEM)) {
-                logger.debug("Found " + project.getPackaging() + " project: " + project);
-                // search plugin configuration (optional)
+            // consider all projects where this plugin is configured
+            Plugin plugin = project.getPlugin(PLUGIN_ID);
+            if (plugin != null) {
+                logger.debug("Found project " + project + " leveraging " + PLUGIN_ID +".");
                 final ProjectInfo info = new ProjectInfo();
-                for (Plugin plugin : project.getBuild().getPlugins()) {
-                    if (plugin.getArtifactId().equals(PLUGIN_ID)) {
-                        info.plugin = plugin;
-                        break;
-                    }
-                }
+                info.plugin = plugin;
                 info.project = project;
                 env.modelProjects.put(project.getGroupId() + ":" + project.getArtifactId(), info);
             }
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 cc2853c..ea74433 100644
--- a/src/main/java/org/apache/sling/maven/slingstart/ModelPreprocessor.java
+++ b/src/main/java/org/apache/sling/maven/slingstart/ModelPreprocessor.java
@@ -91,16 +91,31 @@
         env.logger.debug("Processing project " + info.project);
 
         // read local model
-        final String directory = nodeValue(info.plugin,
-                "modelDirectory",
-                new File(info.project.getBasedir(), "src/main/provisioning").getAbsolutePath());
         final String pattern = nodeValue(info.plugin,
-                "modelPattern", "((.*)\\.txt|(.*)\\.model)");
-
+                "modelPattern", AbstractSlingStartMojo.DEFAULT_MODEL_PATTERN);
+        
         final String inlinedModel = nodeValue(info.plugin,
                 "model", null);
+        
+        String scope = Artifact.SCOPE_PROVIDED;
         try {
-            info.localModel = readLocalModel(info.project, inlinedModel, new File(directory), pattern, env.logger);
+            if (hasNodeValue(info.plugin, "modelDirectory")) {
+                final String directory = nodeValue(info.plugin,
+                        "modelDirectory", null);
+                info.localModel = readLocalModel(info.project, inlinedModel, new File(directory), pattern, env.logger);
+            } 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()) {
+                    env.logger.debug("Try to extract model from default provisioning directory " + defaultModelDirectory.getAbsolutePath());
+                    info.localModel = readLocalModel(info.project, inlinedModel, defaultModelDirectory, pattern, env.logger);
+                } else {
+                    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);
+                    scope = Artifact.SCOPE_TEST;
+                }
+            }
         } catch ( final IOException ioe) {
             throw new MavenExecutionException(ioe.getMessage(), ioe);
         }
@@ -132,7 +147,7 @@
             throw new MavenExecutionException("Unable to create model file for " + info.project + " : " + errors, (File)null);
         }
 
-        addDependenciesFromModel(env, info);
+        addDependenciesFromModel(env, info, scope);
 
         try {
            ProjectHelper.storeProjectInfo(info);
@@ -144,14 +159,15 @@
 
     /**
      * Add all dependencies from the model
-     * @param project The project
-     * @param model The model
-     * @param log The logger
+     * @param env The environment
+     * @param info The project info
+     * @param scope The scope which the new dependencies should have
      * @throws MavenExecutionException
      */
     private void addDependenciesFromModel(
             final Environment env,
-            final ProjectInfo info)
+            final ProjectInfo info,
+            final String scope)
     throws MavenExecutionException {
         if ( info.project.getPackaging().equals(BuildConstants.PACKAGING_SLINGSTART ) ) {
             // add base artifact if defined in current model
@@ -168,7 +184,7 @@
                 if ( BuildConstants.CLASSIFIER_WEBAPP.equals(c) ) {
                     dep.setType(BuildConstants.TYPE_WAR);
                 }
-                dep.setScope(Artifact.SCOPE_PROVIDED);
+                dep.setScope(scope);
 
                 info.project.getDependencies().add(dep);
                 env.logger.debug("- adding base dependency " + ModelUtils.toString(dep));
@@ -197,7 +213,7 @@
                         dep.setType(a.getType());
                         dep.setClassifier(a.getClassifier());
 
-                        dep.setScope(Artifact.SCOPE_PROVIDED);
+                        dep.setScope(scope);
 
                         env.logger.debug("- adding dependency " + ModelUtils.toString(dep));
                         info.project.getDependencies().add(dep);
@@ -348,6 +364,18 @@
             return defaultValue;
         }
     }
+    
+    /**
+     * Checks if plugin configuration value is set in POM for a specific configuration parameter.
+     * @param plugin Plugin
+     * @param name Configuration parameter.
+     * @return {@code true} in case the configuration has been set in the pom, otherwise {@code false}
+     */
+    private boolean hasNodeValue(final Plugin plugin, final String name) {
+        final Xpp3Dom config = plugin == null ? null : (Xpp3Dom)plugin.getConfiguration();
+        final Xpp3Dom node = (config == null ? null : config.getChild(name));
+        return (node != null);
+    }
 
     /**
      * Gets plugins configuration from POM (boolean parameter).
@@ -387,7 +415,9 @@
      * Only files ending with .txt or .model are read.
      *
      * @param project The current maven project
+     * @param inlinedModel the inlined model to be merged with the models in modelDirectory (may be null)
      * @param modelDirectory The directory to scan for models
+     * @param pattern Pattern used to find the textual models within the modelDirectory
      * @param logger The logger
      */
     protected Model readLocalModel(
diff --git a/src/main/java/org/apache/sling/maven/slingstart/PackageMojo.java b/src/main/java/org/apache/sling/maven/slingstart/PackageMojo.java
index 8e19ecd..de1bb05 100644
--- a/src/main/java/org/apache/sling/maven/slingstart/PackageMojo.java
+++ b/src/main/java/org/apache/sling/maven/slingstart/PackageMojo.java
@@ -75,7 +75,8 @@
             fis = new FileInputStream(manifestFile);
             final Manifest mf = new Manifest(fis);
 
-            final File outputFile = new File(buildDirectory, this.project.getArtifactId() + "-" + this.project.getVersion() + ".jar");
+            // make sure this filename does not conflict with any other project artifacts (primary or secondary)
+            final File outputFile = new File(buildDirectory, this.project.getArtifactId() + "-" + this.project.getVersion() + ".standalonelaunchpad.jar");
 
             final JarArchiverHelper helper = new JarArchiverHelper(jarArchiver, this.project, outputFile, mf);
             helper.addDirectory(buildOutputDirectory, null, EXCLUDES_MANIFEST);
@@ -105,7 +106,8 @@
             final Map<String, File> contentsMap = (Map<String, File>) this.project.getContextValue(BuildConstants.CONTEXT_WEBAPP);
 
             final File buildOutputDirectory = new File(buildDirectory, BuildConstants.WEBAPP_OUTDIR);
-            final File outputFile = new File(buildDirectory, this.project.getArtifactId() + "-" + this.project.getVersion() + ".war");
+            // make sure this filename does not conflict with any other project artifacts (primary or secondary)
+            final File outputFile = new File(buildDirectory, this.project.getArtifactId() + "-" + this.project.getVersion() + ".webapplaunchpad.war");
 
             final JarArchiverHelper helper = new JarArchiverHelper(this.jarArchiver, this.project, outputFile);
             helper.addDirectory(buildOutputDirectory, null, EXCLUDES_MANIFEST);
diff --git a/src/main/java/org/apache/sling/maven/slingstart/run/StartMojo.java b/src/main/java/org/apache/sling/maven/slingstart/run/StartMojo.java
index 928204f..ded1135 100644
--- a/src/main/java/org/apache/sling/maven/slingstart/run/StartMojo.java
+++ b/src/main/java/org/apache/sling/maven/slingstart/run/StartMojo.java
@@ -390,11 +390,13 @@
 
         // If a launchpad JAR is specified, use it
         if (launchpadJar != null) {
+            getLog().info("Using launchpad jar from '" +  launchpadJar + "' given as configuration parameter!");
             return launchpadJar;
         }
 
         // If a launchpad dependency is configured, resolve it
         if (launchpadDependency != null) {
+            getLog().info("Using launchpad dependency '" +  launchpadDependency + "' given as configuration parameter!");
             return getArtifact(launchpadDependency).getFile();
         }
 
@@ -402,10 +404,20 @@
         if (this.project.getPackaging().equals(BuildConstants.PACKAGING_SLINGSTART)) {
             final File jarFile = new File(this.project.getBuild().getDirectory(), this.project.getBuild().getFinalName() + ".jar");
             if (jarFile.exists()) {
+                getLog().info("Using launchpad jar being generated as this project's primary artifact: '" +  jarFile + "'!");
                 return jarFile;
             }
         }
 
+        // In case there was a provisioning model found but this is not a slingstart project, the JAR might be attached already through goal "package"
+        for (Artifact attachedArtifact : project.getAttachedArtifacts()) {
+            // find the attached artifact with classifier "standalone"
+            if (BuildConstants.TYPE_JAR.equals(attachedArtifact.getType()) && BuildConstants.CLASSIFIER_APP.equals(attachedArtifact.getClassifier())) {
+                getLog().info("Using launchpad jar being attached as additional project artifact: '" +  attachedArtifact.getFile() + "'!");
+                return attachedArtifact.getFile();
+            }
+        }
+
         // Last chance: use the first declared dependency with type "slingstart"
         final Set<Artifact> dependencies = this.project.getDependencyArtifacts();
         for (final Artifact dep : dependencies) {
@@ -416,6 +428,7 @@
                 d.setVersion(dep.getVersion());
                 d.setScope(Artifact.SCOPE_RUNTIME);
                 d.setType(BuildConstants.TYPE_JAR);
+                getLog().info("Using launchpad jar from first dependency of type 'slingstart': '"+ d +"'!");
                 return getArtifact(d).getFile();
             }
         }