Author: fmeschbe Date: Mon Jan 19 06:37:41 2009 New Revision: 735715 URL: http://svn.apache.org/viewvc?rev=735715&view=rev Log: Documentation update
Modified: incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/webapp/SlingServlet.java Modified: incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java?rev=735715&r1=735714&r2=735715&view=diff ============================================================================== --- incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java (original) +++ incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/app/Main.java Mon Jan 19 06:37:41 2009 @@ -32,38 +32,18 @@ import org.apache.sling.launchpad.base.shared.SharedConstants; /** - * The <code>Main</code> class is a simple Java Application which interprests - * the command line and creates the {...@link Sling} launcher class and thus starts - * the OSGi framework. In addition a shutdown thread is registered to ensure - * proper shutdown on VM termination. + * The <code>Main</code> is the externally visible Standalone Java Application + * launcher for Sling. Please refer to the full description <i>The Sling + * Launchpad</i> on the Sling Wiki for a full description of this class. * <p> - * The supported command line options are: - * <dl> - * <dt>-l loglevel</dt> - * <dd>Sets the initial loglevel as an integer in the range 0 to 4 or as one of - * the well known level strings FATAL, ERROR, WARN, INFO or DEBUG. This option - * overwrites the <code>org.apache.sling.osg.log.level</code> setting the - * <code>sling.properties</code> file.</dd> - * <dt>-f logfile</dt> - * <dd>The log file, \"-\" for stdout (default logs/error.log). This option - * overwrites the <code>org.apache.sling.osg.log.file</code> setting the - * <code>sling.properties</code> file.</dd> - * <dt>-c slinghome</dt> - * <dd>The directory in which Sling locates its initial configuration file - * <code>sling.properties</code> and where files of Sling itself such as the - * Apache Felix bundle archive or the JCR repository files are stored (default - * sling).</dd> - * <dt>-a address</dt> - * <dd>The interfact to bind to (use 0.0.0.0 for any). This option is not - * implemented yet.</dd> - * <dt>-p port</dt> - * <dd>The port to listen (default 8080) to handle HTTP requests. This option - * overwrites the <code>org.osgi.service.http.port</code> setting the - * <code>sling.properties</code> file.</dd> - * <dt>-h</dt> - * <dd>Prints a simple usage message listing all available command line options. - * </dd> - * </dl> + * 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://cwiki.apache.org/SLING/the-sling-launchpad.html">The + * Sling Launchpad</a> */ public class Main extends Thread implements Notifiable { @@ -100,50 +80,13 @@ SharedConstants.DEFAULT_SLING_LAUNCHER_JAR)); } - private void startSling(URL launcherJar) { - if (launcherJar != null) { - try { - info("Installing " + launcherJar + " to " + slingHome, null); - Loader.installLauncherJar(launcherJar, slingHome); - } catch (IOException ioe) { - error("Failed installing " + launcherJar, ioe); - } - } else { - info("No Launcher JAR to install", null); - } - - Object object; - try { - info( - "Loading launcher class " + SharedConstants.DEFAULT_SLING_MAIN, - null); - object = Loader.loadLauncher(SharedConstants.DEFAULT_SLING_MAIN, - slingHome); - } catch (IllegalArgumentException iae) { - error("Failed loading Sling class " - + SharedConstants.DEFAULT_SLING_MAIN, iae); - return; - } - - if (object instanceof Launcher) { - - // configure the launcher - Launcher sling = (Launcher) object; - sling.setNotifiable(this); - sling.setCommandLine(commandLineArgs); - sling.setSlingHome(slingHome); - - // launch it - info("Starting launcher ...", null); - if (sling.start()) { - info("Startup completed", null); - this.sling = sling; - } else { - error("There was a problem launching Sling", null); - } - } - } + // ---------- Notifiable interface + /** + * The framework has been stopped by calling the <code>Bundle.stop()</code> + * on the system bundle. This actually terminates the Sling Standalone + * application. + */ public void stopped() { /** * This method is called if the framework is stopped from within by @@ -167,6 +110,18 @@ } } + /** + * 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. + */ public void updated(File updateFile) { if (updateFile == null) { @@ -187,6 +142,14 @@ } } + // --------- Thread + + /** + * Called when the Java VM is being terminiated, for example because the + * KILL signal has been sent to the process. This method calls stop on the + * launched Sling instance to terminate the framework before returning. + */ + @Override public void run() { info("Java VM is shutting down", null); if (sling != null) { @@ -195,8 +158,67 @@ } } + // ---------- internal + + private void startSling(URL launcherJar) { + if (launcherJar != null) { + try { + info("Installing " + launcherJar + " to " + slingHome, null); + Loader.installLauncherJar(launcherJar, slingHome); + } catch (IOException ioe) { + error("Failed installing " + launcherJar, ioe); + } + } else { + info("No Launcher JAR to install", null); + } + + Object object; + try { + info( + "Loading launcher class " + SharedConstants.DEFAULT_SLING_MAIN, + null); + object = Loader.loadLauncher(SharedConstants.DEFAULT_SLING_MAIN, + slingHome); + } catch (IllegalArgumentException iae) { + error("Failed loading Sling class " + + SharedConstants.DEFAULT_SLING_MAIN, iae); + return; + } + + if (object instanceof Launcher) { + + // configure the launcher + Launcher sling = (Launcher) object; + sling.setNotifiable(this); + sling.setCommandLine(commandLineArgs); + sling.setSlingHome(slingHome); + + // launch it + info("Starting launcher ...", null); + if (sling.start()) { + info("Startup completed", null); + this.sling = sling; + } else { + error("There was a problem launching Sling", null); + } + } + } + + /** + * Define the sling.home parameter implementing the algorithme defined on + * the wiki page to find the setting according to this algorithm: + * <ol> + * <li>Command line option <code>-c</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 args The command line arguments + * @return The value to use for sling.home + */ private static String getSlingHome(String[] args) { - String message = null; + String source = null; String slingHome = null; for (int argc = 0; argc < args.length; argc++) { @@ -205,7 +227,7 @@ && arg.charAt(1) == 'c') { argc++; if (argc < args.length) { - message = "command line"; + source = "command line"; slingHome = args[argc]; } break; @@ -215,34 +237,36 @@ if (slingHome == null) { slingHome = System.getProperty(SharedConstants.SLING_HOME); if (slingHome != null) { - message = "system property sling.home"; + source = "system property sling.home"; } else { slingHome = System.getenv(ENV_SLING_HOME); if (slingHome != null) { - message = "environment variable SLING_HOME"; + source = "environment variable SLING_HOME"; } else { - message = "default"; + source = "default"; slingHome = SharedConstants.SLING_HOME_DEFAULT; } } } - info("Setting sling.home=" + slingHome + " (" + message + ")", null); + info("Setting sling.home=" + slingHome + " (" + source + ")", null); return slingHome; } + // ---------- logging + + // emit an informational message to standard out private static void info(String message, Throwable t) { log(System.out, "INF: ", message, t); } - private static void warn(String message, Throwable t) { - log(System.out, "WRN: ", message, t); - } - + // emit an error message to standard err private static void error(String message, Throwable t) { log(System.err, "ERR: ", message, t); } + // 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) { out.print(prefix); Modified: incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/webapp/SlingServlet.java URL: http://svn.apache.org/viewvc/incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/webapp/SlingServlet.java?rev=735715&r1=735714&r2=735715&view=diff ============================================================================== --- incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/webapp/SlingServlet.java (original) +++ incubator/sling/whiteboard/fmeschbe/launchpad/base/src/main/java/org/apache/sling/launchpad/webapp/SlingServlet.java Mon Jan 19 06:37:41 2009 @@ -36,6 +36,19 @@ import org.apache.sling.launchpad.base.shared.Notifiable; import org.apache.sling.launchpad.base.shared.SharedConstants; +/** + * The <code>SlingServlet</code> is the externally visible Web Application + * launcher for Sling. Please refer to the full description <i>The Sling + * Launchpad</i> on the Sling Wiki for a full description of this class. + * <p> + * Logging goes to ServletContext.log methods. + * <p> + * This class goes into the secondary artifact with the classifier <i>webapp</i> + * to be used as the main servlet to be registered in the servlet container. + * + * @see <a href="http://cwiki.apache.org/SLING/the-sling-launchpad.html">The + * Sling Launchpad</a> + */ public class SlingServlet extends GenericServlet implements Notifiable { /** @@ -62,7 +75,15 @@ */ private int startFailureCounter = 0; - public void init() throws ServletException { + // ---------- GenericServlet + + /** + * Launches the SLing framework if the sling.home setting can be derived + * from the configuration or the SerlvetContext. Otherwise Sling is not + * started yet and will be started when the first request comes in. + */ + @Override + public void init() { slingHome = getSlingHome(null); if (slingHome != null) { @@ -74,6 +95,7 @@ log("Servlet " + getServletName() + " initialized"); } + @Override public String getServletInfo() { if (sling != null) { return sling.getServletInfo(); @@ -82,6 +104,19 @@ return "Sling Launchpad Proxy"; } + /** + * If Sling has already been started, the request is forwarded to the + * started Sling framework. Otherwise the Sling framework is started unless + * there were too many startup failures. + * <p> + * If the request is not forwarded to Sling, this method returns a 404/NOT + * FOUND if the startup failure counter has exceeded or 503/SERVICE + * UNAVAILABLE if the Sling framework is starting up. + * <p> + * If a request causes the framework to start, it is immediately terminated + * with said response status and framework is started in a separate thread. + */ + @Override public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException { @@ -106,14 +141,87 @@ } } + /** + * Stop the Sling framework when the web application is being stopped + */ + @Override public void destroy() { if (sling != null) { sling.destroy(); } } - // ----- + // ---------- Notifiable interface + + /** + * The framework has been stopped by calling the <code>Bundle.stop()</code> + * on the system bundle. This actually terminates the Sling Standalone + * application. + * <p> + * Note, that a new request coming in while the web application is still + * running, will actually cause Sling to restart ! + */ + 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 initated 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). + */ + + log("Sling has been stopped"); + + // clear the reference to the framework + sling = null; + } + + /** + * 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. + */ + public void updated(File updateFile) { + + // drop the sling reference to be able to restart + synchronized (this) { + if (startingSling == null) { + sling = null; + } + } + + if (updateFile == null) { + + log("Restarting Framework and Sling"); + startSling((URL) null); + + } else { + + log("Restarting Framework with update from " + updateFile); + try { + startSling(updateFile.toURI().toURL()); + } catch (MalformedURLException mue) { + log("Cannot get URL for file " + updateFile, mue); + } finally { + updateFile.delete(); + } + + } + } + + // --------- internal + /** + * If Sling is not currently starting up, a thread is started to start Sling + * in the background. + */ private void startSling(final ServletRequest request) { if (startingSling == null) { slingHome = getSlingHome((HttpServletRequest) request); @@ -130,6 +238,10 @@ } } + /** + * Called from the startup thread initiated by a request or from + * {...@link #init()} to install the launcher jar and actually start sling. + */ private void startSling() { try { URL launcherJar = getServletContext().getResource( @@ -146,6 +258,10 @@ } } + /** + * Installs the launcher jar from the given URL (if not <code>null</code>) + * and launches Sling from that launcher. + */ private void startSling(URL launcherJar) { synchronized (this) { if (sling != null) { @@ -161,15 +277,20 @@ if (launcherJar != null) { try { + log("Installing " + launcherJar + " to " + slingHome); Loader.installLauncherJar(launcherJar, slingHome); } catch (IOException ioe) { - log("Failed installing the launcher JAR " + launcherJar, ioe); + log("Failed installing " + launcherJar, ioe); } + } else { + log("No Launcher JAR to install"); } Object object = Loader.loadLauncher( SharedConstants.DEFAULT_SLING_SERVLET, slingHome); try { + log("Loading launcher class " + + SharedConstants.DEFAULT_SLING_SERVLET); object = Loader.loadLauncher(SharedConstants.DEFAULT_SLING_SERVLET, slingHome); } catch (IllegalArgumentException iae) { @@ -188,9 +309,11 @@ } try { + log("Starting launcher ..."); sling.init(getServletConfig()); this.sling = sling; - + this.startFailureCounter = 0; + log("Startup completed"); } catch (ServletException se) { Throwable cause = se.getCause(); if (cause == null) { @@ -208,66 +331,53 @@ } } - 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 initated 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). - */ - - System.out.println("Sling has been stopped"); - - // clear the reference to the framework - sling = null; - } - - public void updated(File updateFile) { - - // drop the sling reference to be able to restart - synchronized (this) { - if (startingSling == null) { - sling = null; - } - } - - if (updateFile == null) { - - System.out.println("Restarting ...."); - startSling((URL) null); - - } else { - - System.out.println("Sling has been updated with " + updateFile); - try { - startSling(updateFile.toURL()); - } catch (MalformedURLException mue) { - // TODO: Shout ! - } finally { - updateFile.delete(); - } - - } - } + /** + * Define the sling.home parameter implementing the algorithme defined on + * the wiki page to find the setting according to this algorithm: + * <ol> + * <li>Servlet parameter <code>sling.home</code></li> + * <li>Context <code>sling.home</code></li> + * <li>Derived from ServletContext path</li> + * </ol> + * <p> + * <code>null</code> may be returned by this method if no + * <code>sling.home</code> parameter is set and if the servlet container + * does not provide the Servlet API 2.5 + * <code>ServletContext.getContextPath()</code> method and the + * <code>request</code> parameter is <code>null</code>. + * + * @param args The command line arguments + * @return The value to use for sling.home or <code>null</code> if the value + * cannot be retrieved. + */ + private String getSlingHome(HttpServletRequest request) { - protected String getSlingHome(HttpServletRequest request) { + String source = null; // 1. servlet config parameter String slingHome = getServletConfig().getInitParameter( SharedConstants.SLING_HOME); - if (slingHome == null) { + if (slingHome != null) { + + source = "servlet parameter sling.home"; + + } else { // 2. servlet context parameter slingHome = getServletContext().getInitParameter( SharedConstants.SLING_HOME); - if (slingHome == null) { + if (slingHome != null) { + + source = "servlet context parameter sling.home"; + + } else { // 3. servlet context path (Servlet API 2.5 and later) try { String contextPath = getServletContext().getContextPath(); slingHome = toSlingHome(contextPath); + source = "servlet context path"; } catch (NoSuchMethodError nsme) { @@ -276,10 +386,12 @@ String contextPath = request.getContextPath(); slingHome = toSlingHome(contextPath); + source = "servlet context path (from request)"; } else { - // TODO: log < 2.5 API and require request for this + log("ServletContext path not available here, delaying startup until first request"); + return null; } } @@ -287,10 +399,12 @@ } } + log("Setting sling.home=" + slingHome + " (" + source + ")"); return slingHome; } - protected String toSlingHome(String contextPath) { + // convert the servlet context path to a directory path for sling.home + private String toSlingHome(String contextPath) { String prefix = "sling/"; if (contextPath == null || contextPath.length() == 0) { return prefix + "_";