View Javadoc

    1   //========================================================================
    2   //$Id: AbstractJettyMojo.java 4001 2008-11-06 07:54:23Z janb $
    3   //Copyright 2000-2004 Mort Bay Consulting Pty. Ltd.
    4   //------------------------------------------------------------------------
    5   //Licensed under the Apache License, Version 2.0 (the "License");
    6   //you may not use this file except in compliance with the License.
    7   //You may obtain a copy of the License at
    8   //http://www.apache.org/licenses/LICENSE-2.0
    9   //Unless required by applicable law or agreed to in writing, software
    10  //distributed under the License is distributed on an "AS IS" BASIS,
    11  //WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  //See the License for the specific language governing permissions and
    13  //limitations under the License.
    14  //========================================================================
    15
    16
    17  package org.mortbay.jetty.plugin;
    18
    19
    20  import java.io.File;
    21  import java.util.ArrayList;
    22  import java.util.Iterator;
    23  import java.util.List;
    24
    25  import org.apache.maven.plugin.AbstractMojo;
    26  import org.apache.maven.plugin.MojoExecutionException;
    27  import org.apache.maven.plugin.MojoFailureException;
    28  import org.apache.maven.project.MavenProject;
    29  import org.mortbay.jetty.Server;
    30  import org.mortbay.jetty.plugin.util.ConsoleScanner;
    31  import org.mortbay.jetty.plugin.util.JettyPluginServer;
    32  import org.mortbay.jetty.plugin.util.PluginLog;
    33  import org.mortbay.jetty.plugin.util.SystemProperties;
    34  import org.mortbay.jetty.plugin.util.SystemProperty;
    35  import org.mortbay.util.Scanner;
    36
    37
    38
    39  /**
    40   * AbstractJettyMojo
    41   *
    42   *
    43   */
    44  public abstract class AbstractJettyMojo extends AbstractMojo
    45  {
    46      /**
    47       * The proxy for the Server object
    48       */
    49      protected JettyPluginServer server;
    50
    51
    52      /**
    53       * The "virtual" webapp created by the plugin
    54       * @parameter
    55       */
    56      protected Jetty6PluginWebAppContext webAppConfig;
    57
    58
    59
    60      /**
    61       * The maven project.
    62       *
    63       * @parameter expression="${executedProject}"
    64       * @required
    65       * @readonly
    66       */
    67      protected MavenProject project;
    68
    69
    70
    71      /**
    72       * The context path for the webapp. Defaults to the
    73       * name of the webapp's artifact.
    74       *
    75       * @parameter expression="/${project.artifactId}"
    76       * @required
    77       */
    78      protected String contextPath;
    79
    80
    81      /**
    82       * The temporary directory to use for the webapp.
    83       * Defaults to target/jetty-tmp
    84       *
    85       * @parameter expression="${project.build.directory}/work"
    86       * @required
    87       */
    88      protected File tmpDirectory;
    89
    90
    91
    92      /**
    93       * A webdefault.xml file to use instead
    94       * of the default for the webapp. Optional.
    95       *
    96       * @parameter
    97       */
    98      protected File webDefaultXml;
    99
    100
    101     /**
    102      * A web.xml file to be applied AFTER
    103      * the webapp's web.xml file. Useful for
    104      * applying different build profiles, eg
    105      * test, production etc. Optional.
    106      * @parameter
    107      */
    108     protected File overrideWebXml;
    109
    110     /**
    111      * The interval in seconds to scan the webapp for changes
    112      * and restart the context if necessary. Ignored if reload
    113      * is enabled. Disabled by default.
    114      *
    115      * @parameter expression="${jetty.scanIntervalSeconds}" default-value="0"
    116      * @required
    117      */
    118     protected int scanIntervalSeconds;
    119
    120
    121     /**
    122      * reload can be set to either 'automatic' or 'manual'
    123      *
    124      * if 'manual' then the context can be reloaded by a linefeed in the console
    125      * if 'automatic' then traditional reloading on changed files is enabled.
    126      *
    127      * @parameter expression="${jetty.reload}" default-value="automatic"
    128      */
    129     protected String reload;
    130
    131
    132     /**
    133      * System properties to set before execution.
    134      * Note that these properties will NOT override System properties
    135      * that have been set on the command line or by the JVM. Optional.
    136      * @parameter
    137      */
    138     protected SystemProperties systemProperties;
    139
    140
    141
    142     /**
    143      * Location of a jetty xml configuration file whose contents
    144      * will be applied before any plugin configuration. Optional.
    145      * @parameter
    146      */
    147     protected File jettyConfig;
    148
    149     /**
    150      * Port to listen to stop jetty on executing -DSTOP.PORT=&lt;stopPort&gt;
    151      * -DSTOP.KEY=&lt;stopKey&gt; -jar start.jar --stop
    152      * @parameter
    153      */
    154     protected int stopPort;
    155
    156     /**
    157      * Key to provide when stopping jetty on executing java -DSTOP.KEY=&lt;stopKey&gt;
    158      * -DSTOP.PORT=&lt;stopPort&gt; -jar start.jar --stop
    159      * @parameter
    160      */
    161     protected String stopKey;
    162
    163     /**
    164  	 * <p>
    165  	 * Determines whether or not the server blocks when started. The default
    166  	 * behavior (daemon = false) will cause the server to pause other processes
    167  	 * while it continues to handle web requests. This is useful when starting the
    168  	 * server with the intent to work with it interactively.
    169  	 * </p><p>
    170  	 * Often, it is desirable to let the server start and continue running subsequent
    171  	 * processes in an automated build environment. This can be facilitated by setting
    172  	 * daemon to true.
    173  	 * </p>
    174  	 * @parameter expression="${jetty.daemon}" default-value="false"
    175  	 */
    176  	protected boolean daemon;
    177
    178     /**
    179      * A scanner to check for changes to the webapp
    180      */
    181     protected Scanner scanner;
    182
    183     /**
    184      *  List of files and directories to scan
    185      */
    186     protected ArrayList scanList;
    187
    188     /**
    189      * List of Listeners for the scanner
    190      */
    191     protected ArrayList scannerListeners;
    192
    193
    194     /**
    195      * A scanner to check ENTER hits on the console
    196      */
    197     protected Thread consoleScanner;
    198
    199
    200     public String PORT_SYSPROPERTY = "jetty.port";
    201
    202     /**
    203      * @return Returns the realms configured in the pom
    204      */
    205     public abstract Object[] getConfiguredUserRealms();
    206
    207     /**
    208      * @return Returns the connectors configured in the pom
    209      */
    210     public abstract Object[] getConfiguredConnectors();
    211
    212     public abstract Object getConfiguredRequestLog();
    213
    214
    215     public abstract void checkPomConfiguration() throws MojoExecutionException;
    216
    217
    218
    219     public abstract void configureScanner () throws MojoExecutionException;
    220
    221
    222     public abstract void applyJettyXml () throws Exception;
    223
    224
    225     /**
    226      * create a proxy that wraps a particular jetty version Server object
    227      * @return
    228      */
    229     public abstract JettyPluginServer createServer() throws Exception;
    230
    231
    232     public abstract void finishConfigurationBeforeStart() throws Exception;
    233
    234
    235     public MavenProject getProject()
    236     {
    237         return this.project;
    238     }
    239
    240     public File getTmpDirectory()
    241     {
    242         return this.tmpDirectory;
    243     }
    244
    245
    246     public File getWebDefaultXml()
    247     {
    248         return this.webDefaultXml;
    249     }
    250
    251     public File getOverrideWebXml()
    252     {
    253         return this.overrideWebXml;
    254     }
    255
    256     /**
    257      * @return Returns the contextPath.
    258      */
    259     public String getContextPath()
    260     {
    261         return this.contextPath;
    262     }
    263
    264     /**
    265      * @return Returns the scanIntervalSeconds.
    266      */
    267     public int getScanIntervalSeconds()
    268     {
    269         return this.scanIntervalSeconds;
    270     }
    271
    272
    273     public File getJettyXmlFile ()
    274     {
    275         return this.jettyConfig;
    276     }
    277
    278
    279     public JettyPluginServer getServer ()
    280     {
    281         return this.server;
    282     }
    283
    284     public void setServer (JettyPluginServer server)
    285     {
    286         this.server = server;
    287     }
    288
    289
    290     public void setScanList (ArrayList list)
    291     {
    292         this.scanList = new ArrayList(list);
    293     }
    294
    295     public ArrayList getScanList ()
    296     {
    297         return this.scanList;
    298     }
    299
    300
    301     public void setScannerListeners (ArrayList listeners)
    302     {
    303         this.scannerListeners = new ArrayList(listeners);
    304     }
    305
    306     public ArrayList getScannerListeners ()
    307     {
    308         return this.scannerListeners;
    309     }
    310
    311     public Scanner getScanner ()
    312     {
    313         return scanner;
    314     }
    315
    316     public void execute() throws MojoExecutionException, MojoFailureException
    317     {
    318         getLog().info("Configuring Jetty for project: " + getProject().getName());
    319         PluginLog.setLog(getLog());
    320         checkPomConfiguration();
    321         startJetty();
    322     }
    323
    324
    325     public void startJetty () throws MojoExecutionException
    326     {
    327         try
    328         {
    329             getLog().debug("Starting Jetty Server ...");
    330
    331             printSystemProperties();
    332             setServer(createServer());
    333
    334             //apply any config from a jetty.xml file first which is able to
    335             //be overwritten by config in the pom.xml
    336             applyJettyXml ();
    337
    338             JettyPluginServer plugin=getServer();
    339
    340
    341             // if the user hasn't configured their project's pom to use a
    342             // different set of connectors,
    343             // use the default
    344             Object[] configuredConnectors = getConfiguredConnectors();
    345
    346             plugin.setConnectors(configuredConnectors);
    347             Object[] connectors = plugin.getConnectors();
    348
    349             if (connectors == null|| connectors.length == 0)
    350             {
    351                 //if a SystemProperty -Djetty.port=<portnum> has been supplied, use that as the default port
    352                 configuredConnectors = new Object[] { plugin.createDefaultConnector(System.getProperty(PORT_SYSPROPERTY, null)) };
    353                 plugin.setConnectors(configuredConnectors);
    354             }
    355
    356
    357             //set up a RequestLog if one is provided
    358             if (getConfiguredRequestLog() != null)
    359                 getServer().setRequestLog(getConfiguredRequestLog());
    360
    361             //set up the webapp and any context provided
    362             getServer().configureHandlers();
    363             configureWebApplication();
    364             getServer().addWebApplication(webAppConfig);
    365
    366
    367             // set up security realms
    368             Object[] configuredRealms = getConfiguredUserRealms();
    369             for (int i = 0; (configuredRealms != null) && i < configuredRealms.length; i++)
    370                 getLog().debug(configuredRealms[i].getClass().getName() + ": "+ configuredRealms[i].toString());
    371
    372             plugin.setUserRealms(configuredRealms);
    373
    374             //do any other configuration required by the
    375             //particular Jetty version
    376             finishConfigurationBeforeStart();
    377
    378             // start Jetty
    379             server.start();
    380
    381             getLog().info("Started Jetty Server");
    382
    383             if(stopPort>0 && stopKey!=null)
    384             {
    385                 org.mortbay.jetty.plugin.util.Monitor monitor = new org.mortbay.jetty.plugin.util.Monitor(stopPort, stopKey, new Server[]{(Server)server.getProxiedObject()}, !daemon);
    386                 monitor.start();
    387             }
    388
    389             // start the scanner thread (if necessary) on the main webapp
    390             configureScanner ();
    391             startScanner();
    392
    393             // start the new line scanner thread if necessary
    394             startConsoleScanner();
    395
    396             // keep the thread going if not in daemon mode
    397             if (!daemon)
    398             {
    399                 server.join();
    400             }
    401         }
    402         catch (Exception e)
    403         {
    404             throw new MojoExecutionException("Failure", e);
    405         }
    406         finally
    407         {
    408             if (!daemon)
    409             {
    410                 getLog().info("Jetty server exiting.");
    411             }
    412         }
    413
    414     }
    415
    416
    417     public abstract void restartWebApp(boolean reconfigureScanner) throws Exception;
    418
    419     /**
    420      * Subclasses should invoke this to setup basic info
    421      * on the webapp
    422      *
    423      * @throws MojoExecutionException
    424      */
    425     public void configureWebApplication () throws Exception
    426     {
    427         //use EITHER a <webAppConfig> element or the now deprecated <contextPath>, <tmpDirectory>, <webDefaultXml>, <overrideWebXml>
    428         //way of doing things
    429         if (webAppConfig == null)
    430         {
    431             webAppConfig = new Jetty6PluginWebAppContext();
    432             webAppConfig.setContextPath((getContextPath().startsWith("/") ? getContextPath() : "/"+ getContextPath()));
    433             if (getTmpDirectory() != null)
    434                 webAppConfig.setTempDirectory(getTmpDirectory());
    435             if (getWebDefaultXml() != null)
    436                 webAppConfig.setDefaultsDescriptor(getWebDefaultXml().getCanonicalPath());
    437             if (getOverrideWebXml() != null)
    438                 webAppConfig.setOverrideDescriptor(getOverrideWebXml().getCanonicalPath());
    439         }
    440
    441
    442         getLog().info("Context path = " + webAppConfig.getContextPath());
    443         getLog().info("Tmp directory = "+ " determined at runtime");
    444         getLog().info("Web defaults = "+(webAppConfig.getDefaultsDescriptor()==null?" jetty default":webAppConfig.getDefaultsDescriptor()));
    445         getLog().info("Web overrides = "+(webAppConfig.getOverrideDescriptor()==null?" none":webAppConfig.getOverrideDescriptor()));
    446
    447     }
    448
    449     /**
    450      * Run a scanner thread on the given list of files and directories, calling
    451      * stop/start on the given list of LifeCycle objects if any of the watched
    452      * files change.
    453      *
    454      */
    455     private void startScanner()
    456     {
    457
    458         // check if scanning is enabled
    459         if (getScanIntervalSeconds() <= 0) return;
    460
    461         // check if reload is manual. It disables file scanning
    462         if ( "manual".equalsIgnoreCase( reload ) )
    463         {
    464             // issue a warning if both scanIntervalSeconds and reload
    465             // are enabled
    466             getLog().warn("scanIntervalSeconds is set to " + scanIntervalSeconds + " but will be IGNORED due to manual reloading");
    467             return;
    468         }
    469
    470         scanner = new Scanner();
    471         scanner.setReportExistingFilesOnStartup(false);
    472         scanner.setScanInterval(getScanIntervalSeconds());
    473         scanner.setScanDirs(getScanList());
    474         scanner.setRecursive(true);
    475         List listeners = getScannerListeners();
    476         Iterator itor = (listeners==null?null:listeners.iterator());
    477         while (itor!=null && itor.hasNext())
    478             scanner.addListener((Scanner.Listener)itor.next());
    479         getLog().info("Starting scanner at interval of " + getScanIntervalSeconds()+ " seconds.");
    480         scanner.start();
    481     }
    482
    483     /**
    484      * Run a thread that monitors the console input to detect ENTER hits.
    485      */
    486     protected void startConsoleScanner()
    487     {
    488         if ( "manual".equalsIgnoreCase( reload ) )
    489         {
    490             getLog().info("Console reloading is ENABLED. Hit ENTER on the console to restart the context.");
    491             consoleScanner = new ConsoleScanner(this);
    492             consoleScanner.start();
    493         }
    494
    495     }
    496
    497     private void printSystemProperties ()
    498     {
    499         // print out which system properties were set up
    500         if (getLog().isDebugEnabled())
    501         {
    502             if (systemProperties != null)
    503             {
    504                 Iterator itor = systemProperties.getSystemProperties().iterator();
    505                 while (itor.hasNext())
    506                 {
    507                     SystemProperty prop = (SystemProperty)itor.next();
    508                     getLog().debug("Property "+prop.getName()+"="+prop.getValue()+" was "+ (prop.isSet() ? "set" : "skipped"));
    509                 }
    510             }
    511         }
    512     }
    513
    514     /**
    515      * Try and find a jetty-web.xml file, using some
    516      * historical naming conventions if necessary.
    517      * @param webInfDir
    518      * @return
    519      */
    520     public File findJettyWebXmlFile (File webInfDir)
    521     {
    522         if (webInfDir == null)
    523             return null;
    524         if (!webInfDir.exists())
    525             return null;
    526
    527         File f = new File (webInfDir, "jetty-web.xml");
    528         if (f.exists())
    529             return f;
    530
    531         //try some historical alternatives
    532         f = new File (webInfDir, "web-jetty.xml");
    533         if (f.exists())
    534             return f;
    535         f = new File (webInfDir, "jetty6-web.xml");
    536         if (f.exists())
    537             return f;
    538
    539         return null;
    540     }
    541 }