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

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


The following commit(s) were added to refs/heads/master by this push:
     new e253419  Allow to specify several features to be launched.
e253419 is described below

commit e25341911ea0889c5be72cd99bce4e96555adf50
Author: Karl Pauls <[email protected]>
AuthorDate: Tue Aug 28 22:47:02 2018 +0200

    Allow to specify several features to be launched.
---
 pom.xml                                            |   2 +
 .../feature/launcher/impl/FeatureProcessor.java    |  91 ++++++++-----
 .../feature/launcher/impl/LauncherConfig.java      |  26 +---
 .../apache/sling/feature/launcher/impl/Main.java   | 147 +++++++++++++--------
 4 files changed, 155 insertions(+), 111 deletions(-)

diff --git a/pom.xml b/pom.xml
index 2062ac6..2fa5dd4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -77,6 +77,8 @@
                 <configuration>
                     <excludes>
                         <exclude>readme.md</exclude>
+                        
<exclude>src/main/resources/META-INF/services/**</exclude>
+                        <exclude>**/*.properties</exclude>
                     </excludes>
                 </configuration>
             </plugin>
diff --git 
a/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java 
b/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
index 5c7a4ff..8fe6874 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
@@ -18,29 +18,36 @@ package org.apache.sling.feature.launcher.impl;
 
 import java.io.File;
 import java.io.FileReader;
-import java.io.FileWriter;
 import java.io.IOException;
 import java.io.StringReader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-
+import java.util.ServiceLoader;
+import java.util.Spliterator;
+import java.util.Spliterators;
+import java.util.stream.StreamSupport;
 import javax.json.Json;
 import javax.json.JsonArray;
 import javax.json.JsonReader;
 
 import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.Configuration;
 import org.apache.sling.feature.Extension;
 import org.apache.sling.feature.ExtensionType;
 import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.FeatureConstants;
-import org.apache.sling.feature.KeyValueMap;
+import org.apache.sling.feature.builder.BuilderContext;
 import org.apache.sling.feature.builder.FeatureBuilder;
+import org.apache.sling.feature.builder.FeatureExtensionHandler;
 import org.apache.sling.feature.io.ArtifactHandler;
 import org.apache.sling.feature.io.ArtifactManager;
+import org.apache.sling.feature.io.IOUtils;
 import org.apache.sling.feature.io.json.FeatureJSONReader;
