View Javadoc

1   package org.codehaus.mojo.tomcat;
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 java.io.File;
23  import java.io.FileReader;
24  import java.io.FileWriter;
25  import java.io.IOException;
26  import java.io.StringWriter;
27  import java.util.Collection;
28  import java.util.List;
29  import java.util.Set;
30  
31  import org.apache.catalina.Context;
32  import org.apache.catalina.loader.WebappLoader;
33  import org.apache.catalina.startup.Embedded;
34  import org.apache.maven.artifact.Artifact;
35  import org.apache.maven.artifact.DependencyResolutionRequiredException;
36  import org.apache.maven.plugin.MojoExecutionException;
37  import org.apache.maven.project.MavenProject;
38  import org.codehaus.plexus.util.IOUtil;
39  import org.codehaus.plexus.util.StringUtils;
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  /**
46   * Runs the current project as a dynamic web application using an embedded Tomcat server.
47   * 
48   * @goal run
49   * @execute phase="compile"
50   * @requiresDependencyResolution runtime
51   * @author Jurgen Lust
52   * @author Mark Hobson <markhobson@gmail.com>
53   * @version $Id: RunMojo.java 13551 2011-02-09 16:05:47Z olamy $
54   */
55  public class RunMojo
56      extends AbstractRunMojo 
57  {
58      // ----------------------------------------------------------------------
59      // Mojo Parameters
60      // ----------------------------------------------------------------------
61  
62      /**
63       * The classes directory for the web application being run.
64       * 
65       * @parameter expression = "${project.build.outputDirectory}"
66       */
67      private File classesDir;
68  
69      /**
70       * The set of dependencies for the web application being run.
71       * 
72       * @parameter default-value = "${project.artifacts}"
73       * @required
74       * @readonly
75       */
76      private Set<Artifact> dependencies;
77  
78      /**
79       * The web resources directory for the web application being run.
80       * 
81       * @parameter expression="${basedir}/src/main/webapp"
82       */
83      private File warSourceDirectory;
84  
85      
86      /**
87       * Set the "follow standard delegation model" flag used to configure our ClassLoader.
88       * @see http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/loader/WebappLoader.html#setDelegate(boolean)
89       * @parameter expression = "${tomcat.delegate}" default-value="true"
90       * @since 1.0
91       */    
92      private boolean delegate = true;
93      
94      /**
95       * represents the delay in seconds between each classPathScanning change invocation
96       * @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>
97       * @parameter expression="${maven.tomcat.backgroundProcessorDelay}" default-value="-1"
98       */
99      protected int backgroundProcessorDelay = -1;    
100     
101     private File temporaryContextFile = null;
102 
103     // ----------------------------------------------------------------------
104     // AbstractRunMojo Implementation
105     // ----------------------------------------------------------------------
106 
107     /**
108      * {@inheritDoc}
109      * @throws MojoExecutionException 
110      */
111     @Override
112     protected Context createContext( Embedded container )
113         throws IOException, MojoExecutionException
114     {
115         Context context = super.createContext( container );
116 
117         context.setReloadable( isContextReloadable() );
118         
119         return context;
120     }
121 
122     /**
123      * {@inheritDoc}
124      * @throws MojoExecutionException 
125      */
126     @Override
127     protected WebappLoader createWebappLoader()
128         throws IOException, MojoExecutionException
129     {
130         WebappLoader loader = super.createWebappLoader();
131         //super.project.
132         if ( useSeparateTomcatClassLoader )
133         {
134             loader.setDelegate( delegate );
135         }
136                 
137         // add classes directories to loader
138         if ( classesDir != null )
139         {
140             try
141             {
142                 @SuppressWarnings("unchecked")
143                 List<String> classPathElements = project.getCompileClasspathElements();
144                 for (String classPathElement : classPathElements)
145                 {
146                     File classPathElementFile = new File(classPathElement);
147                     if (classPathElementFile.exists() && classPathElementFile.isDirectory())
148                     {
149                         getLog().debug( "adding classPathElementFile " + classPathElementFile.toURI().toString() );
150                         loader.addRepository( classPathElementFile.toURI().toString() );
151                     }
152                 }
153             }
154             catch ( DependencyResolutionRequiredException e )
155             {
156                 throw new MojoExecutionException( e.getMessage(), e );
157             }
158             
159             //loader.addRepository( classesDir.toURI().toString() );
160         }
161 
162         // add artifacts to loader
163         if ( dependencies != null )
164         {
165             for ( Artifact artifact : dependencies )
166             {
167                 String scope = artifact.getScope();
168 
169                 // skip provided and test scoped artifacts
170                 if ( !Artifact.SCOPE_PROVIDED.equals( scope ) && !Artifact.SCOPE_TEST.equals( scope ) )
171                 {
172                     getLog().debug(
173                                     "add dependency to webapploader " + artifact.getGroupId() + ":"
174                                         + artifact.getArtifactId() + ":" + artifact.getVersion() + ":"
175                                         + artifact.getScope() );
176                     if ( !isInProjectReferences( artifact ) )
177                     {
178                         loader.addRepository( artifact.getFile().toURI().toString() );
179                     }
180                     else
181                     {
182                         getLog().debug( "skip adding artifact " + artifact.getArtifactId() + " as it's in reactors" );
183                     }
184                 }
185             }
186         }
187 
188         return loader;
189     }
190     
191     protected boolean isInProjectReferences(Artifact artifact)
192     {
193         if ( project.getProjectReferences() == null || project.getProjectReferences().isEmpty() )
194         {
195             return false;
196         }
197         @SuppressWarnings("unchecked")
198         Collection<MavenProject> mavenProjects = project.getProjectReferences().values();
199         for ( MavenProject mavenProject : mavenProjects )
200         {
201             if (StringUtils.equals( mavenProject.getId(), artifact.getId() ))
202             {
203                 return true;
204             }
205         }
206         return false;
207     }
208 
209     /**
210      * {@inheritDoc}
211      */
212     @Override
213     protected File getDocBase()
214     {
215         return warSourceDirectory;
216     }
217 
218     /**
219      * {@inheritDoc}
220      */
221     @Override
222     protected File getContextFile() throws MojoExecutionException
223     {
224         if ( temporaryContextFile != null )
225         {
226             return temporaryContextFile;
227         }
228         //----------------------------------------------------------------------------
229         // context attributes backgroundProcessorDelay reloadable cannot be modifiec at runtime.
230         // It looks only values from the file ared used
231         // so here we create a temporary file with values modified
232         //----------------------------------------------------------------------------
233         FileReader fr = null;
234         FileWriter fw = null;
235         StringWriter sw = new StringWriter();
236         try
237         {
238             temporaryContextFile = File.createTempFile( "tomcat-maven-plugin", "temp-ctx-file" );
239             fw = new FileWriter( temporaryContextFile );
240             // format to modify/create <Context backgroundProcessorDelay="5" reloadable="false">
241             if ( contextFile != null && contextFile.exists() )
242             {
243                 fr = new FileReader( contextFile );
244                 Xpp3Dom xpp3Dom = Xpp3DomBuilder.build( fr );
245                 xpp3Dom.setAttribute( "backgroundProcessorDelay", Integer.toString( backgroundProcessorDelay ) );
246                 xpp3Dom.setAttribute( "reloadable", Boolean.toString( isContextReloadable() ) );
247                 Xpp3DomWriter.write( fw, xpp3Dom );
248                 Xpp3DomWriter.write( sw, xpp3Dom );
249                 getLog().debug( " generated context file " + sw.toString() );
250             }
251             else
252             {
253                 if ( contextReloadable )
254                 {
255                     // don't care about using a complicated xml api to create one xml line :-)
256                     StringBuilder sb = new StringBuilder( "<Context " ).append( "backgroundProcessorDelay=\"" )
257                         .append( Integer.toString( backgroundProcessorDelay ) ).append( "\"" )
258                         .append( " reloadable=\"" + Boolean.toString( isContextReloadable() ) + "\"/>" );
259                     
260                     getLog().debug( " generated context file " + sb.toString() );
261                     
262                     fw.write( sb.toString() );
263                 } else {
264                     // no user context file and contextReloadable false so no need about creating a hack one 
265                     return null;
266                 }
267             }
268         }
269         catch ( IOException e )
270         {
271             getLog().error( "error creating fake context.xml : " + e.getMessage(), e );
272             throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e );
273         }
274         catch ( XmlPullParserException e )
275         {
276             getLog().error( "error creating fake context.xml : " + e.getMessage(), e );
277             throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e );
278         } finally {
279             IOUtil.close( fw );
280             IOUtil.close( fr );
281             IOUtil.close( sw );
282         }        
283         
284         return temporaryContextFile;
285     }
286 
287 }