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=<stopPort> 
151      * -DSTOP.KEY=<stopKey> -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=<stopKey> 
158      * -DSTOP.PORT=<stopPort> -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 }