View Javadoc

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