sling-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From cziege...@apache.org
Subject svn commit: r1797026 [1/2] - in /sling/whiteboard/cziegeler: feature-launcher/ feature-launcher/src/ feature-launcher/src/main/ feature-launcher/src/main/java/ feature-launcher/src/main/java/org/ feature-launcher/src/main/java/org/apache/ feature-launc...
Date Wed, 31 May 2017 12:29:13 GMT
Author: cziegeler
Date: Wed May 31 12:29:12 2017
New Revision: 1797026

URL: http://svn.apache.org/viewvc?rev=1797026&view=rev
Log:
Add launcher prototype

Added:
    sling/whiteboard/cziegeler/feature-launcher/
    sling/whiteboard/cziegeler/feature-launcher/pom.xml   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/
    sling/whiteboard/cziegeler/feature-launcher/src/main/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/api/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/api/Logger.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Main.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactHandler.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactManager.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkRunner.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/spi/
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/spi/ArtifactProvider.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/spi/ArtifactProviderContext.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/spi/Launcher.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/spi/LauncherPrepareContext.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/spi/LauncherRunContext.java   (with props)
    sling/whiteboard/cziegeler/feature-launcher/src/test/
    sling/whiteboard/cziegeler/feature-launcher/src/test/java/
    sling/whiteboard/cziegeler/feature-launcher/src/test/java/org/
    sling/whiteboard/cziegeler/feature-launcher/src/test/java/org/apache/
    sling/whiteboard/cziegeler/feature-launcher/src/test/java/org/apache/sling/
    sling/whiteboard/cziegeler/feature-launcher/src/test/java/org/apache/sling/feature/
    sling/whiteboard/cziegeler/feature-launcher/src/test/java/org/apache/sling/feature/launcher/
    sling/whiteboard/cziegeler/feature-launcher/src/test/java/org/apache/sling/feature/launcher/impl/
    sling/whiteboard/cziegeler/feature-launcher/src/test/java/org/apache/sling/feature/launcher/impl/artifacts/
    sling/whiteboard/cziegeler/feature-launcher/src/test/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactManagerTest.java   (with props)
    sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/json/ApplicationJSONWriter.java   (with props)
Modified:
    sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/json/FeatureJSONReader.java
    sling/whiteboard/cziegeler/feature/src/main/java/org/apache/sling/feature/json/FeatureJSONWriter.java

