View Javadoc

1   //========================================================================
2   //$Id: AbstractJettyRunMojo.java 4776 2009-03-17 09:03:43Z dyu $
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  package org.mortbay.jetty.plugin;
17  
18  import java.io.File;
19  import java.io.IOException;
20  import java.util.ArrayList;
21  import java.util.Collections;
22  import java.util.Date;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.apache.maven.artifact.Artifact;
27  import org.apache.maven.plugin.MojoExecutionException;
28  import org.apache.maven.plugin.MojoFailureException;
29  import org.codehaus.plexus.util.FileUtils;
30  import org.mortbay.jetty.plugin.util.ScanTargetPattern;
31  import org.mortbay.resource.Resource;
32  import org.mortbay.resource.ResourceCollection;
33  import org.mortbay.util.Scanner;
34  
35  /**
36   * AbstractJettyRunMojo
37   * 
38   * 
39   * Base class for all jetty versions for the "run" mojo.
40   * 
41   */
42  public abstract class AbstractJettyRunMojo extends AbstractJettyMojo
43  {
44  
45      /**
46       * If true, the <testOutputDirectory>
47       * and the dependencies of <scope>test<scope>
48       * will be put first on the runtime classpath.
49       * @parameter default-value="false"
50       */
51      private boolean useTestClasspath;
52      
53      
54      /**
55       * The location of a jetty-env.xml file. Optional.
56       * @parameter
57       */
58      private File jettyEnvXml;
59      
60      /**
61       * The location of the web.xml file. If not
62       * set then it is assumed it is in ${basedir}/src/main/webapp/WEB-INF
63       * 
64       * @parameter expression="${maven.war.webxml}"
65       */
66      private File webXml;
67      
68      /**
69       * The directory containing generated classes.
70       *
71       * @parameter expression="${project.build.outputDirectory}"
72       * @required
73       * 
74       */
75      private File classesDirectory;
76      
77      
78      
79      /**
80       * The directory containing generated test classes.
81       * 
82       * @parameter expression="${project.build.testOutputDirectory}"
83       * @required
84       */
85      private File testClassesDirectory;
86      
87      /**
88       * Root directory for all html/jsp etc files
89       *
90       * @parameter expression="${basedir}/src/main/webapp"
91       * @required
92       */
93      private File webAppSourceDirectory;
94      
95      /**
96       * @parameter expression="${plugin.artifacts}"
97       * @readonly
98       */
99      private List pluginArtifacts;
100     
101     /**
102      * List of files or directories to additionally periodically scan for changes. Optional.
103      * @parameter
104      */
105     private File[] scanTargets;
106     
107     
108     /**
109      * List of directories with ant-style <include> and <exclude> patterns
110      * for extra targets to periodically scan for changes. Can be used instead of,
111      * or in conjunction with <scanTargets>.Optional.
112      * @parameter
113      */
114     private ScanTargetPattern[] scanTargetPatterns;
115 
116     /**
117      * web.xml as a File
118      */
119     private File webXmlFile;
120     
121     
122     /**
123      * jetty-env.xml as a File
124      */
125     private File jettyEnvXmlFile;
126 
127     /**
128      * List of files on the classpath for the webapp
129      */
130     private List classPathFiles;
131     
132     
133     /**
134      * Extra scan targets as a list
135      */
136     private List extraScanTargets;
137     
138     /**
139      * overlays (resources)
140      */
141     private List _overlays;
142 
143     public File getWebXml()
144     {
145         return this.webXml;
146     }
147     
148     public File getJettyEnvXml ()
149     {
150         return this.jettyEnvXml;
151     }
152 
153     public File getClassesDirectory()
154     {
155         return this.classesDirectory;
156     }
157 
158     public File getWebAppSourceDirectory()
159     {
160         return this.webAppSourceDirectory;
161     }
162 
163     public void setWebXmlFile (File f)
164     {
165         this.webXmlFile = f;
166     }
167     
168     public File getWebXmlFile ()
169     {
170         return this.webXmlFile;
171     }
172     
173     public File getJettyEnvXmlFile ()
174     {
175         return this.jettyEnvXmlFile;
176     }
177     
178     public void setJettyEnvXmlFile (File f)
179     {
180         this.jettyEnvXmlFile = f;
181     }
182     
183     public void setClassPathFiles (List list)
184     {
185         this.classPathFiles = new ArrayList(list);
186     }
187 
188     public List getClassPathFiles ()
189     {
190         return this.classPathFiles;
191     }
192 
193 
194     public List getExtraScanTargets ()
195     {
196         return this.extraScanTargets;
197     }
198     
199     public void setExtraScanTargets(List list)
200     {
201         this.extraScanTargets = list;
202     }
203 
204     /**
205      * Run the mojo
206      * @see org.apache.maven.plugin.Mojo#execute()
207      */
208     public void execute() throws MojoExecutionException, MojoFailureException
209     {
210        super.execute();
211     }
212     
213     
214     /**
215      * Verify the configuration given in the pom.
216      * 
217      * @see org.mortbay.jetty.plugin.AbstractJettyMojo#checkPomConfiguration()
218      */
219     public void checkPomConfiguration () throws MojoExecutionException
220     {
221         // check the location of the static content/jsps etc
222         try
223         {
224             if ((getWebAppSourceDirectory() == null) || !getWebAppSourceDirectory().exists())
225                 throw new MojoExecutionException("Webapp source directory "
226                         + (getWebAppSourceDirectory() == null ? "null" : getWebAppSourceDirectory().getCanonicalPath())
227                         + " does not exist");
228             else
229                 getLog().info( "Webapp source directory = "
230                         + getWebAppSourceDirectory().getCanonicalPath());
231         }
232         catch (IOException e)
233         {
234             throw new MojoExecutionException("Webapp source directory does not exist", e);
235         }
236         
237         // check reload mechanic
238         if ( !"automatic".equalsIgnoreCase( reload ) && !"manual".equalsIgnoreCase( reload ) )
239         {
240             throw new MojoExecutionException( "invalid reload mechanic specified, must be 'automatic' or 'manual'" );
241         }
242         else
243         {
244             getLog().info("Reload Mechanic: " + reload );
245         }
246 
247 
248         // get the web.xml file if one has been provided, otherwise assume it is
249         // in the webapp src directory
250         if (getWebXml() == null )
251             webXml = new File(new File(getWebAppSourceDirectory(),"WEB-INF"), "web.xml");
252         setWebXmlFile(webXml);
253         
254         try
255         {
256             if (!getWebXmlFile().exists())
257                 throw new MojoExecutionException( "web.xml does not exist at location "
258                         + webXmlFile.getCanonicalPath());
259             else
260                 getLog().info( "web.xml file = "
261                         + webXmlFile.getCanonicalPath());
262         }
263         catch (IOException e)
264         {
265             throw new MojoExecutionException("web.xml does not exist", e);
266         }
267         
268         //check if a jetty-env.xml location has been provided, if so, it must exist
269         if  (getJettyEnvXml() != null)
270         {
271             setJettyEnvXmlFile(jettyEnvXml);
272             
273             try
274             {
275                 if (!getJettyEnvXmlFile().exists())
276                     throw new MojoExecutionException("jetty-env.xml file does not exist at location "+jettyEnvXml);
277                 else
278                     getLog().info(" jetty-env.xml = "+getJettyEnvXmlFile().getCanonicalPath());
279             }
280             catch (IOException e)
281             {
282                 throw new MojoExecutionException("jetty-env.xml does not exist");
283             }
284         }
285         
286         
287         // check the classes to form a classpath with
288         try
289         {
290             //allow a webapp with no classes in it (just jsps/html)
291             if (getClassesDirectory() != null)
292             {
293                 if (!getClassesDirectory().exists())
294                     getLog().info( "Classes directory "+ getClassesDirectory().getCanonicalPath()+ " does not exist");
295                 else
296                     getLog().info("Classes = " + getClassesDirectory().getCanonicalPath());
297             }
298             else
299                 getLog().info("Classes directory not set");         
300         }
301         catch (IOException e)
302         {
303             throw new MojoExecutionException("Location of classesDirectory does not exist");
304         }
305         
306         
307         setExtraScanTargets(new ArrayList());
308         if (scanTargets != null)
309         {            
310             for (int i=0; i< scanTargets.length; i++)
311             {
312                 getLog().info("Added extra scan target:"+ scanTargets[i]);
313                 getExtraScanTargets().add(scanTargets[i]);
314             }            
315         }
316         
317         
318         if (scanTargetPatterns!=null)
319         {
320             for (int i=0;i<scanTargetPatterns.length; i++)
321             {
322                 Iterator itor = scanTargetPatterns[i].getIncludes().iterator();
323                 StringBuffer strbuff = new StringBuffer();
324                 while (itor.hasNext())
325                 {
326                     strbuff.append((String)itor.next());
327                     if (itor.hasNext())
328                         strbuff.append(",");
329                 }
330                 String includes = strbuff.toString();
331                 
332                 itor = scanTargetPatterns[i].getExcludes().iterator();
333                 strbuff= new StringBuffer();
334                 while (itor.hasNext())
335                 {
336                     strbuff.append((String)itor.next());
337                     if (itor.hasNext())
338                         strbuff.append(",");
339                 }
340                 String excludes = strbuff.toString();
341 
342                 try
343                 {
344                     List files = FileUtils.getFiles(scanTargetPatterns[i].getDirectory(), includes, excludes);
345                     itor = files.iterator();
346                     while (itor.hasNext())
347                         getLog().info("Adding extra scan target from pattern: "+itor.next());
348                     List currentTargets = getExtraScanTargets();
349                     if(currentTargets!=null && !currentTargets.isEmpty())
350                         currentTargets.addAll(files);
351                     else
352                         setExtraScanTargets(files);
353                 }
354                 catch (IOException e)
355                 {
356                     throw new MojoExecutionException(e.getMessage());
357                 }
358             }
359             
360            
361         }
362     }
363 
364    
365 
366 
367 
368     public void configureWebApplication() throws Exception
369     {
370        super.configureWebApplication();
371         setClassPathFiles(setUpClassPath());
372         if(webAppConfig.getWebXmlFile()==null)
373             webAppConfig.setWebXmlFile(getWebXmlFile());
374         if(webAppConfig.getJettyEnvXmlFile()==null)
375             webAppConfig.setJettyEnvXmlFile(getJettyEnvXmlFile());
376         if(webAppConfig.getClassPathFiles()==null)
377             webAppConfig.setClassPathFiles(getClassPathFiles());
378         if(webAppConfig.getWar()==null)
379             webAppConfig.setWar(getWebAppSourceDirectory().getCanonicalPath());
380         getLog().info("Webapp directory = " + getWebAppSourceDirectory().getCanonicalPath());
381 
382         webAppConfig.configure();
383     }
384     
385     public void configureScanner ()
386     {
387         // start the scanner thread (if necessary) on the main webapp
388         final ArrayList scanList = new ArrayList();
389         scanList.add(getWebXmlFile());
390         if (getJettyEnvXmlFile() != null)
391             scanList.add(getJettyEnvXmlFile());
392         File jettyWebXmlFile = findJettyWebXmlFile(new File(getWebAppSourceDirectory(),"WEB-INF"));
393         if (jettyWebXmlFile != null)
394             scanList.add(jettyWebXmlFile);
395         scanList.addAll(getExtraScanTargets());
396         scanList.add(getProject().getFile());
397         scanList.addAll(getClassPathFiles());
398         setScanList(scanList);
399         ArrayList listeners = new ArrayList();
400         listeners.add(new Scanner.BulkListener()
401         {
402             public void filesChanged (List changes)
403             {
404                 try
405                 {
406                     boolean reconfigure = changes.contains(getProject().getFile().getCanonicalPath());
407                     restartWebApp(reconfigure);
408                 }
409                 catch (Exception e)
410                 {
411                     getLog().error("Error reconfiguring/restarting webapp after change in watched files",e);
412                 }
413             }
414 
415 
416         });
417         setScannerListeners(listeners);
418     }
419 
420     public void restartWebApp(boolean reconfigureScanner) throws Exception 
421     {
422         getLog().info("restarting "+webAppConfig);
423         getLog().debug("Stopping webapp ...");
424         webAppConfig.stop();
425         getLog().debug("Reconfiguring webapp ...");
426 
427         checkPomConfiguration();
428         configureWebApplication();
429 
430         // check if we need to reconfigure the scanner,
431         // which is if the pom changes
432         if (reconfigureScanner)
433         {
434             getLog().info("Reconfiguring scanner after change to pom.xml ...");
435             scanList.clear();
436             scanList.add(getWebXmlFile());
437             if (getJettyEnvXmlFile() != null)
438                 scanList.add(getJettyEnvXmlFile());
439             scanList.addAll(getExtraScanTargets());
440             scanList.add(getProject().getFile());
441             scanList.addAll(getClassPathFiles());
442             getScanner().setScanDirs(scanList);
443         }
444 
445         getLog().debug("Restarting webapp ...");
446         webAppConfig.start();
447         getLog().info("Restart completed at "+new Date().toString());
448     }
449     
450     private List getDependencyFiles ()
451     {
452         List dependencyFiles = new ArrayList();
453         List overlays = new ArrayList();
454         for ( Iterator iter = getProject().getArtifacts().iterator(); iter.hasNext(); )
455         {
456             Artifact artifact = (Artifact) iter.next();
457             // Include runtime and compile time libraries, and possibly test libs too
458             if(artifact.getType().equals("war"))
459             {
460                 try
461                 {
462                     Resource r = Resource.newResource("jar:" + artifact.getFile().toURL().toString() + "!/");
463                     overlays.add(r);
464                     getExtraScanTargets().add(artifact.getFile());
465                 }
466                 catch(Exception e)
467                 {
468                     throw new RuntimeException(e);
469                 }
470                 continue;
471             }
472             if (((!Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) && (!Artifact.SCOPE_TEST.equals( artifact.getScope()))) 
473                     ||
474                 (useTestClasspath && Artifact.SCOPE_TEST.equals( artifact.getScope())))
475             {
476                 dependencyFiles.add(artifact.getFile());
477                 getLog().debug( "Adding artifact " + artifact.getFile().getName() + " for WEB-INF/lib " );   
478             }
479         }
480         if(!overlays.isEmpty() && !isEqual(overlays, _overlays))
481         {
482             try
483             {
484                 Resource resource = _overlays==null ? webAppConfig.getBaseResource() : null;
485                 ResourceCollection rc = new ResourceCollection();
486                 if(resource==null)
487                 {
488                     // nothing configured, so we automagically enable the overlays                    
489                     int size = overlays.size()+1;
490                     Resource[] resources = new Resource[size];
491                     resources[0] = Resource.newResource(getWebAppSourceDirectory().toURL());
492                     for(int i=1; i<size; i++)
493                     {
494                         resources[i] = (Resource)overlays.get(i-1);
495                         getLog().info("Adding overlay: " + resources[i]);
496                     }
497                     rc.setResources(resources);
498                 }                
499                 else
500                 {                    
501                     if(resource instanceof ResourceCollection)
502                     {
503                         // there was a preconfigured ResourceCollection ... append the artifact wars
504                         Resource[] old = ((ResourceCollection)resource).getResources();
505                         int size = old.length + overlays.size();
506                         Resource[] resources = new Resource[size];
507                         System.arraycopy(old, 0, resources, 0, old.length);
508                         for(int i=old.length,j=0; i<size; i++,j++)
509                         {
510                             resources[i] = (Resource)overlays.get(j);
511                             getLog().info("Adding overlay: " + resources[i]);
512                         }
513                         rc.setResources(resources);
514                     }
515                     else
516                     {
517                         // baseResource was already configured w/c could be src/main/webapp
518                         if(!resource.isDirectory() && String.valueOf(resource.getFile()).endsWith(".war"))
519                         {
520                             // its a war                            
521                             resource = Resource.newResource("jar:" + resource.getURL().toString() + "!/");
522                         }
523                         int size = overlays.size()+1;
524                         Resource[] resources = new Resource[size];
525                         resources[0] = resource;
526                         for(int i=1; i<size; i++)
527                         {
528                             resources[i] = (Resource)overlays.get(i-1);
529                             getLog().info("Adding overlay: " + resources[i]);
530                         }
531                         rc.setResources(resources);
532                     }
533                 }
534                 webAppConfig.setBaseResource(rc);
535                 _overlays = overlays;
536             }
537             catch(Exception e)
538             {
539                 throw new RuntimeException(e);
540             }
541         }
542         return dependencyFiles; 
543     }
544     
545     
546    
547 
548     private List setUpClassPath()
549     {
550         List classPathFiles = new ArrayList();       
551         
552         //if using the test classes, make sure they are first
553         //on the list
554         if (useTestClasspath && (testClassesDirectory != null))
555             classPathFiles.add(testClassesDirectory);
556         
557         if (getClassesDirectory() != null)
558             classPathFiles.add(getClassesDirectory());
559         
560         //now add all of the dependencies
561         classPathFiles.addAll(getDependencyFiles());
562         
563         if (getLog().isDebugEnabled())
564         {
565             for (int i = 0; i < classPathFiles.size(); i++)
566             {
567                 getLog().debug("classpath element: "+ ((File) classPathFiles.get(i)).getName());
568             }
569         }
570         return classPathFiles;
571     }
572     
573     static boolean isEqual(List overlays1, List overlays2)
574     {
575         if(overlays2==null || overlays1.size()!=overlays2.size())
576             return false;
577         
578         for(int i=0; i<overlays1.size(); i++)
579         {
580             if(!overlays1.get(i).equals(overlays2.get(i)))
581                 return false;
582         }
583         return true;
584     }
585 
586 }