Save This Page
Home » org.apache.sling.launchpad.base-2.2.0-source-release » org.apache.sling.launchpad.webapp » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one
    3    * or more contributor license agreements.  See the NOTICE file
    4    * distributed with this work for additional information
    5    * regarding copyright ownership.  The ASF licenses this file
    6    * to you under the Apache License, Version 2.0 (the
    7    * "License"); you may not use this file except in compliance
    8    * with the License.  You may obtain a copy of the License at
    9    *
   10    *   http://www.apache.org/licenses/LICENSE-2.0
   11    *
   12    * Unless required by applicable law or agreed to in writing,
   13    * software distributed under the License is distributed on an
   14    * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
   15    * KIND, either express or implied.  See the License for the
   16    * specific language governing permissions and limitations
   17    * under the License.
   18    */
   19   package org.apache.sling.launchpad.webapp;
   20   
   21   import java.io.File;
   22   import java.io.IOException;
   23   import java.net.MalformedURLException;
   24   import java.net.URL;
   25   
   26   import javax.servlet.GenericServlet;
   27   import javax.servlet.Servlet;
   28   import javax.servlet.ServletException;
   29   import javax.servlet.ServletRequest;
   30   import javax.servlet.ServletResponse;
   31   import javax.servlet.http.HttpServletRequest;
   32   import javax.servlet.http.HttpServletResponse;
   33   
   34   import org.apache.sling.launchpad.base.shared.Launcher;
   35   import org.apache.sling.launchpad.base.shared.Loader;
   36   import org.apache.sling.launchpad.base.shared.Notifiable;
   37   import org.apache.sling.launchpad.base.shared.SharedConstants;
   38   
   39   /**
   40    * The <code>SlingServlet</code> is the externally visible Web Application
   41    * launcher for Sling. Please refer to the full description <i>The Sling
   42    * Launchpad</i> on the Sling Wiki for a full description of this class.
   43    * <p>
   44    * Logging goes to ServletContext.log methods.
   45    * <p>
   46    * This class goes into the secondary artifact with the classifier <i>webapp</i>
   47    * to be used as the main servlet to be registered in the servlet container.
   48    *
   49    * @see <a href="http://cwiki.apache.org/SLING/the-sling-launchpad.html">The
   50    *      Sling Launchpad</a>
   51    */
   52   @SuppressWarnings("serial")
   53   public class SlingServlet extends GenericServlet implements Notifiable {
   54   
   55       /**
   56        * The number times Sling will be tried to be started before giving up
   57        * (value is 20). This number is chosen deliberately as generally Sling
   58        * should start up smoothly. Whether any bundles within Sling start or not
   59        * is not counted here.
   60        */
   61       private static final int MAX_START_FAILURES = 20;
   62   
   63       private String slingHome;
   64   
   65       private Loader loader;
   66   
   67       private Servlet sling;
   68   
   69       /**
   70        * Field managed by the {@link #startSling(String)} method to indicate
   71        * whether Sling is in the process of being started.
   72        */
   73       private Thread startingSling;
   74   
   75       /**
   76        * Counter to count the number of failed startups. After this number
   77        * expires, the SlingServlet will not try to start Sling any more.
   78        */
   79       private int startFailureCounter = 0;
   80   
   81       // ---------- GenericServlet
   82   
   83       /**
   84        * Launches the SLing framework if the sling.home setting can be derived
   85        * from the configuration or the SerlvetContext. Otherwise Sling is not
   86        * started yet and will be started when the first request comes in.
   87        */
   88       @Override
   89       public void init() {
   90   
   91           slingHome = getSlingHome(null);
   92           if (slingHome != null) {
   93               startSling();
   94           } else {
   95               log("Sling cannot be started yet, because sling.home is not defined yet");
   96           }
   97   
   98           log("Servlet " + getServletName() + " initialized");
   99       }
  100   
  101       @Override
  102       public String getServletInfo() {
  103           if (sling != null) {
  104               return sling.getServletInfo();
  105           }
  106   
  107           return "Sling Launchpad Proxy";
  108       }
  109   
  110       /**
  111        * If Sling has already been started, the request is forwarded to the
  112        * started Sling framework. Otherwise the Sling framework is started unless
  113        * there were too many startup failures.
  114        * <p>
  115        * If the request is not forwarded to Sling, this method returns a 404/NOT
  116        * FOUND if the startup failure counter has exceeded or 503/SERVICE
  117        * UNAVAILABLE if the Sling framework is starting up.
  118        * <p>
  119        * If a request causes the framework to start, it is immediately terminated
  120        * with said response status and framework is started in a separate thread.
  121        */
  122       @Override
  123       public void service(ServletRequest req, ServletResponse res)
  124               throws ServletException, IOException {
  125   
  126           // delegate the request to the registered delegatee servlet
  127           Servlet delegatee = sling;
  128           if (delegatee != null) {
  129   
  130               delegatee.service(req, res);
  131   
  132           } else if (startFailureCounter > MAX_START_FAILURES) {
  133   
  134               // too many startup retries, fail for ever
  135               ((HttpServletResponse) res).sendError(HttpServletResponse.SC_NOT_FOUND);
  136   
  137           } else {
  138   
  139               startSling(req);
  140   
  141               ((HttpServletResponse) res).sendError(
  142                   HttpServletResponse.SC_SERVICE_UNAVAILABLE,
  143                   "Sling is currently starting up, please try again");
  144           }
  145       }
  146   
  147       /**
  148        * Stop the Sling framework when the web application is being stopped
  149        */
  150       @Override
  151       public void destroy() {
  152           if (sling != null) {
  153               sling.destroy();
  154           }
  155   
  156           // clear fields
  157           slingHome = null;
  158           loader = null;
  159           sling = null;
  160       }
  161   
  162       // ---------- Notifiable interface
  163   
  164       /**
  165        * The framework has been stopped by calling the <code>Bundle.stop()</code>
  166        * on the system bundle. This actually terminates the Sling Standalone
  167        * application.
  168        * <p>
  169        * Note, that a new request coming in while the web application is still
  170        * running, will actually cause Sling to restart !
  171        */
  172       public void stopped() {
  173           /**
  174            * This method is called if the framework is stopped from within by
  175            * calling stop on the system bundle or if the framework is stopped
  176            * because the VM is going down and the shutdown hook has initated the
  177            * shutdown In any case we ensure the reference to the framework is
  178            * removed and remove the shutdown hook (but don't care if that fails).
  179            */
  180   
  181           log("Sling has been stopped");
  182   
  183           // clear the reference to the framework
  184           sling = null;
  185       }
  186   
  187       /**
  188        * The framework has been stopped with the intent to be restarted by calling
  189        * either of the <code>Bundle.update</code> methods on the system bundle.
  190        * <p>
  191        * If an <code>InputStream</code> was provided, this has been copied to a
  192        * temporary file, which will be used in place of the existing launcher jar
  193        * file.
  194        *
  195        * @param updateFile The temporary file to replace the existing launcher jar
  196        *            file. If <code>null</code> the existing launcher jar will be
  197        *            used again.
  198        */
  199       public void updated(File updateFile) {
  200   
  201           // drop the sling reference to be able to restart
  202           synchronized (this) {
  203               if (startingSling == null) {
  204                   sling = null;
  205               }
  206           }
  207   
  208           // ensure we have a VM as clean as possible
  209           loader.cleanupVM();
  210   
  211           if (updateFile == null) {
  212   
  213               log("Restarting Framework and Sling");
  214               startSling((URL) null);
  215   
  216           } else {
  217   
  218               log("Restarting Framework with update from " + updateFile);
  219               try {
  220                   startSling(updateFile.toURI().toURL());
  221               } catch (MalformedURLException mue) {
  222                   log("Cannot get URL for file " + updateFile, mue);
  223               } finally {
  224                   updateFile.delete();
  225               }
  226   
  227           }
  228       }
  229   
  230       // --------- internal
  231   
  232       /**
  233        * If Sling is not currently starting up, a thread is started to start Sling
  234        * in the background.
  235        */
  236       private void startSling(final ServletRequest request) {
  237           if (startingSling == null) {
  238               slingHome = getSlingHome((HttpServletRequest) request);
  239               Thread starter = new Thread("SlingStarter_"
  240                   + System.currentTimeMillis()) {
  241                   @Override
  242                   public void run() {
  243                       startSling();
  244                   }
  245               };
  246   
  247               starter.setDaemon(true);
  248               starter.start();
  249           }
  250       }
  251   
  252       /**
  253        * Called from the startup thread initiated by a request or from
  254        * {@link #init()} to install the launcher jar and actually start sling.
  255        */
  256       private void startSling() {
  257   
  258           try {
  259               this.loader = new Loader(slingHome) {
  260                   @Override
  261                   protected void info(String msg) {
  262                       log(msg);
  263                   }
  264               };
  265           } catch (IllegalArgumentException iae) {
  266               startupFailure(null, iae);
  267               return;
  268           }
  269   
  270           try {
  271               URL launcherJar = getServletContext().getResource(
  272                   SharedConstants.DEFAULT_SLING_LAUNCHER_JAR);
  273               if (launcherJar == null) {
  274                   launcherJar = getServletContext().getResource(
  275                       "/WEB-INF/" + SharedConstants.DEFAULT_SLING_LAUNCHER_JAR);
  276               }
  277   
  278               startSling(launcherJar);
  279           } catch (MalformedURLException mue) {
  280               log("Cannot load Sling Launcher JAR "
  281                   + SharedConstants.DEFAULT_SLING_LAUNCHER_JAR, mue);
  282           }
  283       }
  284   
  285       /**
  286        * Installs the launcher jar from the given URL (if not <code>null</code>)
  287        * and launches Sling from that launcher.
  288        */
  289       private void startSling(URL launcherJar) {
  290           synchronized (this) {
  291               if (sling != null) {
  292                   log("Sling already started, nothing to do");
  293                   return;
  294               } else if (startingSling != null) {
  295                   log("Sling being started by Thread " + startingSling);
  296                   return;
  297               }
  298   
  299               startingSling = Thread.currentThread();
  300           }
  301   
  302           if (launcherJar != null) {
  303               try {
  304                   log("Checking launcher JAR in " + slingHome);
  305                   loader.installLauncherJar(launcherJar);
  306               } catch (IOException ioe) {
  307                   startupFailure("Failed installing " + launcherJar, ioe);
  308                   return;
  309               }
  310           } else {
  311               log("No Launcher JAR to install");
  312           }
  313   
  314           Object object = null;
  315           try {
  316               object = loader.loadLauncher(SharedConstants.DEFAULT_SLING_SERVLET);
  317           } catch (IllegalArgumentException iae) {
  318               startupFailure("Cannot load Launcher Servlet "
  319                   + SharedConstants.DEFAULT_SLING_SERVLET, iae);
  320               return;
  321           }
  322   
  323           if (object instanceof Servlet) {
  324               Servlet sling = (Servlet) object;
  325   
  326               if (sling instanceof Launcher) {
  327                   Launcher slingLauncher = (Launcher) sling;
  328                   slingLauncher.setNotifiable(this);
  329                   slingLauncher.setSlingHome(slingHome);
  330               }
  331   
  332               try {
  333                   log("Starting launcher ...");
  334                   sling.init(getServletConfig());
  335                   this.sling = sling;
  336                   this.startFailureCounter = 0;
  337                   log("Startup completed");
  338               } catch (ServletException se) {
  339                   startupFailure(null, se);
  340               }
  341           }
  342   
  343           // reset the starting flag
  344           synchronized (this) {
  345               startingSling = null;
  346           }
  347       }
  348   
  349       /**
  350        * Define the sling.home parameter implementing the algorithme defined on
  351        * the wiki page to find the setting according to this algorithm:
  352        * <ol>
  353        * <li>Servlet parameter <code>sling.home</code></li>
  354        * <li>Context <code>sling.home</code></li>
  355        * <li>Derived from ServletContext path</li>
  356        * </ol>
  357        * <p>
  358        * <code>null</code> may be returned by this method if no
  359        * <code>sling.home</code> parameter is set and if the servlet container
  360        * does not provide the Servlet API 2.5
  361        * <code>ServletContext.getContextPath()</code> method and the
  362        * <code>request</code> parameter is <code>null</code>.
  363        *
  364        * @param args The command line arguments
  365        * @return The value to use for sling.home or <code>null</code> if the value
  366        *         cannot be retrieved.
  367        */
  368       private String getSlingHome(HttpServletRequest request) {
  369   
  370           String source = null;
  371   
  372           // 1. servlet config parameter
  373           String slingHome = getServletConfig().getInitParameter(
  374               SharedConstants.SLING_HOME);
  375           if (slingHome != null) {
  376   
  377               source = "servlet parameter sling.home";
  378   
  379           } else {
  380   
  381               // 2. servlet context parameter
  382               slingHome = getServletContext().getInitParameter(
  383                   SharedConstants.SLING_HOME);
  384               if (slingHome != null) {
  385   
  386                   source = "servlet context parameter sling.home";
  387   
  388               } else {
  389   
  390                   // 3. servlet context path (Servlet API 2.5 and later)
  391                   try {
  392   
  393                       String contextPath = getServletContext().getContextPath();
  394                       slingHome = toSlingHome(contextPath);
  395                       source = "servlet context path";
  396   
  397                   } catch (NoSuchMethodError nsme) {
  398   
  399                       // 4.servlet context path (Servlet API 2.4 and earlier)
  400                       if (request != null) {
  401   
  402                           String contextPath = request.getContextPath();
  403                           slingHome = toSlingHome(contextPath);
  404                           source = "servlet context path (from request)";
  405   
  406                       } else {
  407   
  408                           log("ServletContext path not available here, delaying startup until first request");
  409                           return null;
  410   
  411                       }
  412                   }
  413   
  414               }
  415           }
  416   
  417           log("Setting sling.home=" + slingHome + " (" + source + ")");
  418           return slingHome;
  419       }
  420   
  421       // convert the servlet context path to a directory path for sling.home
  422       private String toSlingHome(String contextPath) {
  423           String prefix = "sling/";
  424           if (contextPath == null || contextPath.length() == 0) {
  425               return prefix + "_";
  426           }
  427   
  428           return prefix + contextPath.replace('/', '_');
  429       }
  430   
  431       private void startupFailure(String message, Throwable cause) {
  432   
  433           // ensure message
  434           if (message == null) {
  435               message = "Failed to start Sling in " + slingHome;
  436           }
  437   
  438           // unwrap to get the real cause
  439           while (cause.getCause() != null) {
  440               cause = cause.getCause();
  441           }
  442   
  443           // log it now and increase the failure counter
  444           log(message, cause);
  445           startFailureCounter++;
  446   
  447           // ensure the startingSling fields is not set
  448           synchronized (this) {
  449               startingSling = null;
  450           }
  451       }
  452   }

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