View Javadoc

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