You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@sling.apache.org by an...@apache.org on 2019/12/27 23:57:36 UTC
[sling-org-apache-sling-launchpad-base] 01/01: Added support for
Feature Launcher and changed version to 3.0.0
This is an automated email from the ASF dual-hosted git repository.
andysch pushed a commit to branch feature/SLING-8955-Feature-Launcher-Support
in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-launchpad-base.git
commit e4fd8cb2df4da025e297bf0cf73233bb6e0ffc5d
Author: Andreas Schaefer <sc...@iMac.local>
AuthorDate: Fri Dec 27 15:56:52 2019 -0800
Added support for Feature Launcher and changed version to 3.0.0
---
pom.xml | 13 +-
.../launchpad/app/{Main.java => AbstractMain.java} | 172 ++--
.../sling/launchpad/app/ControlListener.java | 4 +-
.../apache/sling/launchpad/app/FeatureMain.java | 221 +++++
.../java/org/apache/sling/launchpad/app/Main.java | 954 +--------------------
.../org/apache/sling/launchpad/app/MainTest.java | 2 +-
6 files changed, 342 insertions(+), 1024 deletions(-)
diff --git a/pom.xml b/pom.xml
index bab110f..c342f60 100644
--- a/pom.xml
+++ b/pom.xml
@@ -32,7 +32,7 @@
This version is a concatenation of the framework version and a version for the launchpad base itself.
For a release only the qualifier (launchpad base version) is increased.
-->
- <version>6.0.2-2.6.37-SNAPSHOT</version>
+ <version>6.0.2-3.0.0-SNAPSHOT</version>
<packaging>bundle</packaging>
<name>Apache Sling Launchpad Base</name>
@@ -124,7 +124,7 @@
<goal>unpack-dependencies</goal>
</goals>
<configuration>
- <includeArtifactIds>org.apache.sling.commons.osgi</includeArtifactIds>
+ <includeArtifactIds>commons-io,org.apache.sling.launchpad.base,org.apache.sling.feature.extension.content,org.apache.sling.feature.launcher,org.apache.sling.commons.osgi</includeArtifactIds>
<excludeTransitive>true</excludeTransitive>
<outputDirectory>
${project.build.directory}/classes
@@ -160,7 +160,7 @@
<archive>
<manifest>
<mainClass>
- org.apache.sling.launchpad.app.Main
+ org.apache.sling.launchpad.app.FeatureMain
</mainClass>
<addDefaultImplementationEntries>
true
@@ -174,6 +174,7 @@
<include>org/apache/sling/commons/osgi/bundleversion/**</include>
<include>org/osgi/framework/Version*</include>
<include>META-INF/**</include>
+ <include>org/apache/sling/feature/**</include>
</includes>
</configuration>
</execution>
@@ -268,6 +269,12 @@
<scope>provided</scope>
</dependency>
<dependency>
+ <groupId>org.apache.sling</groupId>
+ <artifactId>org.apache.sling.feature.launcher</artifactId>
+ <version>1.1.0</version>
+ <scope>provided</scope>
+ </dependency>
+ <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
diff --git a/src/main/java/org/apache/sling/launchpad/app/Main.java b/src/main/java/org/apache/sling/launchpad/app/AbstractMain.java
similarity index 91%
copy from src/main/java/org/apache/sling/launchpad/app/Main.java
copy to src/main/java/org/apache/sling/launchpad/app/AbstractMain.java
index 312ba8f..e03cc3f 100644
--- a/src/main/java/org/apache/sling/launchpad/app/Main.java
+++ b/src/main/java/org/apache/sling/launchpad/app/AbstractMain.java
@@ -16,9 +16,17 @@
*/
package org.apache.sling.launchpad.app;
+import org.apache.sling.launchpad.base.shared.Launcher;
+import org.apache.sling.launchpad.base.shared.Loader;
+import org.apache.sling.launchpad.base.shared.Notifiable;
+import org.apache.sling.launchpad.base.shared.SharedConstants;
+
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.DateFormat;
@@ -28,11 +36,6 @@ import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
-import org.apache.sling.launchpad.base.shared.Launcher;
-import org.apache.sling.launchpad.base.shared.Loader;
-import org.apache.sling.launchpad.base.shared.Notifiable;
-import org.apache.sling.launchpad.base.shared.SharedConstants;
-
/**
* The <code>Main</code> is the externally visible Standalone Java Application
* launcher for Sling. Please refer to the full description <i>The Sling
@@ -47,7 +50,7 @@ import org.apache.sling.launchpad.base.shared.SharedConstants;
* @see <a href="http://sling.apache.org/site/the-sling-launchpad.html">The
* Sling Launchpad</a>
*/
-public class Main {
+public class AbstractMain {
// The name of the environment variable to consult to find out
// about sling.home
@@ -110,11 +113,12 @@ public class Main {
* @param args The command line arguments supplied when starting the Sling
* Launcher through the Java VM.
*/
- public static void main(final String[] args) {
+ public static void main(Class mainClass, final String[] args) {
final Map<String, String> rawArgs = parseCommandLine(args);
// support usage first
- if (doHelp(rawArgs)) {
+ boolean usedHelp = (boolean) callMethod(mainClass, "doHelp", Map.class, rawArgs);
+ if(usedHelp) {
System.exit(0);
}
@@ -123,8 +127,29 @@ public class Main {
System.exit(1);
}
- final Main main = new Main(props);
+ final AbstractMain main = (AbstractMain) callMethod(mainClass, null, Map.class, props);
+
+ launchMain(main);
+ }
+
+ private static Object callMethod(Class clazz, String methodName, Class argumentType, Object argument) {
+ try {
+ Object answer;
+ if (methodName == null || methodName.isEmpty()) {
+ Constructor constructor = clazz.getConstructor(argumentType);
+ answer = constructor.newInstance(argument);
+ } else {
+ Method method = clazz.getMethod(methodName, argumentType);
+ System.out.println("Method found: '" + method + "'");
+ answer = method.invoke(null, argument);
+ }
+ return answer;
+ } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException | InstantiationException e) {
+ throw new RuntimeException("Unexpected issue with reflection", e);
+ }
+ }
+ static void launchMain(AbstractMain main) {
// check for control commands
int rc = main.doControlAction();
if (rc >= 0) {
@@ -143,7 +168,7 @@ public class Main {
* property names as known to the OSGi Framework and its installed
* bundles.
*/
- private final Map<String, String> commandLineArgs;
+ protected final Map<String, String> commandLineArgs;
/**
* Whether to install the shutdown hook.
@@ -152,7 +177,7 @@ public class Main {
* @see #installShutdownHook(Map)
* @see #addShutdownHook()
*/
- private boolean installShutdownHook;
+ protected boolean installShutdownHook;
/**
* The shutdown hook installed into the Java VM after Sling has been
@@ -162,7 +187,7 @@ public class Main {
* @see #addShutdownHook()
* @see #removeShutdownHook()
*/
- private Thread shutdownHook;
+ protected Thread shutdownHook;
/**
* The absolute path to the home directory of the launched Sling
@@ -171,24 +196,24 @@ public class Main {
*
* @see #getSlingHome(Map)
*/
- private final String slingHome;
+ protected final String slingHome;
/**
* The {@link Loader} class used to create the Framework class loader and
* to launch the framework.
*/
- private Loader loader;
+ protected Loader loader;
/**
* The actual launcher accessed through the {@link #loader} to launch
* the OSGi Framework.
*/
- private Launcher sling;
+ protected Launcher sling;
/**
* Flag to indicate if Sling has already been started.
*/
- private boolean started = false;
+ protected boolean started = false;
/**
* Creates an instance of this main loader class. The provided arguments are
@@ -201,7 +226,7 @@ public class Main {
* this parameter is <code>null</code> and empty map without
* configuration is assumed.
*/
- protected Main(Map<String, String> args) {
+ protected AbstractMain(Map<String, String> args) {
this.commandLineArgs = (args == null)
? new HashMap<String, String>()
: args;
@@ -303,7 +328,7 @@ public class Main {
/**
* Terminates the VM which was started by calling the
- * {@link #main(String[])} method of this class using the
+ * {@link #main(Class, String[])} method of this class using the
* <code>status</code> value as the application exit status code.
* <p>
* This method does not return.
@@ -366,7 +391,7 @@ public class Main {
loaderTmp = new Loader(launchpadHome) {
@Override
protected void info(String msg) {
- Main.info(msg, null);
+ AbstractMain.info(msg, null);
}
};
} catch (IllegalArgumentException iae) {
@@ -457,14 +482,14 @@ public class Main {
this.started = false;
}
- private void addShutdownHook() {
+ protected void addShutdownHook() {
if (this.installShutdownHook && this.shutdownHook == null) {
this.shutdownHook = new Thread(new ShutdownHook(), "Apache Sling Terminator");
Runtime.getRuntime().addShutdownHook(this.shutdownHook);
}
}
- private void removeShutdownHook() {
+ protected void removeShutdownHook() {
// remove the shutdown hook, will fail if called from the
// shutdown hook itself. Otherwise this prevents shutdown
// from being called again
@@ -550,7 +575,7 @@ public class Main {
* <code>sling.launchpad</code> property.
* @return The absolute <code>File</code> indicating the launchpad folder.
*/
- private static File getLaunchpadHome(final String slingHome,
+ protected static File getLaunchpadHome(final String slingHome,
final Map<String, String> commandLineArgs) {
final String launchpadHomeParam = commandLineArgs.get(SharedConstants.SLING_LAUNCHPAD);
if (launchpadHomeParam == null || launchpadHomeParam.length() == 0) {
@@ -684,37 +709,7 @@ public class Main {
return commandLine;
}
- /** prints a simple usage plus optional error message */
- private static boolean doHelp(Map<String, String> args) {
- if (args.remove("h") != null) {
- System.out.println("usage: "
- + Main.class.getName()
- + " [ start | stop | status ] [ -j adr ] [ -l loglevel ] [ -f logfile ] [ -c slinghome ] [ -i launchpadhome ] [ -a address ] [ -p port ] { -Dn=v } [ -h ]");
-
- System.out.println(" start listen for control connection (uses -j)");
- System.out.println(" stop terminate running Apache Sling (uses -j)");
- System.out.println(" status check whether Apache Sling is running (uses -j)");
- System.out.println(" threads request a thread dump from Apache Sling (uses -j)");
- System.out.println(" -j adr host and port to use for control connection in the format '[host:]port' (default 127.0.0.1:0)");
- System.out.println(" -l loglevel the initial loglevel (0..4, FATAL, ERROR, WARN, INFO, DEBUG)");
- System.out.println(" -f logfile the log file, \"-\" for stdout (default logs/error.log)");
- System.out.println(" -c slinghome the sling context directory (default sling)");
- System.out.println(" -i launchpadhome the launchpad directory (default slinghome)");
- System.out.println(" -a address the interfact to bind to (use 0.0.0.0 for any)");
- System.out.println(" -p port the port to listen to (default 8080)");
- System.out.println(" -r path the root servlet context path for the http service (default is /)");
- System.out.println(" -n don't install the shutdown hook");
- System.out.println(" -Dn=v sets property n to value v. Make sure to use this option *after* " +
- "the jar filename. The JVM also has a -D option which has a " +
- "different meaning");
- System.out.println(" -h prints this usage message");
-
- return true;
- }
- return false;
- }
-
- private static boolean installShutdownHook(Map<String, String> props) {
+ protected static boolean installShutdownHook(Map<String, String> props) {
String prop = props.remove(PROP_SHUTDOWN_HOOK);
if (prop == null) {
prop = System.getProperty(PROP_SHUTDOWN_HOOK);
@@ -904,15 +899,50 @@ public class Main {
}
}
+ /** prints a simple usage plus optional error message */
+ static boolean doHelp(Class mainClass, String[] additionalLines, Map<String, String> args) {
+ if (args.remove("h") != null) {
+ System.out.println("usage: "
+ + mainClass.getName()
+ + " [ start | stop | status ] [ -j adr ] [ -l loglevel ] [ -f logfile ] [ -c slinghome ] [ -i launchpadhome ] [ -a address ] [ -p port ] { -Dn=v } [ -h ]");
+
+ System.out.println(" start listen for control connection (uses -j)");
+ System.out.println(" stop terminate running Apache Sling (uses -j)");
+ System.out.println(" status check whether Apache Sling is running (uses -j)");
+ System.out.println(" threads request a thread dump from Apache Sling (uses -j)");
+ System.out.println(" -j adr host and port to use for control connection in the format '[host:]port' (default 127.0.0.1:0)");
+ System.out.println(" -l loglevel the initial loglevel (0..4, FATAL, ERROR, WARN, INFO, DEBUG)");
+ System.out.println(" -f logfile the log file, \"-\" for stdout (default logs/error.log)");
+ System.out.println(" -c slinghome the sling context directory (default sling)");
+ System.out.println(" -i launchpadhome the launchpad directory (default slinghome)");
+ System.out.println(" -a address the interface to bind to (use 0.0.0.0 for any)");
+ System.out.println(" -p port the port to listen to (default 8080)");
+ System.out.println(" -r path the root servlet context path for the http service (default is /)");
+ System.out.println(" -n don't install the shutdown hook");
+ System.out.println(" -Dn=v sets property n to value v. Make sure to use this option *after* " +
+ "the jar filename. The JVM also has a -D option which has a " +
+ "different meaning");
+ if(additionalLines != null && additionalLines.length > 0) {
+ for(String line: additionalLines) {
+ System.out.println(line);
+ }
+ }
+ System.out.println(" -h prints this usage message");
+
+ return true;
+ }
+ return false;
+ }
+
private class ShutdownHook implements Runnable {
@Override
public void run() {
- Main.info("Java VM is shutting down", null);
- Main.this.doStop();
+ AbstractMain.info("Java VM is shutting down", null);
+ AbstractMain.this.doStop();
}
}
- private class Notified implements Notifiable {
+ protected class Notified implements Notifiable {
/**
* The framework has been stopped by calling the
@@ -930,10 +960,10 @@ public class Main {
* fails).
*/
- Main.info("Apache Sling has been stopped", null);
+ AbstractMain.info("Apache Sling has been stopped", null);
- Main.this.sling = null;
- Main.this.doStop();
+ AbstractMain.this.sling = null;
+ AbstractMain.this.doStop();
}
/**
@@ -952,35 +982,35 @@ public class Main {
@Override
public void updated(File updateFile) {
- Main.this.sling = null;
- Main.this.doStop();
+ AbstractMain.this.sling = null;
+ AbstractMain.this.doStop();
- Main.cleanupThreads();
+ AbstractMain.cleanupThreads();
if (updateFile == null) {
- Main.info("Restarting Framework and Apache Sling", null);
- if (!Main.this.doStart(null)) {
- Main.error("Failed to restart Sling; terminating", null);
- Main.this.terminateVM(1);
+ AbstractMain.info("Restarting Framework and Apache Sling", null);
+ if (!AbstractMain.this.doStart(null)) {
+ AbstractMain.error("Failed to restart Sling; terminating", null);
+ AbstractMain.this.terminateVM(1);
}
} else {
- Main.info(
+ AbstractMain.info(
"Restarting Framework with update from " + updateFile, null);
boolean started = false;
try {
- started = Main.this.doStart(updateFile.toURI().toURL());
+ started = AbstractMain.this.doStart(updateFile.toURI().toURL());
} catch (MalformedURLException mue) {
- Main.error("Cannot get URL for file " + updateFile, mue);
+ AbstractMain.error("Cannot get URL for file " + updateFile, mue);
} finally {
updateFile.delete();
}
if (!started) {
- Main.error("Failed to restart Sling; terminating", null);
- Main.this.terminateVM(1);
+ AbstractMain.error("Failed to restart Sling; terminating", null);
+ AbstractMain.this.terminateVM(1);
}
}
}
diff --git a/src/main/java/org/apache/sling/launchpad/app/ControlListener.java b/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
index 6e6284b..26689f8 100644
--- a/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
+++ b/src/main/java/org/apache/sling/launchpad/app/ControlListener.java
@@ -93,7 +93,7 @@ class ControlListener implements Runnable {
private static final int DEFAULT_LISTEN_PORT = 0;
// The reference to the Main class to shutdown on request
- private final Main slingMain;
+ private final AbstractMain slingMain;
private final String listenSpec;
@@ -117,7 +117,7 @@ class ControlListener implements Runnable {
* @param listenSpec The specification for the host and port for the socket
* connection. See above for the format of this parameter.
*/
- ControlListener(final Main slingMain, final String listenSpec) {
+ ControlListener(final AbstractMain slingMain, final String listenSpec) {
this.slingMain = slingMain;
this.listenSpec = listenSpec; // socketAddress = this.getSocketAddress(listenSpec, selectNewPort);
}
diff --git a/src/main/java/org/apache/sling/launchpad/app/FeatureMain.java b/src/main/java/org/apache/sling/launchpad/app/FeatureMain.java
new file mode 100644
index 0000000..ffa9301
--- /dev/null
+++ b/src/main/java/org/apache/sling/launchpad/app/FeatureMain.java
@@ -0,0 +1,221 @@
+/*
+ * 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.launchpad.app;
+
+import org.apache.sling.launchpad.base.shared.Launcher;
+import org.apache.sling.launchpad.base.shared.Loader;
+import org.apache.sling.launchpad.base.shared.Notifiable;
+import org.apache.sling.launchpad.base.shared.SharedConstants;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Map.Entry;
+
+/**
+ * The <code>FeatureMain</code> is the externally visible Standalone Java Application
+ * launcher for Feature Model base Sling. Please refer to the full description <i>The Sling
+ * Launchpad</i> on the Sling Web Site for a full description of this class.
+ * <p>
+ * Logging goes to standard output for informational messages and to standard
+ * error for error messages.
+ * <p>
+ * This class goes into the secondary artifact with the classifier <i>app</i> to
+ * be used as the main class when starting the Java Application.
+ *
+ * @see <a href="http://sling.apache.org/site/the-sling-launchpad.html">The
+ * Sling Launchpad</a>
+ */
+public class FeatureMain extends AbstractMain {
+
+ public static void main(final String[] args) {
+ AbstractMain.main(FeatureMain.class, args);
+ }
+
+ /** prints a simple usage plus optional error message */
+ public static boolean doHelp(Map<String, String> args) {
+ return AbstractMain.doHelp(Main.class,
+ new String[] {
+ " -s Main Feature File (will override the provided Sling Feature File)",
+ " -af Additional Feature Files added to the Main Feature File",
+ },
+ args
+ );
+ }
+
+ public FeatureMain(Map<String, String> args) {
+ super(args);
+ }
+
+ protected int doControlAction() {
+ //AS TODO: It would probably best to have the logic of handling Background Service to be inside the Feature Launcher
+// final ControlAction action = getControlAction();
+// if (action != null) {
+// final ControlListener sl = new ControlListener(this,
+// commandLineArgs.remove(PROP_CONTROL_SOCKET));
+// switch (action) {
+// case START:
+// if (!sl.listen()) {
+// // assume service already running
+// return 0;
+// }
+// break;
+// case STATUS:
+// return sl.statusServer();
+// case STOP:
+// return sl.shutdownServer();
+// case THREADS:
+// return sl.dumpThreads();
+// }
+// }
+
+ return -1;
+ }
+
+ protected boolean doStart(final URL launcherJar) {
+
+ // prevent duplicate start
+ if ( this.started) {
+ info("Apache Sling has already been started", null);
+ return true;
+ }
+
+ info("Starting Apache Sling in " + slingHome, null);
+ this.started = true;
+
+ try {
+ java.net.URL url = getClass().getResource("/feature-sling12.json");
+ String[] arguments = new String[]{
+// "-f", tempFile.getAbsolutePath(),
+ "-f", url.toString(),
+ "-D", "org.osgi.service.http.port=8081",
+ "-v"
+ };
+ System.out.println("Before Launching Feature Launcher, arguments: " + Arrays.asList(arguments));
+ org.apache.sling.feature.launcher.impl.Main.main(arguments);
+ } catch(Throwable t) {
+ System.out.println("Caught an Exception: " + t.getLocalizedMessage());
+ t.printStackTrace();
+ }
+
+// Loader loaderTmp = null;
+// try {
+// final File launchpadHome = getLaunchpadHome(slingHome,
+// commandLineArgs);
+// loaderTmp = new Loader(launchpadHome) {
+// @Override
+// protected void info(String msg) {
+// FeatureMain.info(msg, null);
+// }
+// };
+// } catch (IllegalArgumentException iae) {
+// error(
+// "Cannot launch: Launchpad folder cannot be used: "
+// + iae.getMessage(), null);
+// return false;
+// }
+// this.loader = loaderTmp;
+//
+// if (launcherJar != null) {
+// try {
+// loader.installLauncherJar(launcherJar);
+// } catch (IOException ioe) {
+// error("Cannot launch: Cannot install " + launcherJar
+// + " for use", ioe);
+// return false;
+// }
+// } else {
+// info("No Launcher JAR to install", null);
+// }
+//
+// Object object = null;
+// try {
+// object = loader.loadLauncher(SharedConstants.DEFAULT_SLING_MAIN);
+// } catch (IllegalArgumentException iae) {
+// error("Cannot launch: Failed loading Sling class "
+// + SharedConstants.DEFAULT_SLING_MAIN, iae);
+// return false;
+// }
+//
+// if (object instanceof Launcher) {
+//
+// // configure the launcher
+// Launcher sling = (Launcher) object;
+// sling.setNotifiable(new Notified());
+// sling.setCommandLine(commandLineArgs);
+// sling.setSlingHome(slingHome);
+//
+// // launch it
+// info("Starting launcher ...", null);
+// if (sling.start()) {
+// info("Startup completed", null);
+// this.sling = sling;
+// addShutdownHook();
+// return true;
+// }
+//
+// error("Cannot launch: Launcher.start() returned false", null);
+//
+// } else {
+//
+// error("Cannot launch: Class " + SharedConstants.DEFAULT_SLING_MAIN + " is not a Launcher class", null);
+//
+// }
+
+ return false;
+ }
+
+ /**
+ * Maybe called by the application to cause the Sling Application to
+ * properly terminate by stopping the OSGi Framework.
+ * <p>
+ * After calling this method the Sling Application can be started again
+ * by calling the {@link #doStart()} method.
+ * <p>
+ * Calling this method multiple times without calling the {@link #doStart()}
+ * method in between has no effect after the Sling Application has been
+ * terminated.
+ */
+ protected void doStop() {
+ removeShutdownHook();
+
+// // now really shutdown sling
+// if (this.sling != null) {
+// info("Stopping Apache Sling", null);
+// this.sling.stop();
+// this.sling = null;
+// }
+//
+// // clean and VM caches
+// if (this.loader != null) {
+// this.loader.cleanupVM();
+// this.loader = null;
+// }
+//
+// // further cleanup
+// this.started = false;
+ }
+
+}
diff --git a/src/main/java/org/apache/sling/launchpad/app/Main.java b/src/main/java/org/apache/sling/launchpad/app/Main.java
index 312ba8f..38509d2 100644
--- a/src/main/java/org/apache/sling/launchpad/app/Main.java
+++ b/src/main/java/org/apache/sling/launchpad/app/Main.java
@@ -16,22 +16,7 @@
*/
package org.apache.sling.launchpad.app;
-import java.io.File;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.text.DateFormat;
-import java.text.SimpleDateFormat;
-import java.util.Date;
-import java.util.HashMap;
import java.util.Map;
-import java.util.Map.Entry;
-
-import org.apache.sling.launchpad.base.shared.Launcher;
-import org.apache.sling.launchpad.base.shared.Loader;
-import org.apache.sling.launchpad.base.shared.Notifiable;
-import org.apache.sling.launchpad.base.shared.SharedConstants;
/**
* The <code>Main</code> is the externally visible Standalone Java Application
@@ -47,943 +32,18 @@ import org.apache.sling.launchpad.base.shared.SharedConstants;
* @see <a href="http://sling.apache.org/site/the-sling-launchpad.html">The
* Sling Launchpad</a>
*/
-public class Main {
-
- // The name of the environment variable to consult to find out
- // about sling.home
- private static final String ENV_SLING_HOME = "SLING_HOME";
-
- /**
- * The name of the configuration property indicating the
- * {@link ControlAction} to be taken in the {@link #doControlAction()}
- * method.
- */
- protected static final String PROP_CONTROL_ACTION = "sling.control.action";
-
- /**
- * The name of the configuration property indicating the socket to use for
- * the control connection. The value of this property is either just a port
- * number (in which case the host is assumed to be <code>localhost</code>)
- * or a host name (or IP address) and port number separated by a colon.
- */
- protected static final String PROP_CONTROL_SOCKET = "sling.control.socket";
-
- /** The Sling configuration property name setting the initial log level */
- private static final String PROP_LOG_LEVEL = "org.apache.sling.commons.log.level";
-
- /** The Sling configuration property name setting the initial log file */
- private static final String PROP_LOG_FILE = "org.apache.sling.commons.log.file";
-
- /**
- * The configuration property setting the port on which the HTTP service
- * listens
- */
- private static final String PROP_PORT = "org.osgi.service.http.port";
-
- /**
- * The configuration property setting the context path where the HTTP service
- * mounts itself.
- */
- private static final String PROP_CONTEXT_PATH = "org.apache.felix.http.context_path";
-
- /**
- * Host name or IP Address of the interface to listen on.
- */
- private static final String PROP_HOST = "org.apache.felix.http.host";
+public class Main extends AbstractMain {
- /**
- * Name of the configuration property (or system property) indicating
- * whether the shutdown hook should be installed or not. If this property is
- * not set or set to {@code true} (case insensitive), the shutdown hook
- * properly shutting down the framework is installed on startup. Otherwise,
- * if this property is set to any value other than {@code true} (case
- * insensitive) the shutdown hook is not installed.
- * <p>
- * The respective command line option is {@code -n}.
- */
- private static final String PROP_SHUTDOWN_HOOK = "sling.shutdown.hook";
-
- /**
- * The main entry point to the Sling Launcher Standalone Java Application.
- * This method is generally only called by the Java VM to launch Sling.
- *
- * @param args The command line arguments supplied when starting the Sling
- * Launcher through the Java VM.
- */
public static void main(final String[] args) {
- final Map<String, String> rawArgs = parseCommandLine(args);
-
- // support usage first
- if (doHelp(rawArgs)) {
- System.exit(0);
- }
-
- final Map<String, String> props = convertCommandLineArgs(rawArgs);
- if (props == null) {
- System.exit(1);
- }
-
- final Main main = new Main(props);
-
- // check for control commands
- int rc = main.doControlAction();
- if (rc >= 0) {
- main.terminateVM(rc);
- }
-
- // finally start Sling
- if (!main.doStart()) {
- error("Failed to start Sling; terminating", null);
- main.terminateVM(1);
- }
- }
-
- /**
- * The map of command line arguments where the keys are the actual
- * property names as known to the OSGi Framework and its installed
- * bundles.
- */
- private final Map<String, String> commandLineArgs;
-
- /**
- * Whether to install the shutdown hook.
- *
- * @see #PROP_SHUTDOWN_HOOK
- * @see #installShutdownHook(Map)
- * @see #addShutdownHook()
- */
- private boolean installShutdownHook;
-
- /**
- * The shutdown hook installed into the Java VM after Sling has been
- * started. The hook is removed again when Sling is being shut down
- * or the {@link Notified notifier} is notified of the framework shutdown.
- *
- * @see #addShutdownHook()
- * @see #removeShutdownHook()
- */
- private Thread shutdownHook;
-
- /**
- * The absolute path to the home directory of the launched Sling
- * application. This corresponds to the value of the <code>sling.home</code>
- * framework property.
- *
- * @see #getSlingHome(Map)
- */
- private final String slingHome;
-
- /**
- * The {@link Loader} class used to create the Framework class loader and
- * to launch the framework.
- */
- private Loader loader;
-
- /**
- * The actual launcher accessed through the {@link #loader} to launch
- * the OSGi Framework.
- */
- private Launcher sling;
-
- /**
- * Flag to indicate if Sling has already been started.
- */
- private boolean started = false;
-
- /**
- * Creates an instance of this main loader class. The provided arguments are
- * used to configure the OSGi framework being launched with the
- * {@link #doStart(URL)} method.
- *
- * @param args The map of configuration properties to be supplied to the
- * OSGi framework. The keys in this map are assumed to be usefull
- * without translation to the launcher and the OSGi Framework. If
- * this parameter is <code>null</code> and empty map without
- * configuration is assumed.
- */
- protected Main(Map<String, String> args) {
- this.commandLineArgs = (args == null)
- ? new HashMap<String, String>()
- : args;
-
- this.installShutdownHook = installShutdownHook(this.commandLineArgs);
-
- // sling.home from the command line or system properties, else default
- String home = getSlingHome(commandLineArgs);
- final File slingHomeFile = new File(home);
- if (!slingHomeFile.isAbsolute()) {
- home = slingHomeFile.getAbsolutePath();
- }
- this.slingHome = home;
- }
-
- /**
- * After instantiating this class, this method may be called to help with
- * the communication with a running Sling instance. To setup this
- * communication the configuration properties supplied to the constructor
- * are evaluated as follows:
- * <p>
- * <table>
- * <tr>
- * <td><code>{@value #PROP_CONTROL_SOCKET}</code></td>
- * <td>Specifies the socket to use for the control connection. This
- * specification is of the form <i>host:port</i> where the host can be a
- * host name or IP Address and may be omitted (along with the separating
- * colon) and port is just the numeric port number at which to listen. The
- * default is <i>localhost:63000</i>. It is suggested to not use an
- * externally accessible interface for security reasons because there is no
- * added security on this control channel for now.</td>
- * </tr>
- * <tr>
- * <td><code>{@value #PROP_CONTROL_ACTION}</code></td>
- * <td>The actual action to execute:
- * <ul>
- * <b>start</b> -- Start the listener on the configured socket and expect
- * commands there. This action is useful only when launching the Sling
- * application since this action helps manage a running system.
- * </ul>
- * <ul>
- * <b>stop</b> -- Connects to the listener running on the configured socket
- * and send the command to terminate the Sling Application. If this command
- * is used, it is expected the Sling Application will not start.
- * </ul>
- * <ul>
- * <b>status</b> -- Connects to the listener running on the configured
- * socket and query about its status. If this command is used, it is
- * expected the Sling Application will not start.
- * </ul>
- * </td>
- * </tr>
- * </table>
- * <p>
- * After this method has executed the <code>j</code> and
- * {@link #PROP_CONTROL_ACTION} properties have been removed from the
- * configuration properties.
- * <p>
- * While the {@link #doStart()} and {@link #doStop()} methods may be called
- * multiple times this method should only be called once after creating this
- * class's instance.
- *
- * @return An code indicating whether the Java VM is expected to be
- * terminated or not. If <code>-1</code> is returned, the VM should
- * continue as intended, maybe starting the Sling Application. This
- * code is returned if the start action (or no action at all) is
- * supplied. Otherwise the VM should terminate with the returned
- * code as its exit code. For the stop action, this will be zero.
- * For the status action, this will be a LSB compliant code for
- * daemon status check: 0 (application running), 1 (Program Dead),
- * 3 (Program Not Running), 4 (Unknown Problem).
- * @see <a
- * href="http://refspecs.linuxbase.org/LSB_3.1.0/LSB-Core-generic/LSB-Core-generic/iniscrptact.html">Init Script Actions</a>
- * for a definition of the LSB status codes
- */
- protected int doControlAction() {
- final ControlAction action = getControlAction();
- if (action != null) {
- final ControlListener sl = new ControlListener(this,
- commandLineArgs.remove(PROP_CONTROL_SOCKET));
- switch (action) {
- case START:
- if (!sl.listen()) {
- // assume service already running
- return 0;
- }
- break;
- case STATUS:
- return sl.statusServer();
- case STOP:
- return sl.shutdownServer();
- case THREADS:
- return sl.dumpThreads();
- }
- }
-
- return -1;
- }
-
- /**
- * Terminates the VM which was started by calling the
- * {@link #main(String[])} method of this class using the
- * <code>status</code> value as the application exit status code.
- * <p>
- * This method does not return.
- *
- * @param status The application status exit code.
- */
- // default accessor to enable overwriting for unit tests
- void terminateVM(final int status) {
- System.exit(status);
- }
-
- private ControlAction getControlAction() {
- Object action = this.commandLineArgs.remove(PROP_CONTROL_ACTION);
- if (action != null) {
- if (action instanceof ControlAction) {
- return (ControlAction) action;
- }
-
- try {
- return ControlAction.valueOf(action.toString().toUpperCase());
- } catch (IllegalArgumentException iae) {
- error("Illegal control action value: " + action, null);
- }
- }
- return null;
- }
-
- /**
- * Starts the application with the configuration supplied with the
- * configuration properties when this instance has been created.
- * <p>
- * Calling this method multiple times before calling {@link #doStop()} will
- * cause a message to be printed and <code>true</code> being returned.
- *
- * @return <code>true</code> if startup was successful or the application
- * is considered to be started already. Otherwise an error message
- * has been logged and <code>false</code> is returned.
- */
- protected boolean doStart() {
- // ensure up-to-date launcher jar
- return doStart(getClass().getResource(
- SharedConstants.DEFAULT_SLING_LAUNCHER_JAR));
- }
-
- protected boolean doStart(final URL launcherJar) {
-
- // prevent duplicate start
- if ( this.started) {
- info("Apache Sling has already been started", null);
- return true;
- }
-
- info("Starting Apache Sling in " + slingHome, null);
- this.started = true;
-
- Loader loaderTmp = null;
- try {
- final File launchpadHome = getLaunchpadHome(slingHome,
- commandLineArgs);
- loaderTmp = new Loader(launchpadHome) {
- @Override
- protected void info(String msg) {
- Main.info(msg, null);
- }
- };
- } catch (IllegalArgumentException iae) {
- error(
- "Cannot launch: Launchpad folder cannot be used: "
- + iae.getMessage(), null);
- return false;
- }
- this.loader = loaderTmp;
-
- if (launcherJar != null) {
- try {
- loader.installLauncherJar(launcherJar);
- } catch (IOException ioe) {
- error("Cannot launch: Cannot install " + launcherJar
- + " for use", ioe);
- return false;
- }
- } else {
- info("No Launcher JAR to install", null);
- }
-
- Object object = null;
- try {
- object = loader.loadLauncher(SharedConstants.DEFAULT_SLING_MAIN);
- } catch (IllegalArgumentException iae) {
- error("Cannot launch: Failed loading Sling class "
- + SharedConstants.DEFAULT_SLING_MAIN, iae);
- return false;
- }
-
- if (object instanceof Launcher) {
-
- // configure the launcher
- Launcher sling = (Launcher) object;
- sling.setNotifiable(new Notified());
- sling.setCommandLine(commandLineArgs);
- sling.setSlingHome(slingHome);
-
- // launch it
- info("Starting launcher ...", null);
- if (sling.start()) {
- info("Startup completed", null);
- this.sling = sling;
- addShutdownHook();
- return true;
- }
-
- error("Cannot launch: Launcher.start() returned false", null);
-
- } else {
-
- error("Cannot launch: Class " + SharedConstants.DEFAULT_SLING_MAIN + " is not a Launcher class", null);
-
- }
-
- return false;
- }
-
- /**
- * Maybe called by the application to cause the Sling Application to
- * properly terminate by stopping the OSGi Framework.
- * <p>
- * After calling this method the Sling Application can be started again
- * by calling the {@link #doStart()} method.
- * <p>
- * Calling this method multiple times without calling the {@link #doStart()}
- * method in between has no effect after the Sling Application has been
- * terminated.
- */
- protected void doStop() {
- removeShutdownHook();
-
- // now really shutdown sling
- if (this.sling != null) {
- info("Stopping Apache Sling", null);
- this.sling.stop();
- this.sling = null;
- }
-
- // clean and VM caches
- if (this.loader != null) {
- this.loader.cleanupVM();
- this.loader = null;
- }
-
- // further cleanup
- this.started = false;
- }
-
- private void addShutdownHook() {
- if (this.installShutdownHook && this.shutdownHook == null) {
- this.shutdownHook = new Thread(new ShutdownHook(), "Apache Sling Terminator");
- Runtime.getRuntime().addShutdownHook(this.shutdownHook);
- }
- }
-
- private void removeShutdownHook() {
- // remove the shutdown hook, will fail if called from the
- // shutdown hook itself. Otherwise this prevents shutdown
- // from being called again
- Thread shutdownHook = this.shutdownHook;
- this.shutdownHook = null;
-
- if (shutdownHook != null) {
- try {
- Runtime.getRuntime().removeShutdownHook(shutdownHook);
- } catch (Throwable t) {
- // don't care for problems removing the hook
- }
- }
- }
-
- /**
- * Define the sling.home parameter implementing the algorithme defined on
- * the wiki page to find the setting according to this algorithm:
- * <ol>
- * <li>Configuration property <code>sling.home</code></li>
- * <li>System property <code>sling.home</code></li>
- * <li>Environment variable <code>SLING_HOME</code></li>
- * <li>Default value <code>sling</code></li>
- * </ol>
- *
- * @param commandLine The command line arguments
- * @return The value to use for sling.home
- */
- private static String getSlingHome(Map<String, String> commandLine) {
- String source = null;
-
- String slingHome = commandLine.get(SharedConstants.SLING_HOME);
- if (slingHome != null) {
-
- source = "command line";
-
- } else {
-
- slingHome = System.getProperty(SharedConstants.SLING_HOME);
- if (slingHome != null) {
-
- source = "system property sling.home";
-
- } else {
-
- slingHome = System.getenv(ENV_SLING_HOME);
- if (slingHome != null) {
-
- source = "environment variable SLING_HOME";
-
- } else {
-
- source = "default";
- slingHome = SharedConstants.SLING_HOME_DEFAULT;
-
- }
- }
- }
-
- info("Setting sling.home=" + slingHome + " (" + source + ")", null);
- return slingHome;
- }
-
- /**
- * Return the absolute path to sling home
- */
- public String getSlingHome() {
- return this.slingHome;
- }
-
- /**
- * Define the sling.launchpad parameter implementing the algorithm defined
- * on the wiki page to find the setting according to this algorithm:
- * <ol>
- * <li>Configuration property <code>sling.launchpad</code>. This path is
- * resolved against the <code>slingHome</code> folder if relative.</li>
- * <li>Default to same as <code>sling.home</code></li>
- * </ol>
- *
- * @param slingHome The absolute path to the Sling Home folder (aka the
- * <code>sling.home</code>.
- * @param commandLineArgs The configuration properties from where to get the
- * <code>sling.launchpad</code> property.
- * @return The absolute <code>File</code> indicating the launchpad folder.
- */
- private static File getLaunchpadHome(final String slingHome,
- final Map<String, String> commandLineArgs) {
- final String launchpadHomeParam = commandLineArgs.get(SharedConstants.SLING_LAUNCHPAD);
- if (launchpadHomeParam == null || launchpadHomeParam.length() == 0) {
- commandLineArgs.put(SharedConstants.SLING_LAUNCHPAD, slingHome);
- return new File(slingHome);
- }
-
- File launchpadHome = new File(launchpadHomeParam);
- if (!launchpadHome.isAbsolute()) {
- launchpadHome = new File(slingHome, launchpadHomeParam);
- }
-
- commandLineArgs.put(SharedConstants.SLING_LAUNCHPAD,
- launchpadHome.getAbsolutePath());
- return launchpadHome;
- }
-
- // ---------- logging
-
- // emit an informational message to standard out
- protected static void info(String message, Throwable t) {
- log(System.out, "*INFO *", message, t);
- }
-
- // emit an error message to standard err
- protected static void error(String message, Throwable t) {
- log(System.err, "*ERROR*", message, t);
- }
-
- private static final DateFormat fmt = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SSS ");
-
- // helper method to format the message on the correct output channel
- // the throwable if not-null is also prefixed line by line with the prefix
- private static void log(PrintStream out, String prefix, String message,
- Throwable t) {
-
- final StringBuilder linePrefixBuilder = new StringBuilder();
- synchronized (fmt) {
- linePrefixBuilder.append(fmt.format(new Date()));
- }
- linePrefixBuilder.append(prefix);
- linePrefixBuilder.append(" [");
- linePrefixBuilder.append(Thread.currentThread().getName());
- linePrefixBuilder.append("] ");
- final String linePrefix = linePrefixBuilder.toString();
-
- out.print(linePrefix);
- out.println(message);
- if (t != null) {
- t.printStackTrace(new PrintStream(out) {
- @Override
- public void print(String x) {
- super.print(linePrefix);
- super.print(x);
- }
- });
- }
- }
-
- /**
- * Parses the command line arguments into a map of strings indexed by
- * strings. This method supports single character option names only at the
- * moment. Each pair of an option name and its value is stored into the
- * map. If a single dash '-' character is encountered the rest of the command
- * line are interpreted as option names and are stored in the map unmodified
- * as entries with the same key and value.
- * <table>
- * <tr><th>Command Line</th><th>Mapping</th></tr>
- * <tr><td>x</td><td>x -> x</td></tr>
- * <tr><td>-y z</td><td>y -> z</td></tr>
- * <tr><td>-yz</td><td>y -> z</td></tr>
- * <tr><td>-y -z</td><td>y -> y, z -> z</td></tr>
- * <tr><td>-y x - -z a</td><td>y -> x, -z -> -z, a -> a</td></tr>
- * </table>
- *
- * @param args The command line to parse
- *
- * @return The map of command line options and their values
- */
- // default accessor to enable unit tests without requiring reflection
- static Map<String, String> parseCommandLine(String... args) {
- Map<String, String> commandLine = new HashMap<String, String>();
- boolean readUnparsed = false;
- for (int argc = 0; args != null && argc < args.length; argc++) {
- String arg = args[argc];
-
- if (readUnparsed) {
- commandLine.put(arg, arg);
- } else if (arg.startsWith("-")) {
- if (arg.length() == 1) {
- readUnparsed = true;
- } else {
- String key = String.valueOf(arg.charAt(1));
- if (arg.length() > 2) {
- final String val;
- final int indexOfEq = arg.indexOf('=');
- if (indexOfEq != -1) {
- //Handle case -Da=b
- key = arg.substring(1, indexOfEq);
- val = arg.substring(indexOfEq + 1);
- } else {
- val = arg.substring(2);
- }
- commandLine.put(key, val);
- } else {
- argc++;
- if (argc < args.length
- && (args[argc].equals("-") || !args[argc].startsWith("-"))) {
- String val = args[argc];
-
- //Special handling for -D a=b
- if(key.equals("D")){
- final int indexOfEq = val.indexOf('=');
- if (indexOfEq != -1) {
- //Handle case -D a=b. Add key as Da
- key = "D" + val.substring(0, indexOfEq);
- val = val.substring(indexOfEq + 1);
- }
- }
- commandLine.put(key, val);
- } else {
- commandLine.put(key, key);
- argc--;
- }
- }
- }
- } else {
- commandLine.put(arg, arg);
- }
- }
- return commandLine;
- }
-
- /** prints a simple usage plus optional error message */
- private static boolean doHelp(Map<String, String> args) {
- if (args.remove("h") != null) {
- System.out.println("usage: "
- + Main.class.getName()
- + " [ start | stop | status ] [ -j adr ] [ -l loglevel ] [ -f logfile ] [ -c slinghome ] [ -i launchpadhome ] [ -a address ] [ -p port ] { -Dn=v } [ -h ]");
-
- System.out.println(" start listen for control connection (uses -j)");
- System.out.println(" stop terminate running Apache Sling (uses -j)");
- System.out.println(" status check whether Apache Sling is running (uses -j)");
- System.out.println(" threads request a thread dump from Apache Sling (uses -j)");
- System.out.println(" -j adr host and port to use for control connection in the format '[host:]port' (default 127.0.0.1:0)");
- System.out.println(" -l loglevel the initial loglevel (0..4, FATAL, ERROR, WARN, INFO, DEBUG)");
- System.out.println(" -f logfile the log file, \"-\" for stdout (default logs/error.log)");
- System.out.println(" -c slinghome the sling context directory (default sling)");
- System.out.println(" -i launchpadhome the launchpad directory (default slinghome)");
- System.out.println(" -a address the interfact to bind to (use 0.0.0.0 for any)");
- System.out.println(" -p port the port to listen to (default 8080)");
- System.out.println(" -r path the root servlet context path for the http service (default is /)");
- System.out.println(" -n don't install the shutdown hook");
- System.out.println(" -Dn=v sets property n to value v. Make sure to use this option *after* " +
- "the jar filename. The JVM also has a -D option which has a " +
- "different meaning");
- System.out.println(" -h prints this usage message");
-
- return true;
- }
- return false;
+ AbstractMain.main(Main.class, args);
}
- private static boolean installShutdownHook(Map<String, String> props) {
- String prop = props.remove(PROP_SHUTDOWN_HOOK);
- if (prop == null) {
- prop = System.getProperty(PROP_SHUTDOWN_HOOK);
- }
-
- return (prop == null) ? true : Boolean.valueOf(prop);
+ /** prints a simple usage plus optional error message */
+ public static boolean doHelp(Map<String, String> args) {
+ return AbstractMain.doHelp(Main.class, null, args);
}
- // default accessor to enable unit tests without requiring reflection
- static Map<String, String> convertCommandLineArgs(
- Map<String, String> rawArgs) {
- final HashMap<String, String> props = new HashMap<String, String>();
- boolean errorArg = false;
- for (Entry<String, String> arg : rawArgs.entrySet()) {
- if (arg.getKey().length() == 1 || arg.getKey().startsWith("D")) {
- String value = arg.getValue();
- switch (arg.getKey().charAt(0)) {
- case 'j':
- if (value == arg.getKey()) {
- errorArg("-j", "Missing host:port value");
- errorArg = true;
- continue;
- }
- props.put(PROP_CONTROL_SOCKET, value);
- break;
-
- case 'l':
- if (value == arg.getKey()) {
- errorArg("-l", "Missing log level value");
- errorArg = true;
- continue;
- }
- props.put(PROP_LOG_LEVEL, value);
- break;
-
- case 'f':
- if (value == arg.getKey()) {
- errorArg("-f", "Missing log file value");
- errorArg = true;
- continue;
- } else if ("-".equals(value)) {
- value = "";
- }
- props.put(PROP_LOG_FILE, value);
- break;
-
- case 'c':
- if (value == arg.getKey()) {
- errorArg("-c", "Missing directory value");
- errorArg = true;
- continue;
- }
- props.put(SharedConstants.SLING_HOME, value);
- break;
-
- case 'i':
- if (value == arg.getKey()) {
- errorArg("-i", "Missing launchpad directory value");
- errorArg = true;
- continue;
- }
- props.put(SharedConstants.SLING_LAUNCHPAD, value);
- break;
-
- case 'a':
- if (value == arg.getKey()) {
- errorArg("-a", "Missing address value");
- errorArg = true;
- continue;
- }
- props.put(PROP_HOST, value);
- break;
-
- case 'p':
- if (value == arg.getKey()) {
- errorArg("-p", "Missing port value");
- errorArg = true;
- continue;
- }
- try {
- // just to verify it is a number
- Integer.parseInt(value);
- props.put(PROP_PORT, value);
- } catch (RuntimeException e) {
- errorArg("-p", "Bad port: " + value);
- errorArg = true;
- }
- break;
-
- case 'r':
- if (value == arg.getKey()) {
- errorArg("-r", "Missing root path value");
- errorArg = true;
- continue;
- }
- props.put(PROP_CONTEXT_PATH, value);
- break;
-
- case 'n':
- props.put(PROP_SHUTDOWN_HOOK, Boolean.FALSE.toString());
- break;
-
- case 'D':
- if (value == arg.getKey()) {
- errorArg("-D", "Missing property assignment");
- errorArg = true;
- continue;
- }
- if (arg.getKey().length() > 1) {
- //Dfoo=bar arg.key=Dfoo and arg.value=bar
- props.put(arg.getKey().substring(1), arg.getValue());
- } else {
- //D foo=bar arg.key=D and arg.value=foo=bar
- String[] parts = value.split("=");
- int valueIdx = (parts.length > 1) ? 1 : 0;
- props.put(parts[0], parts[valueIdx]);
- }
- break;
-
- default:
- errorArg("-" + arg.getKey(), "Unrecognized option");
- errorArg = true;
- break;
- }
- } else if ("start".equals(arg.getKey())
- || "stop".equals(arg.getKey())
- || "status".equals(arg.getKey())
- || "threads".equals(arg.getKey())) {
- props.put(PROP_CONTROL_ACTION, arg.getValue());
- } else {
- errorArg(arg.getKey(), "Unrecognized option");
- errorArg = true;
- }
- }
- return errorArg ? null : props;
- }
-
- private static void errorArg(String option, String message) {
- error(String.format("%s: %s (use -h for more information)", option,
- message), null);
- }
-
- /**
- * Removes well-known stray threads and thread groups and removes framework
- * thread context class loaders. Well-known stray threads and thread groups
- * are:
- * <ul>
- * <li>The FileCleaningTracker$Reaper thread of the commons-io library</li>
- * <li>The QuartzScheduler:ApacheSling thread group. See <a
- * href="https://issues.apache.org/jira/browse/SLING-2535">SLING-2535
- * QuartzScheduler:ApacheSling thread group remaining after stopping the
- * scheduler bundle</a></li>
- * </ul>
- */
- @SuppressWarnings("deprecation")
- static void cleanupThreads() {
-
- // the current thread is the SlingNotifier thread part of
- // the main thread group, whose parent is the system thread
- // group. We only care for the main thread group here
- ThreadGroup tg = Thread.currentThread().getThreadGroup();
- Thread[] active = new Thread[tg.activeCount()];
- tg.enumerate(active);
- for (Thread thread : active) {
- if (thread != null) {
- if (thread.getName().equals("FileCleaningTracker$Reaper")) {
- // I know, but this thread is stray ...
- // Commons-IO bundle (or consumer of it) should
- // actually stop it
- thread.stop();
- } else {
- ClassLoader loader = thread.getContextClassLoader();
- if (loader != null && loader.getClass().getName().startsWith("org.apache.felix.framework.")) {
- thread.setContextClassLoader(null);
- }
- }
- }
- }
-
- // SLING-2535 - Scheduler thread group
- ThreadGroup[] groups = new ThreadGroup[tg.activeGroupCount()];
- tg.enumerate(groups);
- for (ThreadGroup group : groups) {
- if (group != null && group.getName().equals("QuartzScheduler:ApacheSling")) {
- group.destroy();
- }
- }
+ public Main(Map<String, String> args) {
+ super(args);
}
-
- private class ShutdownHook implements Runnable {
- @Override
- public void run() {
- Main.info("Java VM is shutting down", null);
- Main.this.doStop();
- }
- }
-
- private class Notified implements Notifiable {
-
- /**
- * The framework has been stopped by calling the
- * <code>Bundle.stop()</code> on the system bundle. This actually
- * terminates the Sling Standalone application.
- */
- @Override
- public void stopped() {
- /**
- * This method is called if the framework is stopped from within by
- * calling stop on the system bundle or if the framework is stopped
- * because the VM is going down and the shutdown hook has initiated
- * the shutdown. In any case we ensure the reference to the framework
- * is removed and remove the shutdown hook (but don't care if that
- * fails).
- */
-
- Main.info("Apache Sling has been stopped", null);
-
- Main.this.sling = null;
- Main.this.doStop();
- }
-
- /**
- * The framework has been stopped with the intent to be restarted by
- * calling either of the <code>Bundle.update</code> methods on the
- * system bundle.
- * <p>
- * If an <code>InputStream</code> was provided, this has been copied to
- * a temporary file, which will be used in place of the existing
- * launcher jar file.
- *
- * @param updateFile The temporary file to replace the existing launcher
- * jar file. If <code>null</code> the existing launcher jar
- * will be used again.
- */
- @Override
- public void updated(File updateFile) {
-
- Main.this.sling = null;
- Main.this.doStop();
-
- Main.cleanupThreads();
-
- if (updateFile == null) {
-
- Main.info("Restarting Framework and Apache Sling", null);
- if (!Main.this.doStart(null)) {
- Main.error("Failed to restart Sling; terminating", null);
- Main.this.terminateVM(1);
- }
-
- } else {
-
- Main.info(
- "Restarting Framework with update from " + updateFile, null);
- boolean started = false;
- try {
- started = Main.this.doStart(updateFile.toURI().toURL());
- } catch (MalformedURLException mue) {
- Main.error("Cannot get URL for file " + updateFile, mue);
- } finally {
- updateFile.delete();
- }
-
- if (!started) {
- Main.error("Failed to restart Sling; terminating", null);
- Main.this.terminateVM(1);
- }
- }
- }
- }
-
}
diff --git a/src/test/java/org/apache/sling/launchpad/app/MainTest.java b/src/test/java/org/apache/sling/launchpad/app/MainTest.java
index 440ee89..58d778e 100644
--- a/src/test/java/org/apache/sling/launchpad/app/MainTest.java
+++ b/src/test/java/org/apache/sling/launchpad/app/MainTest.java
@@ -299,7 +299,7 @@ public class MainTest extends TestCase {
public void test_installShutdownHook() throws SecurityException, NoSuchMethodException, IllegalArgumentException,
IllegalAccessException, InvocationTargetException {
- final Method m = Main.class.getDeclaredMethod("installShutdownHook", Map.class);
+ final Method m = AbstractMain.class.getDeclaredMethod("installShutdownHook", Map.class);
m.setAccessible(true);
final String key = "sling.shutdown.hook";