package org.codehaus.mojo.tomcat; /* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.StringWriter; import java.util.Collection; import java.util.List; import java.util.Set; import org.apache.catalina.Context; import org.apache.catalina.loader.WebappLoader; import org.apache.catalina.startup.Embedded; import org.apache.maven.artifact.Artifact; import org.apache.maven.artifact.DependencyResolutionRequiredException; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.project.MavenProject; import org.codehaus.plexus.util.IOUtil; import org.codehaus.plexus.util.StringUtils; import org.codehaus.plexus.util.xml.Xpp3Dom; import org.codehaus.plexus.util.xml.Xpp3DomBuilder; import org.codehaus.plexus.util.xml.Xpp3DomWriter; import org.codehaus.plexus.util.xml.pull.XmlPullParserException; /** * Runs the current project as a dynamic web application using an embedded Tomcat server. * * @goal run * @execute phase="compile" * @requiresDependencyResolution runtime * @author Jurgen Lust * @author Mark Hobson <markhobson@gmail.com> * @version $Id$ */ public class RunMojo extends AbstractRunMojo { // ---------------------------------------------------------------------- // Mojo Parameters // ---------------------------------------------------------------------- /** * The classes directory for the web application being run. * * @parameter expression = "${project.build.outputDirectory}" */ private File classesDir; /** * The set of dependencies for the web application being run. * * @parameter default-value = "${project.artifacts}" * @required * @readonly */ private Set<Artifact> dependencies; /** * The web resources directory for the web application being run. * * @parameter expression="${basedir}/src/main/webapp" */ private File warSourceDirectory; /** * Set the "follow standard delegation model" flag used to configure our ClassLoader. * @see http://tomcat.apache.org/tomcat-6.0-doc/api/org/apache/catalina/loader/WebappLoader.html#setDelegate(boolean) * @parameter expression = "${tomcat.delegate}" default-value="true" * @since 1.0 */ private boolean delegate = true; /** * represents the delay in seconds between each classPathScanning change invocation * @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> * @parameter expression="${maven.tomcat.backgroundProcessorDelay}" default-value="-1" */ protected int backgroundProcessorDelay = -1; private File temporaryContextFile = null; // ---------------------------------------------------------------------- // AbstractRunMojo Implementation // ---------------------------------------------------------------------- /** * {@inheritDoc} * @throws MojoExecutionException */ @Override protected Context createContext( Embedded container ) throws IOException, MojoExecutionException { Context context = super.createContext( container ); context.setReloadable( isContextReloadable() ); return context; } /** * {@inheritDoc} * @throws MojoExecutionException */ @Override protected WebappLoader createWebappLoader() throws IOException, MojoExecutionException { WebappLoader loader = super.createWebappLoader(); //super.project. if ( useSeparateTomcatClassLoader ) { loader.setDelegate( delegate ); } // add classes directories to loader if ( classesDir != null ) { try { @SuppressWarnings("unchecked") List<String> classPathElements = project.getCompileClasspathElements(); for (String classPathElement : classPathElements) { File classPathElementFile = new File(classPathElement); if (classPathElementFile.exists() && classPathElementFile.isDirectory()) { getLog().debug( "adding classPathElementFile " + classPathElementFile.toURI().toString() ); loader.addRepository( classPathElementFile.toURI().toString() ); } } } catch ( DependencyResolutionRequiredException e ) { throw new MojoExecutionException( e.getMessage(), e ); } //loader.addRepository( classesDir.toURI().toString() ); } // add artifacts to loader if ( dependencies != null ) { for ( Artifact artifact : dependencies ) { String scope = artifact.getScope(); // skip provided and test scoped artifacts if ( !Artifact.SCOPE_PROVIDED.equals( scope ) && !Artifact.SCOPE_TEST.equals( scope ) ) { getLog().debug( "add dependency to webapploader " + artifact.getGroupId() + ":" + artifact.getArtifactId() + ":" + artifact.getVersion() + ":" + artifact.getScope() ); if ( !isInProjectReferences( artifact ) ) { loader.addRepository( artifact.getFile().toURI().toString() ); } else { getLog().debug( "skip adding artifact " + artifact.getArtifactId() + " as it's in reactors" ); } } } } return loader; } protected boolean isInProjectReferences(Artifact artifact) { if ( project.getProjectReferences() == null || project.getProjectReferences().isEmpty() ) { return false; } @SuppressWarnings("unchecked") Collection<MavenProject> mavenProjects = project.getProjectReferences().values(); for ( MavenProject mavenProject : mavenProjects ) { if (StringUtils.equals( mavenProject.getId(), artifact.getId() )) { return true; } } return false; } /** * {@inheritDoc} */ @Override protected File getDocBase() { return warSourceDirectory; } /** * {@inheritDoc} */ @Override protected File getContextFile() throws MojoExecutionException { if ( temporaryContextFile != null ) { return temporaryContextFile; } //---------------------------------------------------------------------------- // context attributes backgroundProcessorDelay reloadable cannot be modifiec at runtime. // It looks only values from the file ared used // so here we create a temporary file with values modified //---------------------------------------------------------------------------- FileReader fr = null; FileWriter fw = null; StringWriter sw = new StringWriter(); try { temporaryContextFile = File.createTempFile( "tomcat-maven-plugin", "temp-ctx-file" ); fw = new FileWriter( temporaryContextFile ); // format to modify/create <Context backgroundProcessorDelay="5" reloadable="false"> if ( contextFile != null && contextFile.exists() ) { fr = new FileReader( contextFile ); Xpp3Dom xpp3Dom = Xpp3DomBuilder.build( fr ); xpp3Dom.setAttribute( "backgroundProcessorDelay", Integer.toString( backgroundProcessorDelay ) ); xpp3Dom.setAttribute( "reloadable", Boolean.toString( isContextReloadable() ) ); Xpp3DomWriter.write( fw, xpp3Dom ); Xpp3DomWriter.write( sw, xpp3Dom ); getLog().debug( " generated context file " + sw.toString() ); } else { if ( contextReloadable ) { // don't care about using a complicated xml api to create one xml line :-) StringBuilder sb = new StringBuilder( "<Context " ).append( "backgroundProcessorDelay=\"" ) .append( Integer.toString( backgroundProcessorDelay ) ).append( "\"" ) .append( " reloadable=\"" + Boolean.toString( isContextReloadable() ) + "\"/>" ); getLog().debug( " generated context file " + sb.toString() ); fw.write( sb.toString() ); } else { // no user context file and contextReloadable false so no need about creating a hack one return null; } } } catch ( IOException e ) { getLog().error( "error creating fake context.xml : " + e.getMessage(), e ); throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e ); } catch ( XmlPullParserException e ) { getLog().error( "error creating fake context.xml : " + e.getMessage(), e ); throw new MojoExecutionException( "error creating fake context.xml : " + e.getMessage(), e ); } finally { IOUtil.close( fw ); IOUtil.close( fr ); IOUtil.close( sw ); } return temporaryContextFile; } }