Added: sling/whiteboard/cziegeler/feature-launcher/pom.xml
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/pom.xml?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/pom.xml (added)
+++ sling/whiteboard/cziegeler/feature-launcher/pom.xml Wed May 31 12:29:12 2017
@@ -0,0 +1,104 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+    <!--
+        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.
+    -->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.apache.sling</groupId>
+        <artifactId>sling</artifactId>
+        <version>30</version>
+        <relativePath />
+    </parent>
+
+    <artifactId>org.apache.sling.feature.launcher</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+
+    <name>Apache Sling Feature Launcher</name>
+    <description>
+        A application launcher using Apache Sling Features
+    </description>
+
+    <properties>
+        <sling.java.version>8</sling.java.version>
+    </properties>
+
+    <scm>
+        <connection>scm:svn:http://svn.apache.org/repos/asf/sling/trunk/tooling/support/feature-launcher</connection>
+        <developerConnection>scm:svn:https://svn.apache.org/repos/asf/sling/trunk/tooling/support/featurelauncher</developerConnection>
+        <url>http://svn.apache.org/viewvc/sling/trunk/tooling/support/featurelauncher</url>
+    </scm>
+
+    <build>
+        <plugins>
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-dependency-plugin</artifactId>
+                <executions>
+                <execution>
+                    <id>unpack-dependencies</id>
+                    <phase>prepare-package</phase>
+                    <goals>
+                        <goal>unpack-dependencies</goal>
+                    </goals>
+                    <configuration>
+                        <excludes>META-INF/**</excludes>
+                        <outputDirectory>${project.build.directory}/classes</outputDirectory>
+                        <overWriteReleases>false</overWriteReleases>
+                        <overWriteSnapshots>true</overWriteSnapshots>
+                        <includeArtifactIds>org.apache.sling.provisioning.feature,commons-cli</includeArtifactIds>
+                    </configuration>
+                </execution>
+            </executions>
+            </plugin>
+                <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <mainClass>org.apache.sling.feature.launcher.impl.Main</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>osgi.core</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.feature</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+             <groupId>commons-cli</groupId>
+             <artifactId>commons-cli</artifactId>
+             <version>1.3.1</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.mockito</groupId>
+            <artifactId>mockito-core</artifactId>
+            <version>2.8.9</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+</project>

Propchange: sling/whiteboard/cziegeler/feature-launcher/pom.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/pom.xml
------------------------------------------------------------------------------
    svn:keywords = Id

Added: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/api/Logger.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/api/Logger.java?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/api/Logger.java (added)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/api/Logger.java Wed May 31 12:29:12 2017
@@ -0,0 +1,112 @@
+/*
+ * 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.feature.launcher.api;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.text.MessageFormat;
+
+/**
+ * A simple logger to be used by extensions for the launcher.
+ * Extensions should get the logger through the shared instance {@link #LOG}
+ * The returned logger instance is not thread-safe, it is assumed
+ * that all launching is single threaded!
+ */
+public class Logger {
+
+    /**
+     * The shared logger instance.
+     */
+    public static Logger LOG = new Logger();
+
+    /** Flag if debug is enabled. */
+    private volatile boolean isDebug = false;
+
+    /** Flag if info is enabled. */
+    private final boolean isInfo = true;
+
+    /** Flag if warn is enabled. */
+    private final boolean isWarn = true;
+
+    public boolean isDebugEnabled() {
+        return isDebug;
+    }
+
+    public boolean isInfoEnabled() {
+        return isInfo;
+    }
+
+    public boolean isWarnEnabled() {
+        return isWarn;
+    }
+
+    public void debug(final String msg, final Throwable t) {
+        if ( isDebug ) {
+            final StringWriter sw = new StringWriter();
+            final PrintWriter pw = new PrintWriter(sw);
+            t.printStackTrace(pw);
+            pw.flush();
+            log("[DEBUG]", msg);
+            log("[DEBUG]", sw.toString());
+        }
+    }
+
+    public void debug(final String msg, final Object... params) {
+        if ( isDebug ) {
+            log("[DEBUG]", msg, params);
+        }
+    }
+
+    public void info(final String msg, final Object... params) {
+        if ( isInfo ) {
+            log("[INFO]", msg, params);
+        }
+    }
+
+    public void warn(final String msg, final Object... params) {
+        if ( isWarn ) {
+            log("[WARN]", msg, params);
+        }
+    }
+
+    public void error(final String msg, final Throwable t, final Object... params) {
+        log("[ERROR]", msg, params);
+        final StringWriter sw = new StringWriter();
+        final PrintWriter pw = new PrintWriter(sw);
+        t.printStackTrace(pw);
+        pw.flush();
+        log("[ERROR]", sw.toString());
+    }
+
+    public void error(final String msg, final Object... params) {
+        log("[ERROR]", msg, params);
+    }
+
+    public void log(final String msg, final Object... params) {
+        System.out.println(MessageFormat.format(msg, params));
+    }
+
+    private void log(final String prefix, final String msg, final Object... params) {
+        System.out.print(prefix);
+        System.out.print(' ');
+        System.out.println(MessageFormat.format(msg, params));
+    }
+
+    public void setDebug(final boolean b) {
+       this.isDebug = b;
+    }
+}

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/api/Logger.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/api/Logger.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java (added)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java Wed May 31 12:29:12 2017
@@ -0,0 +1,156 @@
+/*
+ * 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.feature.launcher.impl;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.feature.Application;
+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.json.ApplicationJSONWriter;
+import org.apache.sling.feature.json.FeatureJSONReader;
+import org.apache.sling.feature.launcher.api.Logger;
+import org.apache.sling.feature.launcher.impl.LauncherConfig.StartupMode;
+import org.apache.sling.feature.launcher.impl.artifacts.ArtifactHandler;
+import org.apache.sling.feature.launcher.impl.artifacts.ArtifactManager;
+import org.apache.sling.feature.process.ApplicationBuilder;
+import org.apache.sling.feature.process.FeatureProvider;
+
+public class FeatureProcessor {
+
+    /**
+     * Initialize the launcher
+     * Read the features and prepare the application
+     * @param config The current configuration
+     * @param artifactManager The artifact manager
+     */
+    public static Application createApplication(final LauncherConfig config,
+            final ArtifactManager artifactManager) {
+        final List<Feature> features = new ArrayList<>();
+        try {
+            for(String initFile : config.getFeatureFiles()) {
+                try {
+                    final Feature f = getFeature(config, initFile, artifactManager);
+                    features.add(f);
+                } catch ( final IOException iae) {
+                    Logger.LOG.error("Unable to read feature {0} : {1}", iae, initFile, iae.getMessage());
+                    System.exit(1);
+                }
+            }
+        } catch ( final IOException iae) {
+            Logger.LOG.error("Unable to read feature : {0}", iae, iae.getMessage());
+            System.exit(1);
+        }
+
+        final Application app = ApplicationBuilder.assemble(new FeatureProvider() {
+
+            @Override
+            public Feature provide(ArtifactId id) {
+                // TODO Auto-generated method stub
+                return null;
+            }
+        }, features.toArray(new Feature[features.size()]));
+
+        final File file = new File(config.getHomeDirectory(), "resources" + File.separatorChar + "provisioning" + File.separatorChar + "feature.txt");
+        file.getParentFile().mkdirs();
+
+        try (final FileWriter writer = new FileWriter(file)) {
+            ApplicationJSONWriter.write(writer, app);
+        } catch ( final IOException ioe) {
+            Logger.LOG.error("Error while writing application file: {0}", ioe, ioe.getMessage());
+            System.exit(1);
+        }
+
+        return app;
+    }
+
+    /**
+     * Read the feature
+     * @param config The launcher configuration
+     * @param name The feature file
+     * @param artifactManager The artifact manager to read the model
+     * @return The read feature
+     * @throws IOException If reading fails
+     */
+    private static Feature getFeature(final LauncherConfig config,
+            final String file,
+            final ArtifactManager artifactManager) throws IOException {
+        ArtifactHandler featureArtifact = null;
+        try {
+            featureArtifact = artifactManager.getArtifactHandler(file);
+        } catch ( final IOException use) {
+            Logger.LOG.error("Invalid feature url {0}", file);
+            Logger.LOG.debug("Exception while getting artifact " + file, use);
+            System.exit(1);
+        }
+        Logger.LOG.log("- reading feature {0}", featureArtifact.getArtifactUrl());
+
+        try (final FileInputStream is = new FileInputStream(featureArtifact.getArtifact())) {
+            final Feature f = FeatureJSONReader.read(new InputStreamReader(is, "UTF-8"), featureArtifact.getArtifactUrl());
+            return f;
+        }
+    }
+
+    /**
+     * Prepare the launcher
+     * - add all bundles to the bundle map of the installation object
+     * - add all other artifacts to the install directory (only if startup mode is INSTALL)
+     * - process configurations
+     */
+    public static void prepareLauncher(final LauncherConfig config,
+            final ArtifactManager artifactManager,
+            final Application app) throws Exception {
+        for(final Map.Entry<Integer, List<Artifact>> entry : app.getBundles().getBundlesByStartLevel().entrySet()) {
+            for(final Artifact a : entry.getValue()) {
+                final ArtifactHandler handler = artifactManager.getArtifactHandler(":" + a.getId().toMvnPath());
+                final File artifactFile = handler.getArtifact();
+
+                config.getInstallation().addBundle(entry.getKey(), artifactFile);
+            }
+        }
+        for(final Extension ext : app.getExtensions()) {
+            if ( ext.getType() == ExtensionType.ARTIFACTS ) {
+                for(final Artifact a : ext.getArtifacts() ) {
+                    if ( config.getStartupMode() == StartupMode.PURE ) {
+                        throw new Exception("Artifacts other than bundle are not supported by framework launcher.");
+                    }
+                    final ArtifactHandler handler = artifactManager.getArtifactHandler(":" + a.getId().toMvnPath());
+                    config.getInstallation().addInstallableArtifact(handler.getArtifact());
+                }
+            }
+        }
+
+        for(final Configuration cfg : app.getConfigurations()) {
+            if ( cfg.isFactoryConfiguration() ) {
+                config.getInstallation().addConfiguration(cfg.getName(), cfg.getFactoryPid(), cfg.getProperties());
+            } else {
+                config.getInstallation().addConfiguration(cfg.getPid(), null, cfg.getProperties());
+            }
+        }
+    }
+}

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/FeatureProcessor.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java (added)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java Wed May 31 12:29:12 2017
@@ -0,0 +1,137 @@
+/*
+ * 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.feature.launcher.impl;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Dictionary;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.feature.launcher.spi.LauncherRunContext;
+
+/**
+ * This class holds the configuration of the launcher.
+ */
+public class Installation implements LauncherRunContext {
+
+    /** The map with the framework properties. */
+    private final Map<String, String> fwkProperties = new HashMap<>();
+
+    /** Bundle map */
+    private final Map<Integer, List<File>> bundleMap = new HashMap<>();
+
+    /** Artifacts to be installed */
+    private final List<File> installables = new ArrayList<>();
+
+    /** Configurations, they are installed on first start. */
+    private final List<Object[]> configurations = new ArrayList<>();
+
+    /** The list of app jars. */
+    private final List<File> appJars = new ArrayList<>();
+
+    /**
+     * Add an application jar.
+     * @param jar The application jar
+     */
+    public void addAppJar(final File jar) {
+        this.appJars.add(jar);
+    }
+
+    /**
+     * Get the list of application jars.
+     * @return The list of app jars
+     */
+    public List<File> getAppJars() {
+        return this.appJars;
+    }
+
+    /**
+     * Add a bundle with the given start level
+     * @param startLevel The start level
+     * @param file The bundle file
+     */
+    public void addBundle(final Integer startLevel, final File file) {
+        List<File> files = bundleMap.get(startLevel);
+        if ( files == null ) {
+            files = new ArrayList<>();
+            bundleMap.put(startLevel, files);
+        }
+        files.add(file);
+    }
+
+    /**
+     * Add an artifact to be installed by the installer
+     * @param file The file
+     */
+    public void addInstallableArtifact(final File file) {
+        this.installables.add(file);
+    }
+
+    /**
+     * Add a configuration
+     * @param pid The pid
+     * @param factoryPid The factory pid
+     * @param properties The propertis
+     */
+    public void addConfiguration(final String pid, final String factoryPid, final Dictionary<String, Object> properties) {
+        this.configurations.add(new Object[] {pid, factoryPid, properties});
+    }
+
+    /**
+     * @see org.apache.sling.feature.launcher.spi.LauncherRunContext#getFrameworkProperties()
+     */
+    @Override
+    public Map<String, String> getFrameworkProperties() {
+        return this.fwkProperties;
+    }
+
+    /**
+     * @see org.apache.sling.feature.launcher.spi.LauncherRunContext#getBundleMap()
+     */
+    @Override
+    public Map<Integer, List<File>> getBundleMap() {
+        return this.bundleMap;
+    }
+
+    /**
+     * @see org.apache.sling.feature.launcher.spi.LauncherRunContext#getConfigurations()
+     */
+    @Override
+    public List<Object[]> getConfigurations() {
+        return this.configurations;
+    }
+
+    /**
+     * @see org.apache.sling.feature.launcher.spi.LauncherRunContext#getInstallableArtifacts()
+     */
+    @Override
+    public List<File> getInstallableArtifacts() {
+        return this.installables;
+    }
+
+    /**
+     * Clear all in-memory objects
+     */
+    public void clear() {
+        this.configurations.clear();
+        this.fwkProperties.clear();
+        this.bundleMap.clear();
+        this.installables.clear();
+    }
+}

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Installation.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java (added)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java Wed May 31 12:29:12 2017
@@ -0,0 +1,296 @@
+/*
+ * 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.feature.launcher.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+import org.apache.sling.feature.launcher.spi.ArtifactProviderContext;
+
+/**
+ * This class holds the configuration of the launcher.
+ */
+public class LauncherConfig implements ArtifactProviderContext {
+
+    public static final Comparator<String> FEATURE_PATH_COMP = new Comparator<String>() {
+
+        @Override
+        public int compare(final String o1, final String o2) {
+            // windows path conversion
+            final String key1 = o1.replace(File.separatorChar, '/');
+            final String key2 = o2.replace(File.separatorChar, '/');
+
+            final int lastSlash1 = key1.lastIndexOf('/');
+            final int lastSlash2 = key2.lastIndexOf('/');
+            if ( lastSlash1 == -1 || lastSlash2 == -1 ) {
+                return o1.compareTo(o2);
+            }
+            final String path1 = key1.substring(0, lastSlash1 + 1);
+            final String path2 = key2.substring(0, lastSlash2 + 1);
+            if ( path1.equals(path2) ) {
+                return o1.compareTo(o2);
+            }
+            if ( path1.startsWith(path2) ) {
+                return 1;
+            } else if ( path2.startsWith(path1) ) {
+                return -1;
+            }
+            return o1.compareTo(o2);
+        }
+    };
+
+    public enum StartupMode {
+        INSTALLER,
+        PURE
+    };
+
+    private static final String HOME = "launcher";
+
+    private static final String CACHE_DIR = "cache";
+
+    /** The repository urls. */
+    private volatile String[] repositoryUrls;
+
+    /** The feature files or directories. */
+    private volatile String[] featureFiles;
+
+    private volatile StartupMode startupMode = StartupMode.PURE;
+
+    private volatile long cachedArtifacts;
+
+    private volatile long downloadedArtifacts;
+
+    private volatile long localArtifacts;
+
+    private final Installation installation = new Installation();
+
+    /**
+     * Create a new configuration object.
+     * Set the default values
+     */
+    public LauncherConfig() {
+        // set defaults
+        this.repositoryUrls = new String[] {
+                "file://" + System.getProperty("user.home") + "/.m2/repository",
+                "https://repo.maven.apache.org/maven2/"
+                };
+    }
+
+    /**
+     * Set the repository urls
+     * @param repositoryUrl The repository urls
+     */
+    public void setRepositoryUrls(final String[] urls) {
+        if ( urls == null || urls.length == 0 ) {
+            this.repositoryUrls = null;
+        } else {
+            this.repositoryUrls = new String[urls.length];
+            System.arraycopy(urls, 0, this.repositoryUrls, 0, urls.length);
+            for(int i=0; i<this.repositoryUrls.length; i++) {
+                if ( this.repositoryUrls[i].endsWith("/") ) {
+                    this.repositoryUrls[i] = this.repositoryUrls[i].substring(0, this.repositoryUrls[i].length() - 1);
+                }
+            }
+        }
+    }
+
+    /**
+     * Set the list of feature files or directories.
+     * @param value 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;
+        }
+    }
+
+    /**
+     * Get the repository urls.
+     * A repository url does not end with a slash.
+     * @return The repository urls.
+     */
+    public String[] getRepositoryUrls() {
+        return repositoryUrls;
+    }
+
+    private void processDir(final List<String> paths, final File dir)
+    throws IOException {
+        for(final File f : dir.listFiles()) {
+            if ( f.isFile() && !f.getName().startsWith(".")) {
+                // check if file is a reference
+                if ( f.getName().endsWith(".ref") ) {
+                    final List<String> lines = Files.readAllLines(f.toPath());
+                    for(String line : lines) {
+                        line = line.trim();
+                        if ( !line.isEmpty() && !line.startsWith("#") ) {
+                            paths.add(line);
+                        }
+                    }
+                } else {
+                    paths.add(f.getAbsolutePath());
+                }
+            }
+        }
+    }
+
+    private void processFile(final List<String> paths, final File f)
+    throws IOException {
+        if ( f.getName().endsWith(".ref") ) {
+            final List<String> lines = Files.readAllLines(f.toPath());
+            for(String line : lines) {
+                line = line.trim();
+                if ( !line.isEmpty() && !line.startsWith("#") ) {
+                    if ( line.indexOf(':') == -1 ) {
+                        paths.add(new File(line).getAbsolutePath());
+                    } else {
+                        paths.add(line);
+                    }
+                }
+            }
+        } else {
+            paths.add(f.getAbsolutePath());
+        }
+    }
+
+    /**
+     * Get the list of feature files.
+     * @return The array of names.
+     * @throws IOException
+     */
+    public String[] getFeatureFiles() throws IOException {
+        if ( this.featureFiles == null ) {
+            // Default value - check feature directory otherwise features file
+            final File dir = new File(getHomeDirectory(), "features");
+            if ( dir.exists() && dir.isDirectory() ) {
+                this.featureFiles = new String[] {"features"};
+            } else {
+                this.featureFiles = new String[] {new File("feature.txt").getAbsolutePath()};
+            }
+        }
+        final List<String> paths = new ArrayList<>();
+        for(final String name : this.featureFiles) {
+            // check for absolute
+            if ( name.indexOf(':') > 0 ) {
+                paths.add(name);
+            } else {
+                // relative
+                final File f = new File(name);
+                if ( f.exists() ) {
+                    if ( f.isFile() ) {
+                        this.processFile(paths, f);
+                    } else {
+                        this.processDir(paths, f);
+                    }
+                } else if ( !f.exists() ) {
+                    final File check = new File(getHomeDirectory(), "features" + File.separatorChar + name);
+                    if ( check.exists() && !check.getName().startsWith(".") ) {
+                        if ( check.isFile() ) {
+                            this.processFile(paths, check);
+                        } else {
+                            this.processDir(paths, check);
+                        }
+                    }
+                }
+            }
+        }
+
+        Collections.sort(paths, FEATURE_PATH_COMP);
+        return paths.toArray(new String[paths.size()]);
+    }
+
+
+    /**
+     * Get the home directory.
+     * @return The home directory.
+     */
+    public File getHomeDirectory() {
+        return new File(HOME);
+    }
+
+    /**
+     * Get the cache directory
+     * @return The cache directory.
+     */
+    @Override
+    public File getCacheDirectory() {
+        final File dir = new File(CACHE_DIR);
+        if ( dir.isAbsolute() ) {
+            return dir;
+        }
+        return new File(getHomeDirectory(), CACHE_DIR);
+    }
+
+    /**
+     * Get the startup mode.
+     *
+     * @return The current startup mode.
+     */
+    public StartupMode getStartupMode() {
+        return this.startupMode;
+    }
+
+    /**
+     * Sets the startup mode to {@link StartupMode#INSTALLER}.
+     */
+    public void setUseInstaller() {
+        this.startupMode = StartupMode.INSTALLER;
+    }
+
+    public Installation getInstallation() {
+        return this.installation;
+    }
+
+    /**
+     * Clear all in-memory objects
+     */
+    public void clear() {
+        this.installation.clear();
+    }
+
+    @Override
+    public void incCachedArtifacts() {
+        this.cachedArtifacts++;
+    }
+
+    @Override
+    public void incDownloadedArtifacts() {
+        this.downloadedArtifacts++;
+    }
+
+    @Override
+    public void incLocalArtifacts() {
+        this.localArtifacts++;
+    }
+
+    public long getCachedArtifacts() {
+        return this.cachedArtifacts;
+    }
+
+    public long getDownloadedArtifacts() {
+        return this.downloadedArtifacts;
+    }
+
+    public long getLocalArtifacts() {
+        return this.localArtifacts;
+    }
+}

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/LauncherConfig.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Main.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Main.java?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Main.java (added)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Main.java Wed May 31 12:29:12 2017
@@ -0,0 +1,315 @@
+/*
+ * 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.feature.launcher.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.cli.BasicParser;
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.sling.feature.Application;
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.launcher.api.Logger;
+import org.apache.sling.feature.launcher.impl.artifacts.ArtifactHandler;
+import org.apache.sling.feature.launcher.impl.artifacts.ArtifactManager;
+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;
+
+/**
+ * This is the launcher main class.
+ * It parses command line parameters and prepares the launcher.
+ */
+public class Main {
+
+    /** Split a string into key and value */
+    private static String[] split(final String val) {
+        final int pos = val.indexOf('=');
+        if ( pos == -1 ) {
+            return new String[] {val, "true"};
+        }
+        return new String[] {val.substring(0, pos), val.substring(pos + 1)};
+    }
+
+    /**
+     * Parse the command line parameters and update a configuration object.
+     * @param args Command line parameters
+     * @return Configuration object.
+     */
+    private static void parseArgs(final LauncherConfig config, final String[] args) {
+        Logger.LOG.log("Assembling configuration...");
+        final Options options = new Options();
+
+        final Option repoOption =  new Option("u", true, "Set repository url");
+        final Option modelOption =  new Option("f", true, "Set feature files/directories");
+        final Option fwkProperties = new Option("D", true, "Set framework properties");
+        fwkProperties.setArgs(20);
+        final Option debugOption = new Option("v", true, "Verbose");
+        debugOption.setArgs(0);
+        final Option installerOption = new Option("I", true, "Use OSGi installer for additional artifacts.");
+        installerOption.setArgs(0);
+        options.addOption(repoOption);
+        options.addOption(modelOption);
+        options.addOption(fwkProperties);
+        options.addOption(debugOption);
+        options.addOption(installerOption);
+
+        final CommandLineParser clp = new BasicParser();
+        try {
+            final CommandLine cl = clp.parse(options, args);
+
+            if ( cl.hasOption(repoOption.getOpt()) ) {
+                final String value = cl.getOptionValue(repoOption.getOpt());
+                config.setRepositoryUrls(value.split(","));
+            }
+            if ( cl.hasOption(modelOption.getOpt()) ) {
+                final String value = cl.getOptionValue(modelOption.getOpt());
+                config.setFeatureFiles(value.split(","));
+            }
+            if ( cl.hasOption(fwkProperties.getOpt()) ) {
+                for(final String value : cl.getOptionValues(fwkProperties.getOpt())) {
+                    final String[] keyVal = split(value);
+
+                    config.getInstallation().getFrameworkProperties().put(keyVal[0], keyVal[1]);
+                }
+            }
+            if ( cl.hasOption(debugOption.getOpt()) ) {
+                Logger.LOG.setDebug(true);
+            }
+            if ( cl.hasOption(installerOption.getOpt()) ) {
+                config.setUseInstaller();
+            }
+        } catch ( final ParseException pe) {
+            Logger.LOG.error("Unable to parse command line: {0}", pe, pe.getMessage());
+            System.exit(1);
+        }
+    }
+
+    public static void main(final String[] args) {
+        Logger.LOG.log("");
+        Logger.LOG.log("Apache Sling Application Launcher");
+        Logger.LOG.log("---------------------------------");
+
+        // check if launcher has already been created
+        final LauncherConfig launcherConfig = new LauncherConfig();
+        parseArgs(launcherConfig, args);
+
+        ArtifactManager artifactManager = null;
+        try {
+
+            Logger.LOG.log("Initializing...");
+            try {
+                artifactManager = ArtifactManager.getArtifactManager(launcherConfig);
+            } catch ( final IOException ioe) {
+                Logger.LOG.error("Unable to setup artifact manager: {0}", ioe, ioe.getMessage());
+                System.exit(1);
+            }
+            Logger.LOG.log("Artifact Repositories: {0}", Arrays.toString(launcherConfig.getRepositoryUrls()));
+            Logger.LOG.log("Assembling provisioning model...");
+
+            try {
+                final Launcher launcher = new FrameworkLauncher();
+                final Application app = FeatureProcessor.createApplication(launcherConfig, artifactManager);
+
+                Logger.LOG.log("");
+                Logger.LOG.log("Assembling launcher...");
+                final ArtifactManager aMgr = artifactManager;
+                final LauncherPrepareContext ctx = new LauncherPrepareContext() {
+
+                    @Override
+                    public File getArtifactFile(final ArtifactId artifact) throws IOException {
+                        final ArtifactHandler handler = aMgr.getArtifactHandler(":" + artifact.toMvnPath());
+                        return handler.getArtifact();
+                    }
+
+                    @Override
+                    public void addAppJar(final File jar) {
+                        launcherConfig.getInstallation().addAppJar(jar);
+                    }
+                };
+                launcher.prepare(ctx, app);
+
+                FeatureProcessor.prepareLauncher(launcherConfig, artifactManager, app);
+
+            } catch ( final Exception iae) {
+                Logger.LOG.error("Error while assembling launcher: {0}", iae, iae.getMessage());
+                System.exit(1);
+            }
+
+            Logger.LOG.log("Using {0} local artifacts, {1} cached artifacts, and {2} downloaded artifacts",
+                    launcherConfig.getLocalArtifacts(), launcherConfig.getCachedArtifacts(), launcherConfig.getDownloadedArtifacts());
+        } finally {
+            if ( artifactManager != null ) {
+                artifactManager.shutdown();
+            }
+        }
+
+        try {
+            run(launcherConfig);
+        } catch ( final Exception iae) {
+            Logger.LOG.error("Error while running launcher: {0}", iae, iae.getMessage());
+            System.exit(1);
+        }
+    }
+
+    private static final String STORAGE_PROPERTY = "org.osgi.framework.storage";
+
+    private static final String START_LEVEL_PROP = "org.osgi.framework.startlevel.beginning";
+
+    /**
+     * Run launcher.
+     * @param config The configuration
+     * @throws Exception If anything goes wrong
+     */
+    private static void run(final LauncherConfig config) throws Exception {
+        Logger.LOG.log("");
+        Logger.LOG.log("Starting launcher...");
+        Logger.LOG.log("Launcher Home: {0}", config.getHomeDirectory().getAbsolutePath());
+        Logger.LOG.log("Cache Directory: {0}", config.getCacheDirectory().getAbsolutePath());
+        Logger.LOG.log("Startup Mode: {0}", config.getStartupMode());
+        Logger.LOG.log("");
+
+        final Installation installation = config.getInstallation();
+
+        // set sling home, and use separate locations for launchpad and properties
+        installation.getFrameworkProperties().put("sling.home", config.getHomeDirectory().getAbsolutePath());
+        installation.getFrameworkProperties().put("sling.launchpad", config.getHomeDirectory().getAbsolutePath() + "/launchpad");
+        installation.getFrameworkProperties().put("sling.properties", "conf/sling.properties");
+
+
+        // additional OSGi properties
+        // move storage inside launcher
+        if ( installation.getFrameworkProperties().get(STORAGE_PROPERTY) == null ) {
+            installation.getFrameworkProperties().put(STORAGE_PROPERTY, config.getHomeDirectory().getAbsolutePath() + File.separatorChar + "framework");
+        }
+        // set start level to 30
+        if ( installation.getFrameworkProperties().get(START_LEVEL_PROP) == null ) {
+            installation.getFrameworkProperties().put(START_LEVEL_PROP, "30");
+        }
+
+        if ( Logger.LOG.isDebugEnabled() ) {
+            Logger.LOG.debug("Bundles:");
+            for(final Integer key : installation.getBundleMap().keySet()) {
+                Logger.LOG.debug("-- Start Level {0}", key);
+                for(final File f : installation.getBundleMap().get(key)) {
+                    Logger.LOG.debug("  - {0}", f.getName());
+                }
+            }
+            Logger.LOG.debug("Settings: ");
+            for(final Map.Entry<String, String> entry : installation.getFrameworkProperties().entrySet()) {
+                Logger.LOG.debug("- {0}={1}", entry.getKey(), entry.getValue());
+            }
+            Logger.LOG.debug("Configurations: ");
+            for(final Object[] entry : installation.getConfigurations()) {
+                if ( entry[1] != null ) {
+                    Logger.LOG.debug("- Factory {0} - {1}", entry[1], entry[0]);
+                } else {
+                    Logger.LOG.debug("- {0}", entry[0]);
+                }
+            }
+            Logger.LOG.debug("");
+        }
+
+        final Launcher launcher = new FrameworkLauncher();
+        launcher.run(installation, createClassLoader(installation));
+
+        config.clear();
+    }
+
+    /**
+     * Create the class loader.
+     * @param installation The launcher configuration
+     * @throws Exception If anything goes wrong
+     */
+    public static ClassLoader createClassLoader(final Installation installation) throws Exception {
+        final List<URL> list = new ArrayList<>();
+        for(final File f : installation.getAppJars()) {
+            try {
+                list.add(f.toURI().toURL());
+            } catch (IOException e) {
+                // ignore
+            }
+        }
+        list.add(Main.class.getProtectionDomain().getCodeSource().getLocation());
+
+        final URL[] urls = list.toArray(new URL[list.size()]);
+
+        if ( Logger.LOG.isDebugEnabled() ) {
+            Logger.LOG.debug("App classpath: ");
+            for (int i = 0; i < urls.length; i++) {
+                Logger.LOG.debug(" - {0}", urls[i]);
+            }
+        }
+
+        // create a paranoid class loader, loading from parent last
+        final ClassLoader cl = new URLClassLoader(urls) {
+            @Override
+            public final Class<?> loadClass(String name, boolean resolve) throws ClassNotFoundException {
+                // First check if it's already loaded
+                Class<?> clazz = findLoadedClass(name);
+
+                if (clazz == null) {
+
+                    try {
+                        clazz = findClass(name);
+                    } catch (ClassNotFoundException cnfe) {
+                        ClassLoader parent = getParent();
+                        if (parent != null) {
+                            // Ask to parent ClassLoader (can also throw a CNFE).
+                            clazz = parent.loadClass(name);
+                        } else {
+                            // Propagate exception
+                            throw cnfe;
+                        }
+                    }
+                }
+
+                if (resolve) {
+                    resolveClass(clazz);
+                }
+
+                return clazz;
+            }
+
+            @Override
+            public final URL getResource(final String name) {
+
+                URL resource = findResource(name);
+                ClassLoader parent = this.getParent();
+                if (resource == null && parent != null) {
+                    resource = parent.getResource(name);
+                }
+
+                return resource;
+            }
+        };
+
+        Thread.currentThread().setContextClassLoader(cl);
+
+        return cl;
+    }
+}

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Main.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/Main.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactHandler.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactHandler.java?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactHandler.java (added)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactHandler.java Wed May 31 12:29:12 2017
@@ -0,0 +1,42 @@
+/*
+ * 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.feature.launcher.impl.artifacts;
+
+import java.io.File;
+
+/**
+ * A handler provides a file object for an artifact.
+ */
+public class ArtifactHandler {
+
+    private final String url;
+
+    private final File file;
+
+    public ArtifactHandler(final String url, final File file) {
+        this.url = url;
+        this.file = file;
+    }
+
+    public String getArtifactUrl() {
+        return url;
+    }
+
+    public File getArtifact() {
+        return file;
+    }
+}

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactHandler.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactHandler.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactManager.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactManager.java?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactManager.java (added)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactManager.java Wed May 31 12:29:12 2017
@@ -0,0 +1,362 @@
+/*
+ * 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.feature.launcher.impl.artifacts;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.nio.file.Files;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.ServiceLoader;
+
+import org.apache.sling.feature.ArtifactId;
+import org.apache.sling.feature.launcher.api.Logger;
+import org.apache.sling.feature.launcher.impl.LauncherConfig;
+import org.apache.sling.feature.launcher.spi.ArtifactProvider;
+import org.apache.sling.feature.launcher.spi.ArtifactProviderContext;
+
+/**
+ * The artifact manager is the central service to get artifacts.
+ * It uses {@link ArtifactProvider}s to get artifacts. The
+ * providers are loaded using the service loader.
+ */
+public class ArtifactManager {
+
+    /** The map of providers. */
+    private final Map<String, ArtifactProvider> providers;
+
+    /** The repository urls. */
+    private final String[] repositoryURLs;
+
+    /**
+     * Get an artifact manager based on the configuration
+     * @param config The configuration
+     * @return The artifact manager
+     * @throws IOException If the manager can't be initialized
+     */
+    public static ArtifactManager getArtifactManager(final LauncherConfig config) throws IOException {
+        final ServiceLoader<ArtifactProvider> loader = ServiceLoader.load(ArtifactProvider.class);
+        final Map<String, ArtifactProvider> providers = new HashMap<>();
+        for(final ArtifactProvider provider : loader) {
+            providers.put(provider.getProtocol(), provider);
+        }
+
+        final String[] repositoryURLs = new String[config.getRepositoryUrls().length];
+        int index = 0;
+        for(final String urlString : config.getRepositoryUrls()) {
+            repositoryURLs[index] = urlString;
+            index++;
+        }
+        // default
+        if ( !providers.containsKey("*") ) {
+            providers.put("*", new DefaultArtifactHandler());
+        }
+
+        return new ArtifactManager(config, repositoryURLs, providers);
+    }
+
+    ArtifactManager(final LauncherConfig config, final String[] repoUrls, final Map<String, ArtifactProvider> providers)
+    throws IOException {
+        this.repositoryURLs = repoUrls;
+        this.providers = providers;
+        try {
+            for(final ArtifactProvider provider : this.providers.values()) {
+                provider.init(config);
+            }
+        } catch ( final IOException io) {
+            shutdown();
+            throw io;
+        }
+    }
+
+    /**
+     * Shutdown the artifact manager.
+     */
+    public void shutdown() {
+        for(final ArtifactProvider provider : this.providers.values()) {
+            provider.shutdown();
+        }
+        this.providers.clear();
+    }
+
+    private final File getArtifactFromProviders(final String url, final String relativeCachePath) throws IOException {
+        final int pos = url.indexOf(":");
+        final String scheme = url.substring(0, pos);
+
+        ArtifactProvider provider = this.providers.get(scheme);
+        if ( provider == null ) {
+            provider = this.providers.get("*");
+        }
+        if ( provider == null ) {
+            throw new IOException("No URL provider found for " + url);
+        }
+        return provider.getArtifact(url, relativeCachePath);
+    }
+
+    /**
+     * Get the full artifact url for an artifact.
+     * @param url Artifact url or relative path.
+     * @return Absolute url.
+     * @throws IOException If something goes wrong.
+     */
+    public ArtifactHandler getArtifactHandler(final String url) throws IOException {
+        Logger.LOG.debug("Trying to get artifact for " + url);
+
+        final String path;
+
+        if ( url.startsWith("mvn:") ) {
+            // mvn url
+            path = ArtifactId.fromMvnUrl(url).toMvnPath();
+
+        } else if ( url.startsWith(":") ) {
+            // repository path
+            path = url.substring(1);
+
+        } else if ( url.indexOf(":/") > 0 ) {
+
+            // absolute URL
+            int pos = url.indexOf(":/") + 2;
+            while ( url.charAt(pos) == '/') {
+                pos++;
+            }
+            final File file = this.getArtifactFromProviders(url, url.substring(pos));
+            if ( file == null || !file.exists()) {
+                throw new IOException("Artifact " + url + " not found.");
+            }
+            return new ArtifactHandler(url, file);
+
+        } else {
+            // file (either relative or absolute)
+            final File f = new File(url);
+            if ( !f.exists()) {
+                throw new IOException("Artifact " + url + " not found.");
+            }
+            return new ArtifactHandler(f.toURI().toString(), f);
+        }
+        Logger.LOG.debug("Querying repositories for " + path);
+
+        for(final String repoUrl : this.repositoryURLs) {
+            final StringBuilder builder = new StringBuilder();
+            builder.append(repoUrl);
+            builder.append('/');
+            builder.append(path);
+
+            final String artifactUrl = builder.toString();
+            final int pos = artifactUrl.indexOf(":");
+            final String scheme = artifactUrl.substring(0, pos);
+
+            ArtifactProvider handler = this.providers.get(scheme);
+            if ( handler == null ) {
+                handler = this.providers.get("*");
+            }
+            if ( handler == null ) {
+                throw new IOException("No URL handler found for " + artifactUrl);
+            }
+
+            Logger.LOG.debug("Checking " + handler + " to get artifact from " + artifactUrl);
+
+            final File file = handler.getArtifact(artifactUrl, path);
+            if ( file != null ) {
+                Logger.LOG.debug("Found artifact " + artifactUrl);
+                return new ArtifactHandler(artifactUrl, file);
+            }
+
+            // check for SNAPSHOT
+            final int lastSlash = artifactUrl.lastIndexOf('/');
+            final int startSnapshot = artifactUrl.indexOf("-SNAPSHOT", lastSlash + 1);
+
+            if ( startSnapshot > -1 ) {
+                // special snapshot handling
+                final String metadataUrl = artifactUrl.substring(0, lastSlash) + "/maven-metadata.xml";
+                try {
+                    final ArtifactHandler metadataHandler = this.getArtifactHandler(metadataUrl);
+
+                    final String contents = getFileContents(metadataHandler);
+
+                    final String latestVersion = getLatestSnapshot(contents);
+                    if ( latestVersion != null ) {
+                        final String name = artifactUrl.substring(lastSlash); // includes slash
+                        final String fullURL = artifactUrl.substring(0, lastSlash) + name.replace("SNAPSHOT", latestVersion);
+                        int pos2 = fullURL.indexOf(":/") + 2;
+                        while ( fullURL.charAt(pos2) == '/') {
+                            pos2++;
+                        }
+                        final File file2 = this.getArtifactFromProviders(fullURL, path);
+                        if ( file2 == null || !file2.exists()) {
+                            throw new IOException("Artifact " + fullURL + " not found.");
+                        }
+                        return new ArtifactHandler(artifactUrl, file2);
+                    }
+                } catch ( final IOException ignore ) {
+                    // we ignore this but report the original 404
+                }
+            }
+        }
+
+        throw new IOException("Artifact " + url + " not found in any repository.");
+    }
+
+    protected String getFileContents(final ArtifactHandler handler) throws IOException {
+        final StringBuilder sb = new StringBuilder();
+        for(final String line : Files.readAllLines(handler.getArtifact().toPath())) {
+            sb.append(line).append('\n');
+        }
+
+        return sb.toString();
+    }
+
+    public static String getValue(final String xml, final String[] xpath) {
+        String value = null;
+        int pos = 0;
+        for(final String name : xpath) {
+            final String element = '<' + name + '>';
+
+            pos = xml.indexOf(element, pos);
+            if ( pos == -1 ) {
+                final String elementWithAttributes = '<' + name + ' ';
+                pos = xml.indexOf(elementWithAttributes, pos);
+                if ( pos == -1 ) {
+                    break;
+                }
+            }
+            pos = xml.indexOf('>', pos) + 1;
+        }
+        if ( pos != -1 ) {
+            final int endPos = xml.indexOf("</", pos);
+            if ( endPos != -1 ) {
+                value = xml.substring(pos, endPos).trim();
+            }
+        }
+        return value;
+    }
+    public static String getLatestSnapshot(final String mavenMetadata) {
+        final String timestamp = getValue(mavenMetadata, new String[] {"metadata", "versioning", "snapshot", "timestamp"});
+        final String buildNumber = getValue(mavenMetadata, new String[] {"metadata", "versioning", "snapshot", "buildNumber"});
+
+        if ( timestamp != null && buildNumber != null ) {
+            return timestamp + '-' + buildNumber;
+        }
+
+        return null;
+    }
+
+    private static final class DefaultArtifactHandler implements ArtifactProvider {
+
+        private volatile File cacheDir;
+
+        private volatile ArtifactProviderContext config;
+
+        @Override
+        public String getProtocol() {
+            return "*";
+        }
+
+        @Override
+        public void init(final ArtifactProviderContext config) throws IOException {
+            this.cacheDir = config.getCacheDirectory();
+            this.config = config;
+        }
+
+        @Override
+        public void shutdown() {
+            this.config = null;
+            this.cacheDir = null;
+        }
+
+        @Override
+        public File getArtifact(final String url, final String relativeCachePath) {
+            Logger.LOG.debug("Checking url to be local file " + url);
+            // check if this is already a local file
+            try {
+                final File f = new File(new URL(url).toURI());
+                if ( f.exists() ) {
+                    this.config.incLocalArtifacts();
+                    return f;
+                }
+                return null;
+            } catch ( final URISyntaxException ise) {
+                // ignore
+            } catch ( final IllegalArgumentException iae) {
+                // ignore
+            } catch ( final MalformedURLException mue) {
+                // ignore
+            }
+            Logger.LOG.debug("Checking remote url " + url);
+            try {
+                // check for url
+                if ( url.indexOf(":") == -1 ) {
+                    return null;
+                }
+
+                final String filePath = (this.cacheDir.getAbsolutePath() + File.separatorChar + relativeCachePath).replace('/', File.separatorChar);
+                final File cacheFile = new File(filePath);
+
+                if ( !cacheFile.exists() ) {
+                    cacheFile.getParentFile().mkdirs();
+                    final URL u = new URL(url);
+                    final URLConnection con = u.openConnection();
+                    con.connect();
+
+                    final InputStream readIS = con.getInputStream();
+                    final byte[] buffer = new byte[32768];
+                    int l;
+                    OutputStream os = null;
+                    try {
+                        os = new FileOutputStream(cacheFile);
+                        while ( (l = readIS.read(buffer)) >= 0 ) {
+                            os.write(buffer, 0, l);
+                        }
+                    } finally {
+                        try {
+                            readIS.close();
+                        } catch ( final IOException ignore) {
+                            // ignore
+                        }
+                        if ( os != null ) {
+                            try {
+                                os.close();
+                            } catch ( final IOException ignore ) {
+                                // ignore
+
+                            }
+                        }
+                    }
+                    this.config.incDownloadedArtifacts();
+                } else {
+                    this.config.incCachedArtifacts();
+                }
+                return cacheFile;
+            } catch ( final Exception e) {
+                e.printStackTrace();
+                // ignore for now
+                return null;
+            }
+        }
+
+        @Override
+        public String toString() {
+            return "DefaultArtifactHandler";
+        }
+    }
+}

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactManager.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/artifacts/ArtifactManager.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java (added)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java Wed May 31 12:29:12 2017
@@ -0,0 +1,278 @@
+/*
+ * 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.feature.launcher.impl.launchers;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Array;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Dictionary;
+import java.util.Hashtable;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.feature.launcher.api.Logger;
+import org.osgi.framework.Bundle;
+import org.osgi.framework.BundleContext;
+import org.osgi.framework.BundleException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceReference;
+import org.osgi.framework.launch.Framework;
+import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.wiring.FrameworkWiring;
+import org.osgi.util.tracker.ServiceTracker;
+import org.osgi.util.tracker.ServiceTrackerCustomizer;
+
+/**
+ * Common functionality for the framework start.
+ */
+public class AbstractRunner {
+
+    private volatile ServiceTracker<Object, Object> configAdminTracker;
+
+    private volatile ServiceTracker<Object, Object> installerTracker;
+
+    private final List<Object[]> configurations;
+
+    private final List<File> installables;
+
+    public AbstractRunner(final List<Object[]> configurations, final List<File> installables) {
+        this.configurations = new ArrayList<>(configurations);
+        this.installables = installables;
+    }
+
+    protected void setupFramework(final Framework framework, final Map<Integer, List<File>> bundlesMap)
+    throws BundleException {
+        if ( !configurations.isEmpty() ) {
+            this.configAdminTracker = new ServiceTracker<>(framework.getBundleContext(),
+                    "org.osgi.service.cm.ConfigurationAdmin",
+                    new ServiceTrackerCustomizer<Object, Object>() {
+
+                        @Override
+                        public Object addingService(final ServiceReference<Object> reference) {
+                            // get config admin
+                            final Object cm = framework.getBundleContext().getService(reference);
+                            if ( cm != null ) {
+                                try {
+                                    configure(cm);
+                                } finally {
+                                    framework.getBundleContext().ungetService(reference);
+                                }
+                            }
+                            return null;
+                        }
+
+                        @Override
+                        public void modifiedService(ServiceReference<Object> reference, Object service) {
+                            // nothing to do
+                        }
+
+                        @Override
+                        public void removedService(ServiceReference<Object> reference, Object service) {
+                            // nothing to do
+                        }
+            });
+            this.configAdminTracker.open();
+        }
+        if ( installables != null && !installables.isEmpty() ) {
+            this.installerTracker = new ServiceTracker<>(framework.getBundleContext(),
+                    "org.apache.sling.installer.api.OsgiInstaller",
+                    new ServiceTrackerCustomizer<Object, Object>() {
+
+                        @Override
+                        public Object addingService(final ServiceReference<Object> reference) {
+                            // get installer
+                            final Object installer = framework.getBundleContext().getService(reference);
+                            if ( installer != null ) {
+                                try {
+                                    install(installer);
+                                } finally {
+                                    framework.getBundleContext().ungetService(reference);
+                                }
+                            }
+                            return null;
+                        }
+
+                        @Override
+                        public void modifiedService(ServiceReference<Object> reference, Object service) {
+                            // nothing to do
+                        }
+
+                        @Override
+                        public void removedService(ServiceReference<Object> reference, Object service) {
+                            // nothing to do
+                        }
+            });
+            this.installerTracker.open();
+        }
+        try {
+            this.install(framework, bundlesMap);
+        } catch ( final IOException ioe) {
+            throw new BundleException("Unable to install bundles.", ioe);
+        }
+    }
+
+    private void configure(final Object configAdmin) {
+        try {
+            final Method createConfig = configAdmin.getClass().getDeclaredMethod("getConfiguration", String.class, String.class);
+            final Method createFactoryConfig = configAdmin.getClass().getDeclaredMethod("getFactoryConfiguration", String.class, String.class, String.class);
+
+            Method updateMethod = null;
+            for(final Object[] obj : this.configurations) {
+                final Object cfg;
+                if ( obj[1] != null ) {
+                    cfg = createFactoryConfig.invoke(configAdmin, obj[1], obj[0], null);
+                } else {
+                    cfg = createConfig.invoke(configAdmin, obj[0], null);
+                }
+                if ( updateMethod == null ) {
+                    updateMethod = cfg.getClass().getDeclaredMethod("update", Dictionary.class);
+                }
+                updateMethod.invoke(cfg, obj[2]);
+            }
+        } catch ( final Exception e) {
+            Logger.LOG.error("Unable to create configurations", e);
+            throw new RuntimeException(e);
+        }
+        final Thread t = new Thread(() -> { configAdminTracker.close(); configAdminTracker = null; });
+        t.setDaemon(false);
+        t.start();
+        this.configurations.clear();
+    }
+
+    private boolean isSystemBundleFragment(final Bundle installedBundle) {
+        final String fragmentHeader = getFragmentHostHeader(installedBundle);
+        return fragmentHeader != null
+            && fragmentHeader.indexOf(Constants.EXTENSION_DIRECTIVE) > 0;
+    }
+
+    /**
+     * Gets the bundle's Fragment-Host header.
+     */
+    private String getFragmentHostHeader(final Bundle b) {
+        return b.getHeaders().get( Constants.FRAGMENT_HOST );
+    }
+
+    /**
+     * Install the bundles
+     * @param bundleMap The map with the bundles indexed by start level
+     * @throws IOException, BundleException If anything goes wrong.
+     */
+    private void install(final Framework framework, final Map<Integer, List<File>> bundleMap)
+    throws IOException, BundleException {
+        final BundleContext bc = framework.getBundleContext();
+        int defaultStartLevel = getProperty(bc, "felix.startlevel.bundle", 1);
+        for(final Integer startLevel : sortStartLevels(bundleMap.keySet(), defaultStartLevel)) {
+            Logger.LOG.debug("Installing bundles with start level {0}", startLevel);
+
+            for(final File file : bundleMap.get(startLevel)) {
+                Logger.LOG.debug("- {0}", file.getName());
+
+                // use reference protocol. This avoids copying the binary to the cache directory
+                // of the framework
+                final Bundle bundle = bc.installBundle("reference:" + file.toURI().toURL(), null);
+                if ( startLevel > 0 ) {
+                    bundle.adapt(BundleStartLevel.class).setStartLevel(startLevel);
+                }
+
+                // fragment?
+                if ( !isSystemBundleFragment(bundle) ) {
+                    final String fragmentHostHeader = getFragmentHostHeader(bundle);
+                    if (fragmentHostHeader != null) {
+                        for (final Bundle b : bc.getBundles()) {
+                            if (fragmentHostHeader.equals(b.getSymbolicName())) {
+                                final FrameworkWiring fw = framework.adapt(FrameworkWiring.class);
+                                fw.refreshBundles(Collections.singleton(b));
+                                break;
+                            }
+                        }
+
+                    } else {
+                        bundle.start();
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Sort the start levels in the ascending order. The only exception is the start level
+     * "0", which should be put at the position configured in {@code felix.startlevel.bundle}.
+     *
+     * @param startLevels integer start levels
+     * @return sorted start levels
+     */
+    private static Iterable<Integer> sortStartLevels(final Collection<Integer> startLevels, final int defaultStartLevel) {
+        final List<Integer> result = new ArrayList<>(startLevels);
+        Collections.sort(result, (o1, o2) -> {
+            int i1 = o1 == 0 ? defaultStartLevel : o1;
+            int i2 = o2 == 0 ? defaultStartLevel : o2;
+            return Integer.compare(i1, i2);
+        });
+        return result;
+    }
+
+    private static int getProperty(BundleContext bc, String propName, int defaultValue) {
+        String val = bc.getProperty(propName);
+        if (val == null) {
+            return defaultValue;
+        } else {
+            return Integer.parseInt(val);
+        }
+    }
+
+    private void install(final Object installer) {
+        try {
+            final Class<?> installableResourceClass = installer.getClass().getClassLoader().loadClass("org.apache.sling.installer.api.InstallableResource");
+            final Object resources = Array.newInstance(installableResourceClass, this.installables.size());
+            final Method registerResources = installer.getClass().getDeclaredMethod("registerResources", String.class, resources.getClass());
+            final Constructor<?> constructor = installableResourceClass.getDeclaredConstructor(String.class,
+                    InputStream.class,
+                    Dictionary.class,
+                    String.class,
+                    String.class,
+                    Integer.class);
+
+            for(int i=0; i<this.installables.size();i++) {
+                final File f = this.installables.get(i);
+                final Dictionary<String, Object> dict = new Hashtable<>();
+                dict.put("resource.uri.hint", f.toURI().toString());
+                final Object rsrc = constructor.newInstance(f.getAbsolutePath(),
+                        new FileInputStream(f),
+                        dict,
+                        f.getName(),
+                        "file",
+                        null);
+                Array.set(resources, i, rsrc);
+            }
+            registerResources.invoke(installer, "cloudlauncher", resources);
+        } catch ( final Exception e) {
+            Logger.LOG.error("Unable to contact installer and install additional artifacts", e);
+            throw new RuntimeException(e);
+        }
+        final Thread t = new Thread(() -> { installerTracker.close(); installerTracker = null; });
+        t.setDaemon(false);
+        t.start();
+        this.installables.clear();
+    }
+}

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url

Added: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java
URL: http://svn.apache.org/viewvc/sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java?rev=1797026&view=auto
==============================================================================
--- sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java (added)
+++ sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java Wed May 31 12:29:12 2017
@@ -0,0 +1,75 @@
+/*
+ * 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.feature.launcher.impl.launchers;
+
+import java.lang.reflect.Constructor;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.sling.feature.Application;
+import org.apache.sling.feature.Extension;
+import org.apache.sling.feature.ExtensionType;
+import org.apache.sling.feature.launcher.spi.Launcher;
+import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
+import org.apache.sling.feature.launcher.spi.LauncherRunContext;
+
+/**
+ * Launcher directly using the OSGi launcher API.
+ */
+public class FrameworkLauncher implements Launcher {
+
+    private static final String EXT_NAME = "framework";
+
+    @Override
+    public void prepare(final LauncherPrepareContext context, final Application app) throws Exception {
+        // search for framework extension
+        Extension fwk = null;
+        for(final Extension e : app.getExtensions()) {
+            if ( e.getName().equals(EXT_NAME) ) {
+                fwk = e;
+            }
+        }
+        if ( fwk == null ) {
+            throw new Exception("No " + EXT_NAME + " extension for framework found.");
+        }
+        if ( fwk.getType() != ExtensionType.ARTIFACTS ) {
+            throw new Exception("Extension " + EXT_NAME + " is of wrong type: " + fwk.getType());
+        }
+        if ( fwk.getArtifacts().size() != 1 ) {
+            throw new Exception("Extension " + EXT_NAME + " must have exactly one artifact: " + fwk.getArtifacts().size());
+        }
+        // add the framework to the classpath
+        context.addAppJar(context.getArtifactFile(fwk.getArtifacts().get(0).getId()));
+    }
+
+    /**
+     * Run the launcher
+     * @throws If anything goes wrong
+     */
+    @Override
+    public void run(final LauncherRunContext context, final ClassLoader cl) throws Exception {
+        final Class<?> runnerClass = cl.loadClass(this.getClass().getPackage().getName() + ".FrameworkRunner");
+        final Constructor<?> constructor = runnerClass.getDeclaredConstructor(Map.class, Map.class, List.class, List.class);
+        constructor.setAccessible(true);
+        constructor.newInstance(context.getFrameworkProperties(),
+                context.getBundleMap(),
+                context.getConfigurations(),
+                context.getInstallableArtifacts());
+
+        // nothing else to do, constructor starts everything
+    }
+}

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: sling/whiteboard/cziegeler/feature-launcher/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java
------------------------------------------------------------------------------
    svn:keywords = author date id revision rev url



Mime
View raw message