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.commons.lang.StringUtils;
27  import org.apache.maven.artifact.Artifact;
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.plugins.annotations.Component;
30  import org.apache.maven.plugins.annotations.Execute;
31  import org.apache.maven.plugins.annotations.LifecyclePhase;
32  import org.apache.maven.plugins.annotations.Mojo;
33  import org.apache.maven.plugins.annotations.Parameter;
34  import org.apache.maven.plugins.annotations.ResolutionScope;
35  import org.apache.tomcat.maven.common.run.ClassLoaderEntriesCalculator;
36  import org.apache.tomcat.maven.common.run.ClassLoaderEntriesCalculatorRequest;
37  import org.apache.tomcat.maven.common.run.ClassLoaderEntriesCalculatorResult;
38  import org.apache.tomcat.maven.common.run.TomcatRunException;
39  import org.codehaus.plexus.util.IOUtil;
40  import org.codehaus.plexus.util.xml.Xpp3Dom;
41  import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
42  import org.codehaus.plexus.util.xml.Xpp3DomWriter;
43  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
44  
45  import java.io.File;
46  import java.io.FileReader;
47  import java.io.FileWriter;
48  import java.io.IOException;
49  import java.io.StringWriter;
50  import java.util.List;
51  import java.util.Set;
52  
53  /**
54   * Runs the current project as a dynamic web application using an embedded Tomcat server.
55   *
56   * @author Jurgen Lust
57   * @author Mark Hobson <markhobson@gmail.com>
58   */
59  @Mojo( name = "run", requiresDependencyResolution = ResolutionScope.TEST )
60  @Execute( phase = LifecyclePhase.COMPILE )
61  public class RunMojo
62      extends AbstractRunMojo
63  {
64      // ----------------------------------------------------------------------
65      // Mojo Parameters
66      // ----------------------------------------------------------------------
67  
68  
69      /**
70       * The set of dependencies for the web application being run.
71       */
72      @Parameter( defaultValue = "${project.artifacts}", required = true, readonly = true )
73      private Set<Artifact> dependencies;
74  
75      /**
76       * The web resources directory for the web application being run.
77       */
78      @Parameter( defaultValue = "${basedir}/src/main/webapp", property = "tomcat.warSourceDirectory" )
79      private File warSourceDirectory;
80  
81  
82      /**
83       * Set the "follow standard delegation model" flag used to configure our ClassLoader.
84       *
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      @Parameter( property = "tomcat.delegate", defaultValue = "true" )
89      private boolean delegate = true;
90  
91      /**
92       * represents the delay in seconds between each classPathScanning change invocation
93       *
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      @Parameter( property = "maven.tomcat.backgroundProcessorDelay", defaultValue = "-1" )
97      protected int backgroundProcessorDelay = -1;
98  
99      /**
100      * @since 2.0
101      */
102     @Component( role = ClassLoaderEntriesCalculator.class )
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      * @since 2.0
109      */
110     @Parameter( property = "maven.tomcat.addWarDependenciesInClassloader", defaultValue = "true" )
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      * @since 2.0
117      */
118     @Parameter( property = "maven.tomcat.useTestClasspath", defaultValue = "false" )
119     private boolean useTestClasspath;
120 
121     /**
122      * Additional optional directories to add to the embedded tomcat classpath.
123      *
124      * @since 2.0-beta-1
125      */
126     @Parameter( alias = "additionalClassesDirs" )
127     private List<String> additionalClasspathDirs;
128 
129     // ----------------------------------------------------------------------
130     // AbstractRunMojo Implementation
131     // ----------------------------------------------------------------------
132 
133     /**
134      * {@inheritDoc}
135      *
136      * @throws MojoExecutionException
137      */
138     @Override
139     protected Context createContext( Embedded container )
140         throws IOException, MojoExecutionException
141     {
142         Context context = super.createContext( container );
143 
144         context.setReloadable( isContextReloadable() );
145 
146         return context;
147     }
148 
149     /**
150      * {@inheritDoc}
151      *
152      * @throws MojoExecutionException
153      */
154     @Override
155     protected WebappLoader createWebappLoader()
156         throws IOException, MojoExecutionException
157     {
158         WebappLoader loader = super.createWebappLoader();
159         if ( useSeparateTomcatClassLoader )
160         {
161             loader.setDelegate( delegate );
162         }
163 
164         try
165         {
166             ClassLoaderEntriesCalculatorRequest request =
167                 new ClassLoaderEntriesCalculatorRequest().setDependencies( dependencies ).setLog(
168                     getLog() ).setMavenProject( project ).setAddWarDependenciesInClassloader(
169                     addWarDependenciesInClassloader ).setUseTestClassPath( useTestClasspath );
170             ClassLoaderEntriesCalculatorResult classLoaderEntriesCalculatorResult =
171                 classLoaderEntriesCalculator.calculateClassPathEntries( request );
172             List<String> classLoaderEntries = classLoaderEntriesCalculatorResult.getClassPathEntries();
173             final List<File> tmpDirectories = classLoaderEntriesCalculatorResult.getTmpDirectories();
174 
175             Runtime.getRuntime().addShutdownHook( new Thread()
176             {
177                 @Override
178                 public void run()
179                 {
180                     for ( File tmpDir : tmpDirectories )
181                     {
182                         try
183                         {
184                             FileUtils.deleteDirectory( tmpDir );
185                         }
186                         catch ( IOException e )
187                         {
188                             // ignore
189                         }
190                     }
191                 }
192             } );
193 
194             if ( classLoaderEntries != null )
195             {
196                 for ( String classLoaderEntry : classLoaderEntries )
197                 {
198                     loader.addRepository( classLoaderEntry );
199                 }
200             }
201             if ( additionalClasspathDirs != null && !additionalClasspathDirs.isEmpty() )
202             {
203                 for ( String additionalClasspathDir : additionalClasspathDirs )
204                 {
205                     if ( StringUtils.isNotBlank( additionalClasspathDir ) )
206                     {
207                         File file = new File( additionalClasspathDir );
208                         if ( file.exists() )
209                         {
210                             String fileUri = file.toURI().toString();
211                             getLog().debug( "add file:" + fileUri + " as a additionalClasspathDir" );
212                             loader.addRepository( fileUri );
213                         }
214                     }
215                 }
216             }
217         }
218         catch ( TomcatRunException e )
219         {
220             throw new MojoExecutionException( e.getMessage(), e );
221         }
222 
223         return loader;
224     }
225 
226 
227     /**
228      * {@inheritDoc}
229      */
230     @Override
231     protected File getDocBase()
232     {
233         return warSourceDirectory;
234     }
235 
236     /**
237      * {@inheritDoc}
238      */
239     @Override
240     protected File getContextFile()
241         throws MojoExecutionException
242     {
243         File temporaryContextFile = null;
244 
245         //----------------------------------------------------------------------------
246         // context attributes backgroundProcessorDelay reloadable cannot be modified at runtime.
247         // It looks only values from the file ared used
248         // so here we create a temporary file with values modified
249         //----------------------------------------------------------------------------
250         FileReader fr = null;
251         FileWriter fw = null;
252         StringWriter sw = new StringWriter();
253         try
254         {
255             temporaryContextFile = File.createTempFile( "tomcat-maven-plugin", "temp-ctx-file" );
256             temporaryContextFile.deleteOnExit();
257             fw = new FileWriter( temporaryContextFile );
258             // format to modify/create <Context backgroundProcessorDelay="5" reloadable="false">
259             if ( contextFile != null && contextFile.exists() )
260             {
261                 fr = new FileReader( contextFile );
262                 Xpp3Dom xpp3Dom = Xpp3DomBuilder.build( fr );
263                 xpp3Dom.setAttribute( "backgroundProcessorDelay", Integer.toString( backgroundProcessorDelay ) );
264                 xpp3Dom.setAttribute( "reloadable", Boolean.toString( isContextReloadable() ) );
265                 Xpp3DomWriter.write( fw, xpp3Dom );
266                 Xpp3DomWriter.write( sw, xpp3Dom );
267                 getLog().debug( " generated context file " + sw.toString() );
268             }
269             else
270             {
271                 if ( contextReloadable )
272                 {
273                     // don't care about using a complicated xml api to create one xml line :-)
274                     StringBuilder sb = new StringBuilder( "<Context " ).append( "backgroundProcessorDelay=\"" ).append(
275                         Integer.toString( backgroundProcessorDelay ) ).append( "\"" ).append(
276                         " reloadable=\"" + Boolean.toString( isContextReloadable() ) + "\"/>" );
277 
278                     getLog().debug( " generated context file " + sb.toString() );
279 
280                     fw.write( sb.toString() );
281                 }
282                 else
283                 {
284                     // no user context file and contextReloadable false so no need about creating a hack one 
285                     return null;
286                 }
287             }
288         }
289         catch ( IOException e )
290         {
291             getLog().error( "error creating fake context.xml : " + e.getMessage(), e );
292             throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e );
293         }
294         catch ( XmlPullParserException e )
295         {
296             getLog().error( "error creating fake context.xml : " + e.getMessage(), e );
297             throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e );
298         }
299         finally
300         {
301             IOUtil.close( fw );
302             IOUtil.close( fr );
303             IOUtil.close( sw );
304         }
305 
306         return temporaryContextFile;
307     }
308 
309 }