View Javadoc

1   //========================================================================
2   //$Id: AbstractJettyRunMojo.java 3649 2008-09-18 06:36:58Z 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     public File getWebXml()
139     {
140         return this.webXml;
141     }
142     
143     public File getJettyEnvXml ()
144     {
145         return this.jettyEnvXml;
146     }
147 
148     public File getClassesDirectory()
149     {
150         return this.classesDirectory;
151     }
152 
153     public File getWebAppSourceDirectory()
154     {
155         return this.webAppSourceDirectory;
156     }
157 
158     public void setWebXmlFile (File f)
159     {
160         this.webXmlFile = f;
161     }
162     
163     public File getWebXmlFile ()
164     {
165         return this.webXmlFile;
166     }
167     
168     public File getJettyEnvXmlFile ()
169     {
170         return this.jettyEnvXmlFile;
171     }
172     
173     public void setJettyEnvXmlFile (File f)
174     {
175         this.jettyEnvXmlFile = f;
176     }
177     
178     public void setClassPathFiles (List list)
179     {
180         this.classPathFiles = new ArrayList(list);
181     }
182 
183     public List getClassPathFiles ()
184     {
185         return this.classPathFiles;
186     }
187 
188 
189     public List getExtraScanTargets ()
190     {
191         return this.extraScanTargets;
192     }
193     
194     public void setExtraScanTargets(List list)
195     {
196         this.extraScanTargets = list;
197     }
198 
199     /**
200      * Run the mojo
201      * @see org.apache.maven.plugin.Mojo#execute()
202      */
203     public void execute() throws MojoExecutionException, MojoFailureException
204     {
205        super.execute();
206     }
207     
208     
209     /**
210      * Verify the configuration given in the pom.
211      * 
212      * @see org.mortbay.jetty.plugin.AbstractJettyMojo#checkPomConfiguration()
213      */
214     public void checkPomConfiguration () throws MojoExecutionException
215     {
216         // check the location of the static content/jsps etc
217         try
218         {
219             if ((getWebAppSourceDirectory() == null) || !getWebAppSourceDirectory().exists())
220                 throw new MojoExecutionException("Webapp source directory "
221                         + (getWebAppSourceDirectory() == null ? "null" : getWebAppSourceDirectory().getCanonicalPath())
222                         + " does not exist");
223             else
224                 getLog().info( "Webapp source directory = "
225                         + getWebAppSourceDirectory().getCanonicalPath());
226         }
227         catch (IOException e)
228         {
229             throw new MojoExecutionException("Webapp source directory does not exist", e);
230         }
231         
232         // check reload mechanic
233         if ( !"automatic".equalsIgnoreCase( reload ) && !"manual".equalsIgnoreCase( reload ) )
234         {
235             throw new MojoExecutionException( "invalid reload mechanic specified, must be 'automatic' or 'manual'" );
236         }
237         else
238         {
239             getLog().info("Reload Mechanic: " + reload );
240         }
241 
242 
243         // get the web.xml file if one has been provided, otherwise assume it is
244         // in the webapp src directory
245         if (getWebXml() == null )
246             webXml = new File(new File(getWebAppSourceDirectory(),"WEB-INF"), "web.xml");
247         setWebXmlFile(webXml);
248         
249         try
250         {
251             if (!getWebXmlFile().exists())
252                 throw new MojoExecutionException( "web.xml does not exist at location "
253                         + webXmlFile.getCanonicalPath());
254             else
255                 getLog().info( "web.xml file = "
256                         + webXmlFile.getCanonicalPath());
257         }
258         catch (IOException e)
259         {
260             throw new MojoExecutionException("web.xml does not exist", e);
261         }
262         
263         //check if a jetty-env.xml location has been provided, if so, it must exist
264         if  (getJettyEnvXml() != null)
265         {
266             setJettyEnvXmlFile(jettyEnvXml);
267             
268             try
269             {
270                 if (!getJettyEnvXmlFile().exists())
271                     throw new MojoExecutionException("jetty-env.xml file does not exist at location "+jettyEnvXml);
272                 else
273                     getLog().info(" jetty-env.xml = "+getJettyEnvXmlFile().getCanonicalPath());
274             }
275             catch (IOException e)
276             {
277                 throw new MojoExecutionException("jetty-env.xml does not exist");
278             }
279         }
280         
281         
282         // check the classes to form a classpath with
283         try
284         {
285             //allow a webapp with no classes in it (just jsps/html)
286             if (getClassesDirectory() != null)
287             {
288                 if (!getClassesDirectory().exists())
289                     getLog().info( "Classes directory "+ getClassesDirectory().getCanonicalPath()+ " does not exist");
290                 else
291                     getLog().info("Classes = " + getClassesDirectory().getCanonicalPath());
292             }
293             else
294                 getLog().info("Classes directory not set");         
295         }
296         catch (IOException e)
297         {
298             throw new MojoExecutionException("Location of classesDirectory does not exist");
299         }
300         
301         
302         setExtraScanTargets(new ArrayList());
303         if (scanTargets != null)
304         {            
305             for (int i=0; i< scanTargets.length; i++)
306             {
307                 getLog().info("Added extra scan target:"+ scanTargets[i]);
308                 getExtraScanTargets().add(scanTargets[i]);
309             }            
310         }
311         
312         
313         if (scanTargetPatterns!=null)
314         {
315             for (int i=0;i<scanTargetPatterns.length; i++)
316             {
317                 Iterator itor = scanTargetPatterns[i].getIncludes().iterator();
318                 StringBuffer strbuff = new StringBuffer();
319                 while (itor.hasNext())
320                 {
321                     strbuff.append((String)itor.next());
322                     if (itor.hasNext())
323                         strbuff.append(",");
324                 }
325                 String includes = strbuff.toString();
326                 
327                 itor = scanTargetPatterns[i].getExcludes().iterator();
328                 strbuff= new StringBuffer();
329                 while (itor.hasNext())
330                 {
331                     strbuff.append((String)itor.next());
332                     if (itor.hasNext())
333                         strbuff.append(",");
334                 }
335                 String excludes = strbuff.toString();
336 
337                 try
338                 {
339                     List files = FileUtils.getFiles(scanTargetPatterns[i].getDirectory(), includes, excludes);
340                     itor = files.iterator();
341                     while (itor.hasNext())
342                         getLog().info("Adding extra scan target from pattern: "+itor.next());
343                     List currentTargets = getExtraScanTargets();
344                     if(currentTargets!=null && !currentTargets.isEmpty())
345                         currentTargets.addAll(files);
346                     else
347                         setExtraScanTargets(files);
348                 }
349                 catch (IOException e)
350                 {
351                     throw new MojoExecutionException(e.getMessage());
352                 }
353             }
354             
355            
356         }
357     }
358 
359    
360 
361 
362 
363     public void configureWebApplication() throws Exception
364     {
365        super.configureWebApplication();
366         setClassPathFiles(setUpClassPath());
367         if(webAppConfig.getWebXmlFile()==null)
368             webAppConfig.setWebXmlFile(getWebXmlFile());
369         if(webAppConfig.getJettyEnvXmlFile()==null)
370             webAppConfig.setJettyEnvXmlFile(getJettyEnvXmlFile());
371         if(webAppConfig.getClassPathFiles()==null)
372             webAppConfig.setClassPathFiles(getClassPathFiles());
373         if(webAppConfig.getWar()==null)
374             webAppConfig.setWar(getWebAppSourceDirectory().getCanonicalPath());
375         getLog().info("Webapp directory = " + getWebAppSourceDirectory().getCanonicalPath());
376 
377         webAppConfig.configure();
378     }
379     
380     public void configureScanner ()
381     {
382         // start the scanner thread (if necessary) on the main webapp
383         final ArrayList scanList = new ArrayList();
384         scanList.add(getWebXmlFile());
385         if (getJettyEnvXmlFile() != null)
386             scanList.add(getJettyEnvXmlFile());
387         File jettyWebXmlFile = findJettyWebXmlFile(new File(getWebAppSourceDirectory(),"WEB-INF"));
388         if (jettyWebXmlFile != null)
389             scanList.add(jettyWebXmlFile);
390         scanList.addAll(getExtraScanTargets());
391         scanList.add(getProject().getFile());
392         scanList.addAll(getClassPathFiles());
393         setScanList(scanList);
394         ArrayList listeners = new ArrayList();
395         listeners.add(new Scanner.BulkListener()
396         {
397             public void filesChanged (List changes)
398             {
399                 try
400                 {
401                     boolean reconfigure = changes.contains(getProject().getFile().getCanonicalPath());
402                     restartWebApp(reconfigure);
403                 }
404                 catch (Exception e)
405                 {
406                     getLog().error("Error reconfiguring/restarting webapp after change in watched files",e);
407                 }
408             }
409 
410 
411         });
412         setScannerListeners(listeners);
413     }
414 
415     public void restartWebApp(boolean reconfigureScanner) throws Exception 
416     {
417         getLog().info("restarting "+webAppConfig);
418         getLog().debug("Stopping webapp ...");
419         webAppConfig.stop();
420         getLog().debug("Reconfiguring webapp ...");
421 
422         checkPomConfiguration();
423         configureWebApplication();
424 
425         // check if we need to reconfigure the scanner,
426         // which is if the pom changes
427         if (reconfigureScanner)
428         {
429             getLog().info("Reconfiguring scanner after change to pom.xml ...");
430             scanList.clear();
431             scanList.add(getWebXmlFile());
432             if (getJettyEnvXmlFile() != null)
433                 scanList.add(getJettyEnvXmlFile());
434             scanList.addAll(getExtraScanTargets());
435             scanList.add(getProject().getFile());
436             scanList.addAll(getClassPathFiles());
437             getScanner().setScanDirs(scanList);
438         }
439 
440         getLog().debug("Restarting webapp ...");
441         webAppConfig.start();
442         getLog().info("Restart completed at "+new Date().toString());
443     }
444     
445     private List getDependencyFiles ()
446     {
447         List dependencyFiles = new ArrayList();
448         List overlays = new ArrayList();
449         for ( Iterator iter = getProject().getArtifacts().iterator(); iter.hasNext(); )
450         {
451             Artifact artifact = (Artifact) iter.next();
452             // Include runtime and compile time libraries, and possibly test libs too
453             if(artifact.getType().equals("war"))
454             {
455                 try
456                 {
457                     Resource r = Resource.newResource("jar:" + artifact.getFile().toURL().toString() + "!/");
458                     overlays.add(r);
459                     getExtraScanTargets().add(artifact.getFile());
460                 }
461                 catch(Exception e)
462                 {
463                     throw new RuntimeException(e);
464                 }
465                 continue;
466             }
467             if (((!Artifact.SCOPE_PROVIDED.equals(artifact.getScope())) && (!Artifact.SCOPE_TEST.equals( artifact.getScope()))) 
468                     ||
469                 (useTestClasspath && Artifact.SCOPE_TEST.equals( artifact.getScope())))
470             {
471                 dependencyFiles.add(artifact.getFile());
472                 getLog().debug( "Adding artifact " + artifact.getFile().getName() + " for WEB-INF/lib " );   
473             }
474         }
475         if(!overlays.isEmpty())
476         {
477             try
478             {
479                 Resource resource = webAppConfig.getBaseResource();
480                 ResourceCollection rc = new ResourceCollection();
481                 if(resource==null)
482                 {
483                     // nothing configured, so we automagically enable the overlays                    
484                     int size = overlays.size()+1;
485                     Resource[] resources = new Resource[size];
486                     resources[0] = Resource.newResource(getWebAppSourceDirectory().toURL());
487                     for(int i=1; i<size; i++)
488                     {
489                         resources[i] = (Resource)overlays.get(i-1);
490                         getLog().info("Adding overlay: " + resources[i]);
491                     }
492                     rc.setResources(resources);
493                 }                
494                 else
495                 {                    
496                     if(resource instanceof ResourceCollection)
497                     {
498                         // there was a preconfigured ResourceCollection ... append the artifact wars
499                         Resource[] old = ((ResourceCollection)resource).getResources();
500                         int size = old.length + overlays.size();
501                         Resource[] resources = new Resource[size];
502                         System.arraycopy(old, 0, resources, 0, old.length);
503                         for(int i=old.length,j=0; i<size; i++,j++)
504                         {
505                             resources[i] = (Resource)overlays.get(j);
506                             getLog().info("Adding overlay: " + resources[i]);
507                         }
508                         rc.setResources(resources);
509                     }
510                     else
511                     {
512                         // baseResource was already configured w/c could be src/main/webapp
513                         if(!resource.isDirectory() && String.valueOf(resource.getFile()).endsWith(".war"))
514                         {
515                             // its a war                            
516                             resource = Resource.newResource("jar:" + resource.getURL().toString() + "!/");
517                         }
518                         int size = overlays.size()+1;
519                         Resource[] resources = new Resource[size];
520                         resources[0] = resource;
521                         for(int i=1; i<size; i++)
522                         {
523                             resources[i] = (Resource)overlays.get(i-1);
524                             getLog().info("Adding overlay: " + resources[i]);
525                         }
526                         rc.setResources(resources);
527                     }
528                 }
529                 webAppConfig.setBaseResource(rc);
530             }
531             catch(Exception e)
532             {
533                 throw new RuntimeException(e);
534             }
535         }
536         return dependencyFiles; 
537     }
538     
539     
540    
541 
542     private List setUpClassPath()
543     {
544         List classPathFiles = new ArrayList();       
545         
546         //if using the test classes, make sure they are first
547         //on the list
548         if (useTestClasspath && (testClassesDirectory != null))
549             classPathFiles.add(testClassesDirectory);
550         
551         if (getClassesDirectory() != null)
552             classPathFiles.add(getClassesDirectory());
553         
554         //now add all of the dependencies
555         classPathFiles.addAll(getDependencyFiles());
556         
557         if (getLog().isDebugEnabled())
558         {
559             for (int i = 0; i < classPathFiles.size(); i++)
560             {
561                 getLog().debug("classpath element: "+ ((File) classPathFiles.get(i)).getName());
562             }
563         }
564         return classPathFiles;
565     }
566 
567 }