SLING-5656 Added LauncherListener to crankstart to be able to listen to progress of launch

git-svn-id: https://svn.apache.org/repos/asf/sling/trunk@1738559 13f79535-47bb-0310-9956-ffa450edef68
diff --git a/src/main/java/org/apache/sling/crankstart/junit/CrankstartSetup.java b/src/main/java/org/apache/sling/crankstart/junit/CrankstartSetup.java
index 6e6d02c..c8be85d 100644
--- a/src/main/java/org/apache/sling/crankstart/junit/CrankstartSetup.java
+++ b/src/main/java/org/apache/sling/crankstart/junit/CrankstartSetup.java
@@ -18,6 +18,7 @@
 import org.apache.http.client.methods.HttpUriRequest;
 import org.apache.http.impl.client.DefaultHttpClient;
 import org.apache.sling.crankstart.launcher.Launcher;
+import org.apache.sling.crankstart.launcher.LauncherListener;
 import org.apache.sling.crankstart.launcher.PropertiesVariableResolver;
 import org.apache.sling.provisioning.model.ModelUtility.VariableResolver;
 import org.junit.rules.ExternalResource;
@@ -42,8 +43,12 @@
     private VariableResolver variablesResolver = new PropertiesVariableResolver(replacementProps, Launcher.VARIABLE_OVERRIDE_PREFIX);
     
     private String [] classpathModelPaths;
-    
-    
+    private boolean shutdown;
+    private int bundlesStarted;
+    private int bundlesFailed;
+    private int totalBundles;
+
+
     @Override
     public String toString() {
         return getClass().getSimpleName() + ", port " + port + ", OSGi storage " + storagePath;
@@ -64,7 +69,8 @@
             }
         }
     }
-    
+
+
     public CrankstartSetup withModelResources(String ... classpathModelPaths) {
         this.classpathModelPaths = classpathModelPaths;
         return this;
@@ -103,6 +109,22 @@
     public String getBaseUrl() {
         return baseUrl;
     }
+
+    public int getBundlesFailed() {
+        return bundlesFailed;
+    }
+
+    public int getBundlesStarted() {
+        return bundlesStarted;
+    }
+
+    public int getTotalBundles() {
+        return totalBundles;
+    }
+
+    public boolean isShutdownComplete() {
+        return shutdown;
+    }
     
     private static void cleanup() {
         synchronized (toCleanup) {
@@ -138,8 +160,23 @@
             fail("Expecting connection to " + port + " to fail before starting HTTP service");
         } catch(IOException expected) {
         }
-        
-        final Launcher launcher = new Launcher().withVariableResolver(variablesResolver);
+        shutdown = false;
+        bundlesStarted = 0;
+        bundlesFailed = 0;
+        totalBundles = 0;
+        final Launcher launcher = new Launcher().withVariableResolver(variablesResolver).withListener(new LauncherListener() {
+            @Override
+            public void onStartup(int started, int failed, int totalBundles) {
+                CrankstartSetup.this.bundlesStarted = started;
+                CrankstartSetup.this.bundlesFailed = failed;
+                CrankstartSetup.this.totalBundles = totalBundles;
+            }
+
+            @Override
+            public void onShutdown() {
+                shutdown = true;
+            }
+        });
         for(String path : classpathModelPaths) {
             mergeModelResource(launcher, path);
         }
diff --git a/src/main/java/org/apache/sling/crankstart/launcher/FrameworkSetup.java b/src/main/java/org/apache/sling/crankstart/launcher/FrameworkSetup.java
index cb1f3b7..2d52b2e 100644
--- a/src/main/java/org/apache/sling/crankstart/launcher/FrameworkSetup.java
+++ b/src/main/java/org/apache/sling/crankstart/launcher/FrameworkSetup.java
@@ -50,6 +50,7 @@
     
     public Object call() throws Exception {
         final Model model = require(Launcher.MODEL_KEY, Model.class);
+        final LauncherListener listener = (LauncherListener) get(Launcher.LISTENER_KEY);
         
         log.info("Setting OSGi framework properties");
         final Map<String, String> fprops = new FrameworkProperties(model).getProperties(null);
@@ -98,8 +99,14 @@
         }
         
         log.info("OSGi setup done, waiting for framework to stop");
+        if ( listener != null) {
+            listener.onStartup(started, failed, bundles.length);
+        }
         framework.waitForStop(0);
-        
+        if ( listener != null) {
+            listener.onShutdown();
+        }
+
         return null;
     }
     
diff --git a/src/main/java/org/apache/sling/crankstart/launcher/Launcher.java b/src/main/java/org/apache/sling/crankstart/launcher/Launcher.java
index f68451f..0abbbe8 100644
--- a/src/main/java/org/apache/sling/crankstart/launcher/Launcher.java
+++ b/src/main/java/org/apache/sling/crankstart/launcher/Launcher.java
@@ -51,6 +51,7 @@
     
     public static final String CRANKSTART_FEATURE = ":crankstart";
     public static final String MODEL_KEY = "model";
+    public static final String LISTENER_KEY = "listener";
     public static final String FRAMEWORK_KEY = "framework";
     
     public static final String VARIABLE_OVERRIDE_PREFIX = "crankstart.model.";
@@ -80,7 +81,8 @@
             return !Launcher.CRANKSTART_FEATURE.equals(f.getName());
         }
     };
-    
+    private LauncherListener listener;
+
     public Launcher(String ... args) throws Exception {
         MavenResolver.setup();
         withVariableResolver(null);
@@ -95,6 +97,11 @@
         return this;
     }
 
+    public Launcher withListener(LauncherListener listener) {
+        this.listener = listener;
+        return this;
+    }
+
     /** Add models from the supplied paths, can be either files or folders */ 
     public Launcher withModelPaths(String ... paths) throws Exception {
         // Find all files to read and sort the list, to be deterministic
@@ -158,6 +165,7 @@
         final Callable<?> c = (Callable<?>) getClass().getClassLoader().loadClass("org.apache.sling.crankstart.launcher.FrameworkSetup").newInstance();
         @SuppressWarnings("unchecked") final Map<String, Object> cmap = (Map<String, Object>)c; 
         cmap.put(MODEL_KEY, model);
+        cmap.put(LISTENER_KEY, listener);
         c.call();
     }
     
diff --git a/src/main/java/org/apache/sling/crankstart/launcher/LauncherListener.java b/src/main/java/org/apache/sling/crankstart/launcher/LauncherListener.java
new file mode 100644
index 0000000..055ebb6
--- /dev/null
+++ b/src/main/java/org/apache/sling/crankstart/launcher/LauncherListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.crankstart.launcher;
+
+/**
+ * Listens to startup information from the launcher thread.
+ */
+public interface LauncherListener {
+
+    /**
+     * Called when the launcher has finished loading all initial bundles reporting those that started and those that failed.
+     * @param started the number started.
+     * @param failed the number that failed.
+     * @param length the total number of bundles.
+     */
+    void onStartup(int started, int failed, int length);
+
+    /**
+     * Called when the launcher thread begins to perform shutdown.
+     */
+    void onShutdown();
+}