Save This Page
Home » org.apache.sling.launchpad.base-2.2.0-source-release » org.apache.sling.launchpad.app » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    *
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    *
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   package org.apache.sling.launchpad.app;
   18   
   19   import java.io.File;
   20   import java.io.IOException;
   21   import java.io.PrintStream;
   22   import java.net.MalformedURLException;
   23   import java.net.URL;
   24   import java.text.DateFormat;
   25   import java.text.SimpleDateFormat;
   26   import java.util.Date;
   27   import java.util.HashMap;
   28   import java.util.Map;
   29   
   30   import org.apache.sling.launchpad.base.shared.Launcher;
   31   import org.apache.sling.launchpad.base.shared.Loader;
   32   import org.apache.sling.launchpad.base.shared.Notifiable;
   33   import org.apache.sling.launchpad.base.shared.SharedConstants;
   34   
   35   /**
   36    * The <code>Main</code> is the externally visible Standalone Java Application
   37    * launcher for Sling. Please refer to the full description <i>The Sling
   38    * Launchpad</i> on the Sling Wiki for a full description of this class.
   39    * <p>
   40    * Logging goes to standard output for informational messages and to standard
   41    * error for error messages.
   42    * <p>
   43    * This class goes into the secondary artifact with the classifier <i>app</i> to
   44    * be used as the main class when starting the Java Application.
   45    *
   46    * @see <a href="http://cwiki.apache.org/SLING/the-sling-launchpad.html">The
   47    *      Sling Launchpad</a>
   48    */
   49   public class Main extends Thread implements Notifiable {
   50   
   51       // The name of the environment variable to consult to find out
   52       // about sling.home
   53       private static final String ENV_SLING_HOME = "SLING_HOME";
   54   
   55       public static void main(String[] args) {
   56           new Main(args);
   57       }
   58   
   59       private final Map<String, String> commandLineArgs;
   60   
   61       private final String slingHome;
   62   
   63       private final Loader loader;
   64   
   65       private Launcher sling;
   66   
   67       private Main(String[] args) {
   68   
   69           // set the thread name
   70           super("Sling Terminator");
   71   
   72           this.commandLineArgs = parseCommandLine(args);
   73   
   74           // support usage first
   75           doHelp();
   76   
   77           // check for control commands (might exit)
   78           doControlCommand();
   79   
   80           // sling.home from the command line or system properties, else default
   81           this.slingHome = getSlingHome(commandLineArgs);
   82           File slingHomeFile = new File(slingHome);
   83           if (slingHomeFile.isAbsolute()) {
   84               info("Starting Sling in " + slingHome, null);
   85           } else {
   86               info("Starting Sling in " + slingHome + " ("
   87                   + slingHomeFile.getAbsolutePath() + ")", null);
   88           }
   89   
   90           // The Loader helper
   91           Loader loaderTmp = null;
   92           try {
   93               loaderTmp = new Loader(slingHome) {
   94                   @Override
   95                   protected void info(String msg) {
   96                       Main.info(msg, null);
   97                   }
   98               };
   99           } catch (IllegalArgumentException iae) {
  100               startupFailure(iae.getMessage(), null);
  101           }
  102           this.loader = loaderTmp;
  103   
  104           Runtime.getRuntime().addShutdownHook(this);
  105   
  106           // ensure up-to-date launcher jar
  107           startSling(getClass().getResource(
  108               SharedConstants.DEFAULT_SLING_LAUNCHER_JAR));
  109       }
  110   
  111       // ---------- Shutdown support for control listener and shutdown hook
  112   
  113       void shutdown() {
  114           // remove the shutdown hook, will fail if called from the
  115           // shutdown hook itself. Otherwise this prevents shutdown
  116           // from being called again
  117           try {
  118               Runtime.getRuntime().removeShutdownHook(this);
  119           } catch (Throwable t) {
  120               // don't care for problems removing the hook
  121           }
  122   
  123           // now really shutdown sling
  124           if (sling != null) {
  125               info("Stopping Sling", null);
  126               sling.stop();
  127           }
  128       }
  129   
  130       // ---------- Notifiable interface
  131   
  132       /**
  133        * The framework has been stopped by calling the <code>Bundle.stop()</code>
  134        * on the system bundle. This actually terminates the Sling Standalone
  135        * application.
  136        */
  137       public void stopped() {
  138           /**
  139            * This method is called if the framework is stopped from within by
  140            * calling stop on the system bundle or if the framework is stopped
  141            * because the VM is going down and the shutdown hook has initated the
  142            * shutdown In any case we ensure the reference to the framework is
  143            * removed and remove the shutdown hook (but don't care if that fails).
  144            */
  145   
  146           info("Sling has been stopped", null);
  147   
  148           // clear the reference to the framework
  149           sling = null;
  150   
  151           // remove the shutdown hook, the framework has terminated and
  152           // we do not need to do anything else
  153           try {
  154               Runtime.getRuntime().removeShutdownHook(this);
  155           } catch (Throwable t) {
  156               // don't care for problems removing the hook
  157           }
  158       }
  159   
  160       /**
  161        * The framework has been stopped with the intent to be restarted by calling
  162        * either of the <code>Bundle.update</code> methods on the system bundle.
  163        * <p>
  164        * If an <code>InputStream</code> was provided, this has been copied to a
  165        * temporary file, which will be used in place of the existing launcher jar
  166        * file.
  167        *
  168        * @param updateFile The temporary file to replace the existing launcher jar
  169        *            file. If <code>null</code> the existing launcher jar will be
  170        *            used again.
  171        */
  172       public void updated(File updateFile) {
  173   
  174           // clear the reference to the framework
  175           sling = null;
  176   
  177           // ensure we have a VM as clean as possible
  178           loader.cleanupVM();
  179   
  180           if (updateFile == null) {
  181   
  182               info("Restarting Framework and Sling", null);
  183               startSling(null);
  184   
  185           } else {
  186   
  187               info("Restarting Framework with update from " + updateFile, null);
  188               try {
  189                   startSling(updateFile.toURI().toURL());
  190               } catch (MalformedURLException mue) {
  191                   error("Cannot get URL for file " + updateFile, mue);
  192               } finally {
  193                   updateFile.delete();
  194               }
  195   
  196           }
  197       }
  198   
  199       // --------- Thread
  200   
  201       /**
  202        * Called when the Java VM is being terminiated, for example because the
  203        * KILL signal has been sent to the process. This method calls stop on the
  204        * launched Sling instance to terminate the framework before returning.
  205        */
  206       @Override
  207       public void run() {
  208           info("Java VM is shutting down", null);
  209           shutdown();
  210       }
  211   
  212       // ---------- internal
  213   
  214       private void startSling(URL launcherJar) {
  215           if (launcherJar != null) {
  216               try {
  217                   info("Checking launcher JAR in folder " + slingHome, null);
  218                   loader.installLauncherJar(launcherJar);
  219               } catch (IOException ioe) {
  220                   startupFailure("Failed installing " + launcherJar, ioe);
  221               }
  222           } else {
  223               info("No Launcher JAR to install", null);
  224           }
  225   
  226           Object object = null;
  227           try {
  228               object = loader.loadLauncher(SharedConstants.DEFAULT_SLING_MAIN);
  229           } catch (IllegalArgumentException iae) {
  230               startupFailure("Failed loading Sling class "
  231                   + SharedConstants.DEFAULT_SLING_MAIN, iae);
  232           }
  233   
  234           if (object instanceof Launcher) {
  235   
  236               // configure the launcher
  237               Launcher sling = (Launcher) object;
  238               sling.setNotifiable(this);
  239               sling.setCommandLine(commandLineArgs);
  240               sling.setSlingHome(slingHome);
  241   
  242               // launch it
  243               info("Starting launcher ...", null);
  244               if (sling.start()) {
  245                   info("Startup completed", null);
  246                   this.sling = sling;
  247               } else {
  248                   error("There was a problem launching Sling", null);
  249               }
  250           }
  251       }
  252   
  253       /**
  254        * Parses the command line arguments into a map of strings indexed by
  255        * strings. This method suppports single character option names only at the
  256        * moment. Each pair of an option name and its value is stored into the
  257        * map. If a single dash '-' character is encountered the rest of the command
  258        * line are interpreted as option names and are stored in the map unmodified
  259        * as entries with the same key and value.
  260        * <table>
  261        * <tr><th>Command Line</th><th>Mapping</th></tr>
  262        * <tr><td>x</td><td>x -> x</td></tr>
  263        * <tr><td>-y z</td><td>y -> z</td></tr>
  264        * <tr><td>-yz</td><td>y -> z</td></tr>
  265        * <tr><td>-y -z</td><td>y -> y, z -> z</td></tr>
  266        * <tr><td>-y x - -z a</td><td>y -> x, -z -> -z, a -> a</td></tr>
  267        * </table>
  268        *
  269        * @param args The command line to parse
  270        *
  271        * @return The map of command line options and their values
  272        */
  273       static Map<String, String> parseCommandLine(String[] args) {
  274           Map<String, String> commandLine = new HashMap<String, String>();
  275           boolean readUnparsed = false;
  276           for (int argc = 0; args != null && argc < args.length; argc++) {
  277               String arg = args[argc];
  278   
  279               if (readUnparsed) {
  280                   commandLine.put(arg, arg);
  281               } else if (arg.startsWith("-")) {
  282                   if (arg.length() == 1) {
  283                      readUnparsed = true;
  284                   } else {
  285                       String key = String.valueOf(arg.charAt(1));
  286                       if (arg.length() > 2) {
  287                           commandLine.put(key, arg.substring(2));
  288                       } else {
  289                           argc++;
  290                           if (argc < args.length
  291                               && (args[argc].equals("-") || !args[argc].startsWith("-"))) {
  292                               commandLine.put(key, args[argc]);
  293                           } else {
  294                               commandLine.put(key, key);
  295                               argc--;
  296                           }
  297                       }
  298                   }
  299               } else {
  300                   commandLine.put(arg, arg);
  301               }
  302           }
  303           return commandLine;
  304       }
  305   
  306       /**
  307        * Define the sling.home parameter implementing the algorithme defined on
  308        * the wiki page to find the setting according to this algorithm:
  309        * <ol>
  310        * <li>Command line option <code>-c</code></li>
  311        * <li>System property <code>sling.home</code></li>
  312        * <li>Environment variable <code>SLING_HOME</code></li>
  313        * <li>Default value <code>sling</code></li>
  314        * </ol>
  315        *
  316        * @param args The command line arguments
  317        * @return The value to use for sling.home
  318        */
  319       private static String getSlingHome(Map<String, String> commandLine) {
  320           String source = null;
  321   
  322           String slingHome = commandLine.get("c");
  323           if (slingHome != null) {
  324   
  325               source = "command line";
  326   
  327           } else {
  328   
  329               slingHome = System.getProperty(SharedConstants.SLING_HOME);
  330               if (slingHome != null) {
  331   
  332                   source = "system property sling.home";
  333   
  334               } else {
  335   
  336                   slingHome = System.getenv(ENV_SLING_HOME);
  337                   if (slingHome != null) {
  338   
  339                       source = "environment variable SLING_HOME";
  340   
  341                   } else {
  342   
  343                       source = "default";
  344                       slingHome = SharedConstants.SLING_HOME_DEFAULT;
  345   
  346                   }
  347               }
  348           }
  349   
  350           info("Setting sling.home=" + slingHome + " (" + source + ")", null);
  351           return slingHome;
  352       }
  353   
  354       private void startupFailure(String message, Throwable cause) {
  355           error("Launcher JAR access failure: " + message, cause);
  356           error("Shutting Down", null);
  357           System.exit(1);
  358       }
  359   
  360       // ---------- logging
  361   
  362       // emit an informational message to standard out
  363       static void info(String message, Throwable t) {
  364           log(System.out, "*INFO*", message, t);
  365       }
  366   
  367       // emit an error message to standard err
  368       static void error(String message, Throwable t) {
  369           log(System.err, "*ERROR*", message, t);
  370       }
  371   
  372       private static final DateFormat fmt = new SimpleDateFormat("dd.MM.yyyy HH:mm:ss.SSS ");
  373   
  374       // helper method to format the message on the correct output channel
  375       // the throwable if not-null is also prefixed line by line with the prefix
  376       private static void log(PrintStream out, String prefix, String message,
  377               Throwable t) {
  378   
  379           final StringBuilder linePrefixBuilder = new StringBuilder();
  380           synchronized (fmt) {
  381               linePrefixBuilder.append(fmt.format(new Date()));
  382           }
  383           linePrefixBuilder.append(prefix);
  384           linePrefixBuilder.append(" [");
  385           linePrefixBuilder.append(Thread.currentThread().getName());
  386           linePrefixBuilder.append("] ");
  387           final String linePrefix = linePrefixBuilder.toString();
  388   
  389           out.print(linePrefix);
  390           out.println(message);
  391           if (t != null) {
  392               t.printStackTrace(new PrintStream(out) {
  393                   @Override
  394                   public void println(String x) {
  395                       synchronized (this) {
  396                           print(linePrefix);
  397                           super.println(x);
  398                           flush();
  399                       }
  400                   }
  401               });
  402           }
  403       }
  404   
  405       /** prints a simple usage plus optional error message and exists */
  406       private void doHelp() {
  407           if (commandLineArgs.remove("h") != null) {
  408               System.out.println("usage: "
  409                   + Main.class.getName()
  410                   + " [ start | stop | status ] [ -j adr ] [ -l loglevel ] [ -f logfile ] [ -c slinghome ] [ -a address ] [ -p port ] [ -h ]");
  411   
  412               System.out.println("    start         listen for control connection (uses -j)");
  413               System.out.println("    stop          terminate running Sling (uses -j)");
  414               System.out.println("    status        check whether Sling is running (uses -j)");
  415               System.out.println("    -j adr        host and port to use for control connection in the format '[host:]port' (default localhost:63000)");
  416               System.out.println("    -l loglevel   the initial loglevel (0..4, FATAL, ERROR, WARN, INFO, DEBUG)");
  417               System.out.println("    -f logfile    the log file, \"-\" for stdout (default logs/error.log)");
  418               System.out.println("    -c slinghome  the sling context directory (default sling)");
  419               System.out.println("    -a address    the interfact to bind to (use 0.0.0.0 for any) (not supported yet)");
  420               System.out.println("    -p port       the port to listen to (default 8080)");
  421               System.out.println("    -h            prints this usage message");
  422   
  423               System.exit(0);
  424           }
  425       }
  426   
  427       private void doControlCommand() {
  428           String commandSocketSpec = commandLineArgs.remove("j");
  429           if ("j".equals(commandSocketSpec)) {
  430               commandSocketSpec = null;
  431           }
  432   
  433           ControlListener sl = new ControlListener(this, commandSocketSpec);
  434           if (commandLineArgs.remove(ControlListener.COMMAND_STOP) != null) {
  435               sl.shutdownServer();
  436               System.exit(0);
  437           } else if (commandLineArgs.remove(ControlListener.COMMAND_STATUS) != null) {
  438               sl.statusServer();
  439               System.exit(0);
  440           } else if (commandLineArgs.remove("start") != null) {
  441               sl.listen();
  442           }
  443       }
  444   }

Save This Page
Home » org.apache.sling.launchpad.base-2.2.0-source-release » org.apache.sling.launchpad.app » [javadoc | source]