View Javadoc

    1   //========================================================================
    2   //$Id: AbstractJettyMojo.java 6194 2010-09-27 07:24:19Z 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      * <p>
    195      * If true, execution of the plugin is skipped.
    196      * </p>
    197      * @parameter expression="${jetty.skip}" default-value="false"
    198      */
    199     protected boolean skip;
    200
    201     /**
    202      * A scanner to check for changes to the webapp
    203      */
    204     protected Scanner scanner;
    205
    206     /**
    207      *  List of files and directories to scan
    208      */
    209     protected ArrayList scanList;
    210
    211     /**
    212      * List of Listeners for the scanner
    213      */
    214     protected ArrayList scannerListeners;
    215
    216
    217     /**
    218      * A scanner to check ENTER hits on the console
    219      */
    220     protected Thread consoleScanner;
    221
    222
    223     public String PORT_SYSPROPERTY = "jetty.port";
    224
    225     /**
    226      * @return Returns the realms configured in the pom
    227      */
    228     public abstract Object[] getConfiguredUserRealms();
    229
    230     /**
    231      * @return Returns the connectors configured in the pom
    232      */
    233     public abstract Object[] getConfiguredConnectors();
    234
    235     public abstract Object getConfiguredRequestLog();
    236
    237
    238     public abstract void checkPomConfiguration() throws MojoExecutionException;
    239
    240
    241
    242     public abstract void configureScanner () throws MojoExecutionException;
    243
    244
    245     public abstract void applyJettyXml () throws Exception;
    246
    247
    248     /**
    249      * create a proxy that wraps a particular jetty version Server object
    250      * @return
    251      */
    252     public abstract JettyPluginServer createServer() throws Exception;
    253
    254
    255     public abstract void finishConfigurationBeforeStart() throws Exception;
    256
    257
    258     public MavenProject getProject()
    259     {
    260         return this.project;
    261     }
    262
    263     public File getTmpDirectory()
    264     {
    265         return this.tmpDirectory;
    266     }
    267
    268
    269     public File getWebDefaultXml()
    270     {
    271         return this.webDefaultXml;
    272     }
    273
    274     public File getOverrideWebXml()
    275     {
    276         return this.overrideWebXml;
    277     }
    278
    279     /**
    280      * @return Returns the contextPath.
    281      */
    282     public String getContextPath()
    283     {
    284         return this.contextPath;
    285     }
    286
    287     /**
    288      * @return Returns the scanIntervalSeconds.
    289      */
    290     public int getScanIntervalSeconds()
    291     {
    292         return this.scanIntervalSeconds;
    293     }
    294
    295     /**
    296      * @return returns the path to the systemPropertiesFile
    297      */
    298     public File getSystemPropertiesFile()
    299     {
    300         return this.systemPropertiesFile;
    301     }
    302
    303     public void setSystemPropertiesFile(File file) throws Exception
    304     {
    305         this.systemPropertiesFile = file;
    306         FileInputStream propFile = new FileInputStream(systemPropertiesFile);
    307         Properties properties = new Properties();
    308         properties.load(propFile);
    309
    310         if (this.systemProperties == null )
    311             this.systemProperties = new SystemProperties();
    312
    313         for (Enumeration keys = properties.keys(); keys.hasMoreElements();  )
    314         {
    315             String key = (String)keys.nextElement();
    316             if ( ! systemProperties.containsSystemProperty(key) )
    317             {
    318                 SystemProperty prop = new SystemProperty();
    319                 prop.setKey(key);
    320                 prop.setValue(properties.getProperty(key));
    321
    322                 this.systemProperties.setSystemProperty(prop);
    323             }
    324         }
    325
    326     }
    327
    328     public void setSystemProperties(SystemProperties systemProperties)
    329     {
    330         if (this.systemProperties == null)
    331             this.systemProperties = systemProperties;
    332         else
    333         {
    334             Iterator itor = systemProperties.getSystemProperties().iterator();
    335             while (itor.hasNext())
    336             {
    337                 SystemProperty prop = (SystemProperty)itor.next();
    338                 this.systemProperties.setSystemProperty(prop);
    339             }
    340         }
    341     }
    342
    343     public File getJettyXmlFile ()
    344     {
    345         return this.jettyConfig;
    346     }
    347
    348
    349     public JettyPluginServer getServer ()
    350     {
    351         return this.server;
    352     }
    353
    354     public void setServer (JettyPluginServer server)
    355     {
    356         this.server = server;
    357     }
    358
    359
    360     public void setScanList (ArrayList list)
    361     {
    362         this.scanList = new ArrayList(list);
    363     }
    364
    365     public ArrayList getScanList ()
    366     {
    367         return this.scanList;
    368     }
    369
    370
    371     public void setScannerListeners (ArrayList listeners)
    372     {
    373         this.scannerListeners = new ArrayList(listeners);
    374     }
    375
    376     public ArrayList getScannerListeners ()
    377     {
    378         return this.scannerListeners;
    379     }
    380
    381     public Scanner getScanner ()
    382     {
    383         return scanner;
    384     }
    385
    386     public void execute() throws MojoExecutionException, MojoFailureException
    387     {
    388         getLog().info("Configuring Jetty for project: " + getProject().getName());
    389         if (skip)
    390         {
    391             getLog().info("Skipping jetty: jetty.skip==true");
    392             return;
    393         }
    394         PluginLog.setLog(getLog());
    395         checkPomConfiguration();
    396         startJetty();
    397     }
    398
    399
    400     public void startJetty () throws MojoExecutionException
    401     {
    402         try
    403         {
    404             getLog().debug("Starting Jetty Server ...");
    405
    406             printSystemProperties();
    407             setServer(createServer());
    408
    409             //apply any config from a jetty.xml file first which is able to
    410             //be overwritten by config in the pom.xml
    411             applyJettyXml ();
    412
    413             JettyPluginServer plugin=getServer();
    414
    415
    416             // if the user hasn't configured their project's pom to use a
    417             // different set of connectors,
    418             // use the default
    419             Object[] configuredConnectors = getConfiguredConnectors();
    420
    421             plugin.setConnectors(configuredConnectors);
    422             Object[] connectors = plugin.getConnectors();
    423
    424             if (connectors == null|| connectors.length == 0)
    425             {
    426                 //if a SystemProperty -Djetty.port=<portnum> has been supplied, use that as the default port
    427                 configuredConnectors = new Object[] { plugin.createDefaultConnector(System.getProperty(PORT_SYSPROPERTY, null)) };
    428                 plugin.setConnectors(configuredConnectors);
    429             }
    430
    431
    432             //set up a RequestLog if one is provided
    433             if (getConfiguredRequestLog() != null)
    434                 getServer().setRequestLog(getConfiguredRequestLog());
    435
    436             //set up the webapp and any context provided
    437             getServer().configureHandlers();
    438             configureWebApplication();
    439             getServer().addWebApplication(webAppConfig);
    440
    441
    442             // set up security realms
    443             Object[] configuredRealms = getConfiguredUserRealms();
    444             for (int i = 0; (configuredRealms != null) && i < configuredRealms.length; i++)
    445                 getLog().debug(configuredRealms[i].getClass().getName() + ": "+ configuredRealms[i].toString());
    446
    447             plugin.setUserRealms(configuredRealms);
    448
    449             //do any other configuration required by the
    450             //particular Jetty version
    451             finishConfigurationBeforeStart();
    452
    453             // start Jetty
    454             server.start();
    455
    456             getLog().info("Started Jetty Server");
    457
    458             if(stopPort>0 && stopKey!=null)
    459             {
    460                 org.mortbay.jetty.plugin.util.Monitor monitor = new org.mortbay.jetty.plugin.util.Monitor(stopPort, stopKey, new Server[]{(Server)server.getProxiedObject()}, !daemon);
    461                 monitor.start();
    462             }
    463
    464             // start the scanner thread (if necessary) on the main webapp
    465             configureScanner ();
    466             startScanner();
    467
    468             // start the new line scanner thread if necessary
    469             startConsoleScanner();
    470
    471             // keep the thread going if not in daemon mode
    472             if (!daemon)
    473             {
    474                 server.join();
    475             }
    476         }
    477         catch (Exception e)
    478         {
    479             throw new MojoExecutionException("Failure", e);
    480         }
    481         finally
    482         {
    483             if (!daemon)
    484             {
    485                 getLog().info("Jetty server exiting.");
    486             }
    487         }
    488
    489     }
    490
    491
    492     public abstract void restartWebApp(boolean reconfigureScanner) throws Exception;
    493
    494     /**
    495      * Subclasses should invoke this to setup basic info
    496      * on the webapp
    497      *
    498      * @throws MojoExecutionException
    499      */
    500     public void configureWebApplication () throws Exception
    501     {
    502         //use EITHER a <webAppConfig> element or the now deprecated <contextPath>, <tmpDirectory>, <webDefaultXml>, <overrideWebXml>
    503         //way of doing things
    504         if (webAppConfig == null)
    505         {
    506             webAppConfig = new Jetty6PluginWebAppContext();
    507             webAppConfig.setContextPath((getContextPath().startsWith("/") ? getContextPath() : "/"+ getContextPath()));
    508             if (getTmpDirectory() != null)
    509                 webAppConfig.setTempDirectory(getTmpDirectory());
    510             if (getWebDefaultXml() != null)
    511                 webAppConfig.setDefaultsDescriptor(getWebDefaultXml().getCanonicalPath());
    512             if (getOverrideWebXml() != null)
    513                 webAppConfig.setOverrideDescriptor(getOverrideWebXml().getCanonicalPath());
    514         }
    515
    516         if (webAppConfig.getContextPath() == null)
    517         {
    518             webAppConfig.setContextPath((getContextPath().startsWith("/") ? getContextPath() : "/"+ getContextPath()));
    519         }
    520
    521         getLog().info("Context path = " + webAppConfig.getContextPath());
    522         getLog().info("Tmp directory = "+ " determined at runtime");
    523         getLog().info("Web defaults = "+(webAppConfig.getDefaultsDescriptor()==null?" jetty default":webAppConfig.getDefaultsDescriptor()));
    524         getLog().info("Web overrides = "+(webAppConfig.getOverrideDescriptor()==null?" none":webAppConfig.getOverrideDescriptor()));
    525
    526     }
    527
    528     /**
    529      * Run a scanner thread on the given list of files and directories, calling
    530      * stop/start on the given list of LifeCycle objects if any of the watched
    531      * files change.
    532      *
    533      */
    534     private void startScanner()
    535     {
    536
    537         // check if scanning is enabled
    538         if (getScanIntervalSeconds() <= 0) return;
    539
    540         // check if reload is manual. It disables file scanning
    541         if ( "manual".equalsIgnoreCase( reload ) )
    542         {
    543             // issue a warning if both scanIntervalSeconds and reload
    544             // are enabled
    545             getLog().warn("scanIntervalSeconds is set to " + scanIntervalSeconds + " but will be IGNORED due to manual reloading");
    546             return;
    547         }
    548
    549         scanner = new Scanner();
    550         scanner.setReportExistingFilesOnStartup(false);
    551         scanner.setScanInterval(getScanIntervalSeconds());
    552         scanner.setScanDirs(getScanList());
    553         scanner.setRecursive(true);
    554         List listeners = getScannerListeners();
    555         Iterator itor = (listeners==null?null:listeners.iterator());
    556         while (itor!=null && itor.hasNext())
    557             scanner.addListener((Scanner.Listener)itor.next());
    558         getLog().info("Starting scanner at interval of " + getScanIntervalSeconds()+ " seconds.");
    559         scanner.start();
    560     }
    561
    562     /**
    563      * Run a thread that monitors the console input to detect ENTER hits.
    564      */
    565     protected void startConsoleScanner()
    566     {
    567         if ( "manual".equalsIgnoreCase( reload ) )
    568         {
    569             getLog().info("Console reloading is ENABLED. Hit ENTER on the console to restart the context.");
    570             consoleScanner = new ConsoleScanner(this);
    571             consoleScanner.start();
    572         }
    573
    574     }
    575
    576     private void printSystemProperties ()
    577     {
    578         // print out which system properties were set up
    579         if (getLog().isDebugEnabled())
    580         {
    581             if (systemProperties != null)
    582             {
    583                 Iterator itor = systemProperties.getSystemProperties().iterator();
    584                 while (itor.hasNext())
    585                 {
    586                     SystemProperty prop = (SystemProperty)itor.next();
    587                     getLog().debug("Property "+prop.getName()+"="+prop.getValue()+" was "+ (prop.isSet() ? "set" : "skipped"));
    588                 }
    589             }
    590         }
    591     }
    592
    593     /**
    594      * Try and find a jetty-web.xml file, using some
    595      * historical naming conventions if necessary.
    596      * @param webInfDir
    597      * @return
    598      */
    599     public File findJettyWebXmlFile (File webInfDir)
    600     {
    601         if (webInfDir == null)
    602             return null;
    603         if (!webInfDir.exists())
    604             return null;
    605
    606         File f = new File (webInfDir, "jetty-web.xml");
    607         if (f.exists())
    608             return f;
    609
    610         //try some historical alternatives
    611         f = new File (webInfDir, "web-jetty.xml");
    612         if (f.exists())
    613             return f;
    614         f = new File (webInfDir, "jetty6-web.xml");
    615         if (f.exists())
    616             return f;
    617
    618         return null;
    619     }
    620 }