package org.codehaus.mojo.sysdeo;
/*
* 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.Writer;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.apache.commons.io.FileUtils;
import org.apache.maven.plugin.MojoExecutionException;
import org.codehaus.mojo.sysdeo.ide.AbstractIdeSupportMojo;
import org.codehaus.mojo.sysdeo.ide.IdeDependency;
import org.codehaus.mojo.sysdeo.ide.IdeUtils;
import org.codehaus.plexus.util.xml.Xpp3Dom;
import org.codehaus.plexus.util.xml.Xpp3DomBuilder;
import org.codehaus.plexus.util.xml.Xpp3DomWriter;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
/**
* Generates the required configuration files for the eclipse Sysdeo-tomcat plugin
*
* @goal generate
*/
public class SysdeoMojo
extends AbstractIdeSupportMojo
{
private static final String TOMCAT_PLUGIN = ".tomcatplugin";
/**
* Single directory for extra files to include in the WAR.
*
* @parameter expression="${basedir}/src/main/webapp"
* @required
*/
private File warSourceDirectory;
/**
* Application context definition for Tomcat.
*
* @parameter expression="${basedir}/src/main/webapp/META-INF/context.xml"
* @required
*/
private File contextDefinition;
/**
* Web application context path
*
* @parameter default-value="${project.build.finalName}"
*/
private String webPath;
/**
* Use the M2_REPO Classpath Variable to point to dependencies in the local repository. MUST be set to false to use
* this plugin with m2eclipse project import configurator.
*
* @parameter default-value="true"
*/
private boolean useClasspathVariable;
private List ignoreArtifact;
/**
* define artifacts to exclude from the devloader classpath as they create conflicts (LinkageErrors) with Tomcat
* classes and APIs
*
* @parameter
*/
private String[] ignoredArtifacts;
public SysdeoMojo()
{
ignoreArtifact = new ArrayList();
ignoreArtifact.add( "jsp-api" );
ignoreArtifact.add( "el-api" );
ignoreArtifact.add( "servlet-api" );
ignoreArtifact.add( "gwt-user" );
}
protected boolean getUseProjectReferences()
{
return true;
}
protected boolean setup()
{
if ( ignoredArtifacts != null )
{
for ( int i = 0; i < ignoredArtifacts.length; i++ )
{
ignoreArtifact.add( ignoredArtifacts[i] );
}
}
if ( !getProject().getPackaging().equals( "war" ) )
{
getLog().info( "Not executing sysdeo-tomcat plugin, this is project is not a war package" );
return false;
}
return true;
}
protected void writeConfiguration( IdeDependency[] dependencies )
throws MojoExecutionException
{
File webclasspath = new File( warSourceDirectory, ".#webclasspath" );
if ( webclasspath.exists() )
{
webclasspath.delete();
}
File projectDir = getProject().getBasedir();
forceTomcatNature( projectDir );
List referencedProjects = new ArrayList();
List jarDependencies = new ArrayList();
List systemDependencyPaths = new ArrayList();
IdeDependency thisDependency = new IdeDependency();
thisDependency.setGroupId( getProject().getGroupId() );
thisDependency.setArtifactId( getProject().getArtifactId() );
thisDependency.setVersion( getProject().getVersion() );
thisDependency = resolveWorkspaceProject( thisDependency );
thisDependency.setOutputDirectory( getOutputDirectory() );
referencedProjects.add( thisDependency );
for ( int i = 0; i < dependencies.length; i++ )
{
IdeDependency dependency = dependencies[i];
if ( dependency.isProvided() || dependency.isTestDependency() )
{
// Skip this dependency
continue;
}
if ( dependency.isReferencedProject() || getWorkspaceProjects().contains( dependency ) )
{
referencedProjects.add( dependency );
}
else if ( dependency.isSystemScoped() )
{
String absolutePath = dependency.getFile().getAbsolutePath();
String osName = System.getProperty( "os.name" );
if ( osName.toUpperCase().indexOf( "WINDOWS" ) >= 0 )
{
absolutePath = absolutePath.substring( 0, 1 ).toUpperCase() + absolutePath.substring( 1 );
absolutePath = absolutePath.replace( '\\', '/' );
}
systemDependencyPaths.add( absolutePath );
}
else if ( dependency.getType().equalsIgnoreCase( "jar" )
&& !ignoreArtifact.contains( dependency.getArtifactId() ) )
{
jarDependencies.add( dependency );
}
}
String extraContext = "";
try
{
String contextString = FileUtils.readFileToString( contextDefinition, "UTF-8" );
int context = contextString.indexOf( "Context" );
int start = contextString.indexOf( ">", context );
int stop = contextString.indexOf( "</Context>" );
extraContext = contextString.substring( start + 1, stop );
extraContext = URLEncoder.encode( extraContext );
}
catch ( Exception e )
{
getLog().info( "No valid context file found at: " + contextDefinition );
}
Configuration cfg = new Configuration();
cfg.setClassForTemplateLoading( SysdeoMojo.class, "" );
Map context = new HashMap();
context.put( "referencedProjects", referencedProjects );
context.put( "jarDependencies", jarDependencies );
context.put( "systemDependencyPaths", systemDependencyPaths );
context.put( "webPath", webPath );
context.put( "localRepository", useClasspathVariable ? "M2_REPO" : getLocalRepository().getBasedir() );
context.put( "warSourceDir", IdeUtils.toRelativeAndFixSeparator( getProject().getBasedir(),
getWarSourceDirectory(), false ) );
context.put( "extraContext", extraContext );
File tomcatPluginFile = new File( projectDir, TOMCAT_PLUGIN );
try
{
Writer configWriter = new FileWriter( tomcatPluginFile );
Template template = cfg.getTemplate( "tomcatplugin.fm" );
template.process( context, configWriter );
configWriter.flush();
configWriter.close();
getLog().info( "Write tomcat plugin file to: " + tomcatPluginFile.getAbsolutePath() );
}
catch ( IOException ioe )
{
throw new MojoExecutionException( "Unable to write tomcat plugin config file", ioe );
}
catch ( TemplateException te )
{
throw new MojoExecutionException( "Unable to merge freemarker template", te );
}
}
protected void forceTomcatNature( File projectDir )
{
try
{
File dotProject = new File( projectDir, ".project" );
String content = FileUtils.readFileToString( dotProject, null );
if ( content.indexOf( "<nature>com.sysdeo.eclipse.tomcat.tomcatnature</nature>" ) < 0 )
{
getLog().info( "Add tomcat nature to the eclipse .project file" );
try
{
Xpp3Dom dom = Xpp3DomBuilder.build( new FileReader( dotProject ) );
Xpp3Dom nature = new Xpp3Dom( "nature" );
nature.setValue( "com.sysdeo.eclipse.tomcat.tomcatnature" );
dom.getChild( "natures" ).addChild( nature );
FileWriter writer = new FileWriter( dotProject );
Xpp3DomWriter.write( writer, dom );
writer.close();
}
catch ( Exception e )
{
getLog().warn( "Failed to add missing tomcat nature to the eclipse .project file", e );
}
}
// Required to force devloader classpath refresh on POM changes
File dotClassPath = new File( projectDir, ".classpath" );
FileUtils.touch( dotClassPath );
}
catch ( IOException e )
{
getLog().info( "Failed to retrieve the Eclipse .project file" );
}
}
/**
* @return
*/
protected String getOutputDirectory()
{
String relative = getProject().getFile().getParent();
String buildOutputDirectory = getProject().getBuild().getOutputDirectory();
return buildOutputDirectory.substring( relative.length() + 1 ).replace( '\\', '/' );
}
public File getContextDefinition()
{
return contextDefinition;
}
public void setContextDefinition( File contextDefinition )
{
this.contextDefinition = contextDefinition;
}
public File getWarSourceDirectory()
{
return warSourceDirectory;
}
public void setWarSourceDirectory( File warSourceDirectory )
{
this.warSourceDirectory = warSourceDirectory;
}
/**
* @param useClasspathVariable the useClasspathVariable to set
*/
public void setUseClasspathVariable( boolean useClasspathVariable )
{
this.useClasspathVariable = useClasspathVariable;
}
}