View Javadoc

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