You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by pa...@apache.org on 2018/06/21 11:26:11 UTC

[sling-org-apache-sling-feature-launcher] branch master updated: Rework the framework launching to take into account restart requests, add the launchpad.api, and take into account incremental install flag.

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 2994b09  Rework the framework launching to take into account restart requests, add the launchpad.api, and take into account incremental install flag.
2994b09 is described below

commit 2994b09538d18c4ffbeb71949037d6455b567c2b
Author: Karl Pauls <ka...@gmail.com>
AuthorDate: Thu Jun 21 13:26:02 2018 +0200

    Rework the framework launching to take into account restart requests, add the launchpad.api, and take into account incremental install flag.
---
 pom.xml                                            |   6 ++
 .../apache/sling/feature/launcher/impl/Main.java   |   7 +-
 .../launcher/impl/launchers/AbstractRunner.java    | 112 ++++++++++++++++++++-
 .../launcher/impl/launchers/FrameworkLauncher.java |  27 +++--
 .../launcher/impl/launchers/FrameworkRunner.java   |  30 +++++-
 .../sling/feature/launcher/spi/Launcher.java       |   2 +-
 6 files changed, 167 insertions(+), 17 deletions(-)

diff --git a/pom.xml b/pom.xml
index 9e0db4e..9c5aabf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -131,6 +131,12 @@
             <scope>provided</scope>
         </dependency>
         <dependency>
+            <groupId>org.apache.sling</groupId>
+            <artifactId>org.apache.sling.launchpad.api</artifactId>
+            <version>1.2.0</version>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
             <groupId>org.apache.felix</groupId>
             <artifactId>org.apache.felix.converter</artifactId>
             <version>1.0.0</version>
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 39d6f40..8b990c5 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
@@ -38,6 +38,7 @@ import org.apache.sling.feature.io.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;
+import org.osgi.framework.FrameworkEvent;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
@@ -233,9 +234,9 @@ public class Main {
         }
 
         final Launcher launcher = new FrameworkLauncher();
-        launcher.run(installation, createClassLoader(installation));
-
-        config.clear();
+        while (launcher.run(installation, createClassLoader(installation)) == FrameworkEvent.STOPPED_SYSTEM_REFRESHED) {
+            Main.LOG().info("Framework restart due to extension refresh");
+        }
     }
 
     /**
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java
index 466297b..00ec9cb 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/AbstractRunner.java
@@ -17,13 +17,18 @@
 package org.apache.sling.feature.launcher.impl.launchers;
 
 import org.apache.sling.feature.launcher.impl.Main;
+import org.apache.sling.launchpad.api.StartupHandler;
+import org.apache.sling.launchpad.api.StartupMode;
 import org.osgi.framework.Bundle;
 import org.osgi.framework.BundleContext;
 import org.osgi.framework.BundleException;
 import org.osgi.framework.Constants;
+import org.osgi.framework.FrameworkEvent;
+import org.osgi.framework.FrameworkListener;
 import org.osgi.framework.ServiceReference;
 import org.osgi.framework.launch.Framework;
 import org.osgi.framework.startlevel.BundleStartLevel;
+import org.osgi.framework.startlevel.FrameworkStartLevel;
 import org.osgi.util.tracker.ServiceTracker;
 import org.osgi.util.tracker.ServiceTrackerCustomizer;
 
@@ -41,11 +46,15 @@ import java.util.Dictionary;
 import java.util.Hashtable;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Callable;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Common functionality for the framework start.
  */
