View Javadoc

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