View Javadoc

1   package org.apache.tomcat.maven.plugin.tomcat6;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.catalina.Context;
23  import org.apache.catalina.loader.WebappLoader;
24  import org.apache.catalina.startup.Embedded;
25  import org.apache.commons.io.FileUtils;
26  import org.apache.maven.artifact.Artifact;
27  import org.apache.maven.plugin.MojoExecutionException;
28  import org.apache.tomcat.maven.common.run.ClassLoaderEntriesCalculator;
29  import org.apache.tomcat.maven.common.run.ClassLoaderEntriesCalculatorRequest;
30  import org.apache.tomcat.maven.common.run.ClassLoaderEntriesCalculatorResult;
31  import org.apache.tomcat.maven.common.run.TomcatRunException;
32  import org.codehaus.plexus.util.IOUtil;
33  import org.codehaus.plexus.util.xml.Xpp3Dom;
34  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
35  import org.codehaus.plexus.util.xml.Xpp3DomWriter;
36  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
37  
38  import java.io.File;
39  import java.io.FileReader;
40  import java.io.FileWriter;
41  import java.io.IOException;
42  import java.io.StringWriter;
43  import java.util.List;
44  import java.util.Set;
45  
46  /**
47   * Runs the current project as a dynamic web application using an embedded Tomcat server.
48   *
49   * @author Jurgen Lust
50   * @author Mark Hobson <markhobson@gmail.com>
51   * @version $Id: RunMojo.html 1305321 2012-03-26 12:01:12Z olamy $
52   * @goal run
53   * @execute phase="compile"
54   * @requiresDependencyResolution test
55   */
56  public class RunMojo
57      extends AbstractRunMojo
58  {
59      // ----------------------------------------------------------------------
60      // Mojo Parameters
61      // ----------------------------------------------------------------------
62  
63  
64      /**
65       * The set of dependencies for the web application being run.
66       *
67       * @parameter default-value = "${project.artifacts}"
68       * @required
69       * @readonly
70       */
71      private Set<Artifact> dependencies;
72  
73      /**
74       * The web resources directory for the web application being run.
75       *
76       * @parameter default-value="${basedir}/src/main/webapp" expression = "${tomcat.warSourceDirectory}"
77       */
78      private File warSourceDirectory;
79  
80  
81      /**
82       * Set the "follow standard delegation model" flag used to configure our ClassLoader.
83       *
84       * @parameter expression = "${tomcat.delegate}" default-value="true"
85       * @see http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/loader/WebappLoader.html#setDelegate(boolean)
86       * @since 1.0
87       */
88      private boolean delegate = true;
89  
90      /**
91       * represents the delay in seconds between each classPathScanning change invocation
92       *
93       * @parameter expression="${maven.tomcat.backgroundProcessorDelay}" default-value="-1"
94       * @see <a href="http://tomcat.apache.org/tomcat-6.0-doc/config/context.html">http://tomcat.apache.org/tomcat-6.0-doc/config/context.html</a>
95       */
96      protected int backgroundProcessorDelay = -1;
97  
98      /**
99       * @readonly
100      * @component
101      * @since 2.0
102      */
103     private ClassLoaderEntriesCalculator classLoaderEntriesCalculator;
104 
105     /**
106      * will add /WEB-INF/lib/*.jar and /WEB-INF/classes from war dependencies in the webappclassloader
107      *
108      * @parameter expression="${maven.tomcat.addWarDependenciesInClassloader}" default-value="true"
109      * @since 2.0
110      */
111     private boolean addWarDependenciesInClassloader;
112 
113     /**
114      * will use the test classpath rather than the compile one and will add test dependencies too
115      *
116      * @parameter expression="${maven.tomcat.useTestClasspath}" default-value="false"
117      * @since 2.0
118      */
119     private boolean useTestClasspath;
120 
121     /**
122      * Additional optional directories to add to the embedded tomcat classpath.
123      *
124      * @parameter alias = "additionalClassesDirs"
125      * @since 2.0
126      */
127     private List<File> additionalClasspathDirs;
128 
129     private File temporaryContextFile = null;
130 
131     // ----------------------------------------------------------------------
132     // AbstractRunMojo Implementation
133     // ----------------------------------------------------------------------
134 
135     /**
136      * {@inheritDoc}
137      *
138      * @throws MojoExecutionException
139      */
140     @Override
141     protected Context createContext( Embedded container )
142         throws IOException, MojoExecutionException
143     {
144         Context context = super.createContext( container );
145 
146         context.setReloadable( isContextReloadable() );
147 
148         return context;
149     }
150 
151     /**
152      * {@inheritDoc}
153      *
154      * @throws MojoExecutionException
155      */
156     @Override
157     protected WebappLoader createWebappLoader()
158         throws IOException, MojoExecutionException
159     {
160         WebappLoader loader = super.createWebappLoader();
161         if ( useSeparateTomcatClassLoader )
162         {
163             loader.setDelegate( delegate );
164         }
165 
166         try
167         {
168             ClassLoaderEntriesCalculatorRequest request =
169                 new ClassLoaderEntriesCalculatorRequest().setDependencies( dependencies ).setLog(
170                     getLog() ).setMavenProject( project ).setAddWarDependenciesInClassloader(
171                     addWarDependenciesInClassloader ).setUseTestClassPath( useTestClasspath );
172             ClassLoaderEntriesCalculatorResult classLoaderEntriesCalculatorResult =
173                 classLoaderEntriesCalculator.calculateClassPathEntries( request );
174             List<String> classLoaderEntries = classLoaderEntriesCalculatorResult.getClassPathEntries();
175             final List<File> tmpDirectories = classLoaderEntriesCalculatorResult.getTmpDirectories();
176 
177             Runtime.getRuntime().addShutdownHook( new Thread()
178             {
179                 @Override
180                 public void run()
181                 {
182                     for ( File tmpDir : tmpDirectories )
183                     {
184                         try
185                         {
186                             FileUtils.deleteDirectory( tmpDir );
187                         }
188                         catch ( IOException e )
189                         {
190                             // ignore
191                         }
192                     }
193                 }
194             } );
195 
196             if ( classLoaderEntries != null )
197             {
198                 for ( String classLoaderEntry : classLoaderEntries )
199                 {
200                     loader.addRepository( classLoaderEntry );
201                 }
202             }
203             if ( additionalClasspathDirs != null && !additionalClasspathDirs.isEmpty() )
204             {
205                 for ( File additionalClasspathDir : additionalClasspathDirs )
206                 {
207                     if ( additionalClasspathDir.exists() )
208                     {
209                         loader.addRepository( additionalClasspathDir.toURI().toString() );
210                     }
211                 }
212             }
213         }
214         catch ( TomcatRunException e )
215         {
216             throw new MojoExecutionException( e.getMessage(), e );
217         }
218 
219         return loader;
220     }
221 
222 
223     /**
224      * {@inheritDoc}
225      */
226     @Override
227     protected File getDocBase()
228     {
229         return warSourceDirectory;
230     }
231 
232     /**
233      * {@inheritDoc}
234      */
235     @Override
236     protected File getContextFile()
237         throws MojoExecutionException
238     {
239         if ( temporaryContextFile != null )
240         {
241             return temporaryContextFile;
242         }
243         //----------------------------------------------------------------------------
244         // context attributes backgroundProcessorDelay reloadable cannot be modified at runtime.
245         // It looks only values from the file ared used
246         // so here we create a temporary file with values modified
247         //----------------------------------------------------------------------------
248         FileReader fr = null;
249         FileWriter fw = null;
250         StringWriter sw = new StringWriter();
251         try
252         {
253             temporaryContextFile = File.createTempFile( "tomcat-maven-plugin", "temp-ctx-file" );
254             temporaryContextFile.deleteOnExit();
255             fw = new FileWriter( temporaryContextFile );
256             // format to modify/create <Context backgroundProcessorDelay="5" reloadable="false">
257             if ( contextFile != null && contextFile.exists() )
258             {
259                 fr = new FileReader( contextFile );
260                 Xpp3Dom xpp3Dom = Xpp3DomBuilder.build( fr );
261                 xpp3Dom.setAttribute( "backgroundProcessorDelay", Integer.toString( backgroundProcessorDelay ) );
262                 xpp3Dom.setAttribute( "reloadable", Boolean.toString( isContextReloadable() ) );
263                 Xpp3DomWriter.write( fw, xpp3Dom );
264                 Xpp3DomWriter.write( sw, xpp3Dom );
265                 getLog().debug( " generated context file " + sw.toString() );
266             }
267             else
268             {
269                 if ( contextReloadable )
270                 {
271                     // don't care about using a complicated xml api to create one xml line :-)
272                     StringBuilder sb = new StringBuilder( "<Context " ).append( "backgroundProcessorDelay=\"" ).append(
273                         Integer.toString( backgroundProcessorDelay ) ).append( "\"" ).append(
274                         " reloadable=\"" + Boolean.toString( isContextReloadable() ) + "\"/>" );
275 
276                     getLog().debug( " generated context file " + sb.toString() );
277 
278                     fw.write( sb.toString() );
279                 }
280                 else
281                 {
282                     // no user context file and contextReloadable false so no need about creating a hack one 
283                     return null;
284                 }
285             }
286         }
287         catch ( IOException e )
288         {
289             getLog().error( "error creating fake context.xml : " + e.getMessage(), e );
290             throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e );
291         }
292         catch ( XmlPullParserException e )
293         {
294             getLog().error( "error creating fake context.xml : " + e.getMessage(), e );
295             throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e );
296         }
297         finally
298         {
299             IOUtil.close( fw );
300             IOUtil.close( fr );
301             IOUtil.close( sw );
302         }
303 
304         return temporaryContextFile;
305     }
306 
307 }