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