-import org.apache.sling.feature.io.json.FeatureJSONWriter;
 import org.apache.sling.feature.launcher.impl.LauncherConfig.StartupMode;
 
 public class FeatureProcessor {
@@ -52,27 +59,50 @@ public class FeatureProcessor {
      * @param artifactManager The artifact manager
      */
     public static Feature createApplication(final LauncherConfig config,
-            final ArtifactManager artifactManager)
-    throws IOException {
-        Feature app = null;
-        if ( config.getApplicationFile() != null ) {
-            app = read(config.getApplicationFile(), artifactManager, 
config.getVariables());
-            // write application back
-            final File file = new File(config.getHomeDirectory(), "resources" 
+ File.separatorChar + "provisioning" + File.separatorChar + 
"application.json");
-            file.getParentFile().mkdirs();
-
-            try (final FileWriter writer = new FileWriter(file)) {
-                FeatureJSONWriter.write(writer, app);
-            } catch ( final IOException ioe) {
-                Main.LOG().error("Error while writing application file: {}", 
ioe.getMessage(), ioe);
-                System.exit(1);
+            final ArtifactManager artifactManager) throws IOException
+    {
+
+        final BuilderContext builderContext = new BuilderContext(
+            id -> {
+                try {
+                    final ArtifactHandler handler = 
artifactManager.getArtifactHandler(id.toMvnUrl());
+                    try (final FileReader r = new 
FileReader(handler.getFile())) {
+                        final Feature f = FeatureJSONReader.read(r, 
handler.getUrl());
+                        return f;
+                    }
+
+                } catch (final IOException e) {
+                    // ignore
+                }
+                return null;
+            }).add(StreamSupport.stream(Spliterators.spliteratorUnknownSize(
+            ServiceLoader.load(FeatureExtensionHandler.class).iterator(), 
Spliterator.ORDERED), false).toArray(FeatureExtensionHandler[]::new));
+
+        List<Feature> features = new ArrayList<>();
+
+        for (final String initFile : config.getFeatureFiles())
+        {
+            try
+            {
+                final Feature f = IOUtils.getFeature(initFile, 
artifactManager);
+                features.add(f);
+            }
+            catch (Exception ex)
+            {
+                throw new IOException("Error reading feature: " + initFile, 
ex);
             }
         }
-        else {
-            app = read(new File(config.getHomeDirectory(), "resources" + 
File.separatorChar + "provisioning" + File.separatorChar + 
"application.json").getPath(),
-                    artifactManager, config.getVariables());
-        }
 
+        Collections.sort(features);
+
+        // TODO make feature id configurable
+        final Feature app = 
FeatureBuilder.assemble(ArtifactId.fromMvnId("group:assembled:1.0.0"), 
builderContext, features.toArray(new Feature[0]));
+
+        final Artifact a = new 
Artifact(ArtifactId.parse("org.apache.sling/org.apache.sling.launchpad.api/1.2.0"));
+        a.getMetadata().put(org.apache.sling.feature.Artifact.KEY_START_ORDER, 
"1");
+        app.getBundles().add(a);
+
+        // TODO: this sucks
         for (Artifact bundle : app.getBundles()) {
             if ( bundle.getStartOrder() == 0) {
                 final int so = bundle.getMetadata().get("start-level") != null 
? Integer.parseInt(bundle.getMetadata().get("start-level")) : 1;
@@ -80,22 +110,11 @@ public class FeatureProcessor {
             }
         }
 
+        FeatureBuilder.resolveVariables(app, config.getVariables());
+
         return app;
     }
 
-    private static Feature read(String absoluteArg, ArtifactManager 
artifactManager,
-            KeyValueMap overriddenVars) throws IOException {
-        if ( absoluteArg.indexOf(":") < 2 ) {
-            absoluteArg = new File(absoluteArg).getAbsolutePath();
-        }
-        final ArtifactHandler appArtifact = 
artifactManager.getArtifactHandler(absoluteArg);
-
-        try (final FileReader r = new FileReader(appArtifact.getFile())) {
-            final Feature f = FeatureJSONReader.read(r, appArtifact.getUrl());
-            FeatureBuilder.resolveVariables(f, overriddenVars);
-            return f;
-        }
-    }
     /**
      * Prepare the launcher
      * - add all bundles to the bundle map of the installation object
@@ -115,7 +134,7 @@ public class FeatureProcessor {
         }
         int index = 1;
         for(final Extension ext : app.getExtensions()) {
-            if ( ext.getType() == ExtensionType.ARTIFACTS ) {
+            if ( ext.getType() == ExtensionType.ARTIFACTS && 
!ext.getName().equals(FeatureConstants.EXTENSION_NAME_ASSEMBLED_FEATURES)) {
                 for(final Artifact a : ext.getArtifacts() ) {
                     if ( config.getStartupMode() == StartupMode.PURE ) {
                         throw new Exception("Artifacts other than bundle are 
not supported by framework launcher.");
diff --git 
a/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java 
b/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java
index 191ecb6..a6272a2 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java
@@ -18,6 +18,8 @@ package org.apache.sling.feature.launcher.impl;
 
 import java.io.File;
 import java.io.IOException;
+import java.util.Arrays;
+import java.util.LinkedHashSet;
 
 import org.apache.sling.feature.KeyValueMap;
 import org.apache.sling.feature.io.ArtifactManagerConfig;
@@ -40,10 +42,7 @@ public class LauncherConfig
     private static final String CACHE_DIR = "cache";
 
     /** The feature files or directories. */
-    private volatile String[] featureFiles;
-
-    /** The application file. */
-    private volatile String appFile;
+    private final LinkedHashSet<String> featureFiles = new LinkedHashSet<>();
 
     private volatile StartupMode startupMode = StartupMode.PURE;
 
@@ -61,23 +60,12 @@ public class LauncherConfig
         this.setCacheDirectory(new File(getHomeDirectory(), CACHE_DIR));
     }
 
-    public void setApplicationFile(final String value) {
-        appFile = value;
-    }
-
-    public String getApplicationFile() {
-        return this.appFile;
-    }
-
     /**
      * Set the list of feature files or directories.
-     * @param value The array with the feature file names.
+     * @param featureFiles The array with the feature file names.
      */
-    public void setFeatureFiles(final String[] value) {
-        this.featureFiles = value;
-        if ( value != null && value.length == 0 ) {
-            this.featureFiles = null;
-        }
+    public void addFeatureFiles(final String... featureFiles) {
+        this.featureFiles.addAll(Arrays.asList(featureFiles));
     }
 
     /**
@@ -86,7 +74,7 @@ public class LauncherConfig
      * @throws IOException
      */
     public String[] getFeatureFiles() {
-        return this.featureFiles;
+        return this.featureFiles.toArray(new String[0]);
     }
 
 
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/Main.java 
b/src/main/java/org/apache/sling/feature/launcher/impl/Main.java
index eb657e0..5a2f335 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/Main.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/Main.java
@@ -17,7 +17,9 @@
 package org.apache.sling.feature.launcher.impl;
 
 import java.io.File;
+import java.io.FileWriter;
 import java.io.IOException;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.net.URLClassLoader;
 import java.nio.file.Files;
@@ -39,6 +41,7 @@ import org.apache.sling.feature.Feature;
 import org.apache.sling.feature.io.ArtifactHandler;
 import org.apache.sling.feature.io.ArtifactManager;
 import org.apache.sling.feature.io.IOUtils;
+import org.apache.sling.feature.io.json.FeatureJSONWriter;
 import org.apache.sling.feature.launcher.impl.launchers.FrameworkLauncher;
 import org.apache.sling.feature.launcher.spi.Launcher;
 import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
@@ -83,7 +86,7 @@ public class Main {
         final Options options = new Options();
 
         final Option repoOption =  new Option("u", true, "Set repository url");
-        final Option appOption =  new Option("a", true, "Set application 
file");
+        final Option featureOption =  new Option("f", true, "Set feature 
files");
         final Option fwkProperties = new Option("D", true, "Set framework 
properties");
         final Option varValue = new Option("V", true, "Set variable value");
         final Option debugOption = new Option("v", false, "Verbose");
@@ -97,7 +100,7 @@ public class Main {
         final Option frameworkOption = new Option("fv", true, "Set felix 
framework version");
 
         options.addOption(repoOption);
-        options.addOption(appOption);
+        options.addOption(featureOption);
         options.addOption(fwkProperties);
         options.addOption(varValue);
         options.addOption(debugOption);
@@ -135,8 +138,10 @@ public class Main {
             if ( cl.hasOption(installerOption.getOpt()) ) {
                 config.setUseInstaller();
             }
-            if ( cl.hasOption(appOption.getOpt()) ) {
-                
config.setApplicationFile(cl.getOptionValue(appOption.getOpt()));
+            if ( cl.hasOption(featureOption.getOpt()) ) {
+                for(final String optVal : 
cl.getOptionValues(featureOption.getOpt())) {
+                    config.addFeatureFiles(optVal.split(","));
+                }
             }
             if (cl.hasOption(cacheOption.getOpt())) {
                 config.setCacheDirectory(new 
File(cl.getOptionValue(cacheOption.getOpt())));
@@ -185,53 +190,44 @@ public class Main {
         Main.LOG().info("Apache Sling Application Launcher");
         Main.LOG().info("---------------------------------");
 
-        ArtifactManager artifactManager = null;
-        try {
 
-            Main.LOG().info("Initializing...");
-            try {
-                artifactManager = 
ArtifactManager.getArtifactManager(launcherConfig);
-            } catch ( final IOException ioe) {
-                Main.LOG().error("Unable to setup artifact manager: {}", 
ioe.getMessage(), ioe);
-                System.exit(1);
-            }
+        Main.LOG().info("Initializing...");
+
+        final Launcher launcher = new FrameworkLauncher();
+
+        try (ArtifactManager artifactManager = 
ArtifactManager.getArtifactManager(launcherConfig)) {
+
             Main.LOG().info("Artifact Repositories: {}", 
Arrays.toString(launcherConfig.getRepositoryUrls()));
             Main.LOG().info("Assembling provisioning model...");
 
             try {
-                final Launcher launcher = new FrameworkLauncher();
-                final Feature app = 
FeatureProcessor.createApplication(launcherConfig, artifactManager);
+                boolean restart = launcherConfig.getFeatureFiles().length == 0;
+
+                final Feature app = assemble(launcherConfig, artifactManager);
 
                 Main.LOG().info("");
                 Main.LOG().info("Assembling launcher...");
-                final ArtifactManager aMgr = artifactManager;
-                final LauncherPrepareContext ctx = new 
LauncherPrepareContext() {
 
+                final LauncherPrepareContext ctx = new LauncherPrepareContext()
+                {
                     @Override
-                    public File getArtifactFile(final ArtifactId artifact) 
throws IOException {
-                        final ArtifactHandler handler = 
aMgr.getArtifactHandler(":" + artifact.toMvnPath());
-                        if (m_populate != null) {
-                            File source = handler.getFile();
-                            File target = new File(m_populate, 
artifact.toMvnPath().replace('/', File.separatorChar));
-
-                            if (!target.isFile()) {
-                                if (Main.LOG().isDebugEnabled()) {
-                                    Main.LOG().debug("Populating {} with {}", 
target.getAbsolutePath(), source.getAbsolutePath());
-                                }
-                                target.getParentFile().mkdirs();
-                                Files.copy(source.toPath(), target.toPath());
-                            }
+                    public File getArtifactFile(final ArtifactId artifact) 
throws IOException
+                    {
+                        final ArtifactHandler handler = 
artifactManager.getArtifactHandler(":" + artifact.toMvnPath());
+                        if (m_populate != null)
+                        {
+                            populate(handler.getFile(), artifact);
                         }
                         return handler.getFile();
                     }
 
                     @Override
-                    public void addAppJar(final File jar) {
+                    public void addAppJar(final File jar)
+                    {
                         launcherConfig.getInstallation().addAppJar(jar);
                     }
                 };
 
-                // use hard coded Apache Felix
                 launcher.prepare(ctx, 
IOUtils.getFelixFrameworkId(m_frameworkVersion), app);
 
                 FeatureProcessor.prepareLauncher(launcherConfig, 
artifactManager, app);
@@ -239,46 +235,86 @@ public class Main {
                 Main.LOG().info("Using {} local artifacts, {} cached 
artifacts, and {} downloaded artifacts",
                     launcherConfig.getLocalArtifacts(), 
launcherConfig.getCachedArtifacts(), launcherConfig.getDownloadedArtifacts());
 
-                if (m_populate != null) {
+                if (m_populate != null)
+                {
                     Map<Artifact, File> local = 
FeatureProcessor.calculateArtifacts(artifactManager, app);
-                    for (Map.Entry<Artifact, File> entry : local.entrySet()) {
-                        File source = entry.getValue();
-                        File target = new File(m_populate, 
entry.getKey().getId().toMvnPath().replace('/', File.separatorChar));
-
-                        if (!target.isFile()) {
-                            if (Main.LOG().isDebugEnabled()) {
-                                Main.LOG().debug("Populating {} with {}", 
target.getAbsolutePath(), source.getAbsolutePath());
-                            }
-                            target.getParentFile().mkdirs();
-                            Files.copy(source.toPath(), target.toPath());
-                        }
+                    for (Map.Entry<Artifact, File> entry : local.entrySet())
+                    {
+                        populate(entry.getValue(), entry.getKey().getId());
                     }
                     return;
                 }
 
+                if (restart) {
+                    
launcherConfig.getInstallation().getInstallableArtifacts().clear();
+                    
launcherConfig.getInstallation().getConfigurations().clear();
+                    launcherConfig.getInstallation().getBundleMap().clear();
+                }
             } catch ( final Exception iae) {
                 Main.LOG().error("Error while assembling launcher: {}", 
iae.getMessage(), iae);
                 System.exit(1);
             }
-        } finally {
-            if ( artifactManager != null ) {
-                artifactManager.shutdown();
-            }
         }
-
-        if (launcherConfig.getApplicationFile() == null) {
-            launcherConfig.getInstallation().getBundleMap().clear();
-            launcherConfig.getInstallation().getConfigurations().clear();
-            launcherConfig.getInstallation().getInstallableArtifacts().clear();
+        catch (IOException ex) {
+            Main.LOG().error("Unable to setup artifact manager: {}", 
ex.getMessage(), ex);
+            System.exit(1);
         }
+
         try {
-            run(launcherConfig);
+            run(launcherConfig, launcher);
         } catch ( final Exception iae) {
             Main.LOG().error("Error while running launcher: {}", 
iae.getMessage(), iae);
             System.exit(1);
         }
     }
 
+    private static void populate(File file, ArtifactId artifactId) throws 
IOException{
+        File target = new File(m_populate, artifactId.toMvnPath().replace('/', 
File.separatorChar));
+
+        if (!target.isFile())
+        {
+            if (Main.LOG().isDebugEnabled())
+            {
+                Main.LOG().debug("Populating {} with {}", 
target.getAbsolutePath(), file.getAbsolutePath());
+            }
+            target.getParentFile().mkdirs();
+            Files.copy(file.toPath(), target.toPath());
+        }
+    }
+
+    private static Feature assemble(final LauncherConfig launcherConfig, final 
ArtifactManager artifactManager) throws IOException
+    {
+        if (launcherConfig.getFeatureFiles().length == 0) {
+            File application = new File(launcherConfig.getHomeDirectory(), 
"resources" + File.separatorChar + "provisioning" + File.separatorChar + 
"application.json");
+            if (application.isFile()) {
+                
launcherConfig.addFeatureFiles(application.toURI().toURL().toString());
+            }
+            else {
+                throw new IllegalStateException("No feature(s) to launch found 
and none where specified");
+            }
+            return FeatureProcessor.createApplication(launcherConfig, 
artifactManager);
+        }
+        else
+        {
+            final Feature app = 
FeatureProcessor.createApplication(launcherConfig, artifactManager);
+
+            // write application back
+            final File file = new File(launcherConfig.getHomeDirectory(), 
"resources" + File.separatorChar + "provisioning" + File.separatorChar + 
"application.json");
+            file.getParentFile().mkdirs();
+
+            try (final FileWriter writer = new FileWriter(file))
+            {
+                FeatureJSONWriter.write(writer, app);
+            }
+            catch (final IOException ioe)
+            {
+                Main.LOG().error("Error while writing application file: {}", 
ioe.getMessage(), ioe);
+                System.exit(1);
+            }
+            return app;
+        }
+    }
+
     private static final String STORAGE_PROPERTY = 
"org.osgi.framework.storage";
 
     private static final String START_LEVEL_PROP = 
"org.osgi.framework.startlevel.beginning";
@@ -288,7 +324,7 @@ public class Main {
      * @param config The configuration
      * @throws Exception If anything goes wrong
      */
-    private static void run(final LauncherConfig config) throws Exception {
+    private static void run(final LauncherConfig config, final Launcher 
launcher) throws Exception {
         Main.LOG().info("");
         Main.LOG().info("Starting launcher...");
         Main.LOG().info("Launcher Home: {}", 
config.getHomeDirectory().getAbsolutePath());
@@ -317,7 +353,6 @@ public class Main {
             installation.getFrameworkProperties().put(START_LEVEL_PROP, "30");
         }
 
-        final Launcher launcher = new FrameworkLauncher();
         while (launcher.run(installation, createClassLoader(installation)) == 
FrameworkEvent.STOPPED_SYSTEM_REFRESHED) {
             Main.LOG().info("Framework restart due to extension refresh");
         }

Reply via email to