-public class AbstractRunner {
+public abstract class AbstractRunner implements Callable<Integer> {
 
     private volatile ServiceTracker<Object, Object> configAdminTracker;
 
@@ -55,9 +64,22 @@ public class AbstractRunner {
 
     private final List<File> installables;
 
-    public AbstractRunner(final List<Object[]> configurations, final List<File> installables) {
+    private final int targetStartlevel;
+
+    private final AtomicInteger waitRequested = new AtomicInteger(0);
+
+    public AbstractRunner(final Map<String, String> frameworkProperties, final List<Object[]> configurations, final List<File> installables) {
         this.configurations = new ArrayList<>(configurations);
         this.installables = installables;
+        String target = frameworkProperties.get(Constants.FRAMEWORK_BEGINNING_STARTLEVEL);
+        if (target != null) {
+            targetStartlevel = Integer.parseInt(target);
+        } else {
+            targetStartlevel = 1;
+        }
+        if ("true".equalsIgnoreCase(frameworkProperties.get("sling.framework.install.incremental"))) {
+            frameworkProperties.put(Constants.FRAMEWORK_BEGINNING_STARTLEVEL, "1");
+        }
     }
 
     protected void setupFramework(final Framework framework, final Map<Integer, List<File>> bundlesMap)
@@ -124,11 +146,97 @@ public class AbstractRunner {
             });
             this.installerTracker.open();
         }
+
+        int bundleCount = framework.getBundleContext().getBundles().length;
+
         try {
             this.install(framework, bundlesMap);
         } catch ( final IOException ioe) {
             throw new BundleException("Unable to install bundles.", ioe);
         }
+
+        try
+        {
+            // TODO: double check bundles and take installables into account
+            final StartupMode mode = framework.getBundleContext().getBundles().length == bundleCount ? StartupMode.RESTART :
+                bundleCount > 1 ? StartupMode.UPDATE : StartupMode.INSTALL;
+
+            framework.getBundleContext().registerService(StartupHandler.class, new StartupHandler()
+            {
+                @Override
+                public StartupMode getMode()
+                {
+                    return mode;
+                }
+
+                @Override
+                public boolean isFinished() {
+                    return framework.getState() == Framework.ACTIVE && targetStartlevel > framework.adapt(FrameworkStartLevel.class).getStartLevel();
+                }
+
+                @Override
+                public void waitWithStartup(boolean b) {
+                    if (b) {
+                        waitRequested.incrementAndGet();
+                    }
+                    else {
+                        waitRequested.decrementAndGet();
+                    }
+                }
+            }, null);
+        } catch (NoClassDefFoundError ex) {
+            // Ignore, we don't have the launchpad.api
+        }
+    }
+
+    protected boolean startFramework(final Framework framework, long timeout, TimeUnit unit) throws BundleException, InterruptedException
+    {
+        CountDownLatch latch = new CountDownLatch(1);
+        FrameworkListener listener = new FrameworkListener()
+        {
+            @Override
+            public void frameworkEvent(FrameworkEvent frameworkEvent)
+            {
+                if (frameworkEvent.getType() == FrameworkEvent.STARTED || frameworkEvent.getType() == FrameworkEvent.STARTLEVEL_CHANGED) {
+                    if (framework.getState() == Framework.ACTIVE && targetStartlevel > framework.adapt(FrameworkStartLevel.class).getStartLevel())
+                    {
+                        if (waitRequested.get() == 0) {
+                            try {
+                                Thread.sleep(2000);
+                            } catch (InterruptedException e)
+                            {
+                                e.printStackTrace();
+                            }
+                        }
+                        while (waitRequested.get() > 0) {
+                            try {
+                                Thread.sleep(500);
+                            }  catch (InterruptedException e) {
+                                e.printStackTrace();
+                            }
+                        }
+                        try {
+                            framework.adapt(FrameworkStartLevel.class).setStartLevel(framework.adapt(FrameworkStartLevel.class).getStartLevel() + 1);
+                        } catch (Exception ex) {
+                            latch.countDown();
+                        }
+                    }
+                    else {
+                        latch.countDown();
+                    }
+                }
+            }
+        };
+
+        framework.getBundleContext().addFrameworkListener(listener);
+
+        framework.start();
+
+        try {
+            return latch.await(timeout, unit);
+        } finally {
+            framework.getBundleContext().removeFrameworkListener(listener);
+        }
     }
 
     private void configure(final Object configAdmin) {
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java
index 12a5c66..d34221f 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkLauncher.java
@@ -19,16 +19,20 @@ package org.apache.sling.feature.launcher.impl.launchers;
 import org.apache.commons.lang.text.StrLookup;
 import org.apache.commons.lang.text.StrSubstitutor;
 import org.apache.sling.feature.Application;
+import org.apache.sling.feature.Artifact;
+import org.apache.sling.feature.ArtifactId;
 import org.apache.sling.feature.launcher.impl.Main;
 import org.apache.sling.feature.launcher.spi.Launcher;
 import org.apache.sling.feature.launcher.spi.LauncherPrepareContext;
 import org.apache.sling.feature.launcher.spi.LauncherRunContext;
+import org.osgi.framework.Constants;
 
 import java.io.File;
 import java.lang.reflect.Constructor;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.Callable;
 
 /**
  * Launcher directly using the OSGi launcher API.
@@ -39,14 +43,23 @@ public class FrameworkLauncher implements Launcher {
     @Override
     public void prepare(final LauncherPrepareContext context, final Application app) throws Exception {
         context.addAppJar(context.getArtifactFile(app.getFramework()));
+        ArtifactId api = ArtifactId.fromMvnId("org.apache.sling:org.apache.sling.launchpad.api:1.2.0");
+        Artifact artifact = app.getBundles().getSame(api);
+        if (artifact != null)
+        {
+            api = artifact.getId();
+            context.addAppJar(context.getArtifactFile(api));
+            app.getBundles().removeExact(api);
+            app.getFrameworkProperties().put(Constants.FRAMEWORK_SYSTEMPACKAGES_EXTRA, "org.apache.sling.launchpad.api;version=\"" + api.getOSGiVersion() + "\"");
+        }
     }
 
     /**
      * Run the launcher
-     * @throws If anything goes wrong
+     * @throws Exception If anything goes wrong
      */
     @Override
-    public void run(final LauncherRunContext context, final ClassLoader cl) throws Exception {
+    public int run(final LauncherRunContext context, final ClassLoader cl) throws Exception {
         StrSubstitutor ss = new StrSubstitutor(new StrLookup() {
             @Override
             public String lookup(String key) {
@@ -62,7 +75,7 @@ public class FrameworkLauncher implements Launcher {
 
         Map<String, String> properties = new HashMap<>();
         context.getFrameworkProperties().forEach((key, value) -> {
-            properties.put(key, ss.replace(value));
+            properties.put(key, ss.replace(value).replace("{dollar}", "$"));
         });
         if ( Main.LOG().isDebugEnabled() ) {
             Main.LOG().debug("Bundles:");
@@ -86,18 +99,16 @@ public class FrameworkLauncher implements Launcher {
             }
             Main.LOG().debug("");
         }
-        long time = System.currentTimeMillis();
 
-        final Class<?> runnerClass = cl.loadClass(this.getClass().getPackage().getName() + ".FrameworkRunner");
+        final Class<?> runnerClass = cl.loadClass(FrameworkRunner.class.getName());
         final Constructor<?> constructor = runnerClass.getDeclaredConstructor(Map.class, Map.class, List.class, List.class);
         constructor.setAccessible(true);
-        constructor.newInstance(properties,
+        Callable<Integer> restart = (Callable<Integer>) constructor.newInstance(properties,
                 context.getBundleMap(),
                 context.getConfigurations(),
                 context.getInstallableArtifacts());
 
-        Main.LOG().debug("Startup took: " + (System.currentTimeMillis() - time));
+        return restart.call();
         // nothing else to do, constructor starts everything
-        // TODO: wait for stop and restart framework when necessary
     }
 }
diff --git a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkRunner.java b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkRunner.java
index f17b6c3..25ce117 100644
--- a/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkRunner.java
+++ b/src/main/java/org/apache/sling/feature/launcher/impl/launchers/FrameworkRunner.java
@@ -26,17 +26,21 @@ import java.io.File;
 import java.util.List;
 import java.util.Map;
 import java.util.ServiceLoader;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.TimeoutException;
 
 /**
  * Launcher directly using the OSGi launcher API.
  */
 public class FrameworkRunner extends AbstractRunner {
 
+    private volatile int type = -1;
+
     public FrameworkRunner(final Map<String, String> frameworkProperties,
             final Map<Integer, List<File>> bundlesMap,
             final List<Object[]> configurations,
             final List<File> installables) throws Exception {
-        super(configurations, installables);
+        super(frameworkProperties, configurations, installables);
 
         final ServiceLoader<FrameworkFactory> loader = ServiceLoader.load(FrameworkFactory.class);
         FrameworkFactory factory = null;
@@ -58,7 +62,7 @@ public class FrameworkRunner extends AbstractRunner {
             public void run() {
                 try {
                     framework.stop();
-                    FrameworkEvent waitForStop = framework.waitForStop(10000);
+                    FrameworkEvent waitForStop = framework.waitForStop(60 * 1000);
                     if (waitForStop.getType() != FrameworkEvent.STOPPED)
                     {
                         Main.LOG().warn("Framework stopped with: " + waitForStop.getType(), waitForStop.getThrowable());
@@ -76,7 +80,27 @@ public class FrameworkRunner extends AbstractRunner {
 
         this.setupFramework(framework, bundlesMap);
 
+
+        long time = System.currentTimeMillis();
+
         // finally start
-        framework.start();
+        if (!this.startFramework(framework, 10, TimeUnit.MINUTES)) {
+            throw new TimeoutException("Waited for more than 10 minutes to startup framework.");
+        }
+
+        Main.LOG().debug("Startup took: " + (System.currentTimeMillis() - time));
+
+        while ((type = framework.waitForStop(Long.MAX_VALUE).getType()) == FrameworkEvent.STOPPED_UPDATE) {
+            Main.LOG().info("Framework restart due to update");
+            time = System.currentTimeMillis();
+            if (!this.startFramework(framework, 10, TimeUnit.MINUTES)) {
+                throw new TimeoutException("Waited for more than 10 minutes to startup framework.");
+            }
+            Main.LOG().debug("Restart took: " + (System.currentTimeMillis() - time));
+        }
+    }
+
+    public Integer call() {
+        return type;
     }
 }
diff --git a/src/main/java/org/apache/sling/feature/launcher/spi/Launcher.java b/src/main/java/org/apache/sling/feature/launcher/spi/Launcher.java
index a1ced11..26819c4 100644
--- a/src/main/java/org/apache/sling/feature/launcher/spi/Launcher.java
+++ b/src/main/java/org/apache/sling/feature/launcher/spi/Launcher.java
@@ -22,5 +22,5 @@ public interface Launcher {
 
     void prepare(LauncherPrepareContext context, Application app) throws Exception;
 
-    void run(LauncherRunContext context, ClassLoader cl) throws Exception;
+    int run(LauncherRunContext context, ClassLoader cl) throws Exception;
 }