package org.apache.myfaces.maven.plugin;
/*
* 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.PrintWriter;
import java.io.StringWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import java.util.ArrayList;
import org.apache.maven.project.MavenProject;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.artifact.DependencyResolutionRequiredException;
import org.codehaus.plexus.compiler.util.scan.SourceInclusionScanner;
import org.codehaus.plexus.compiler.javac.Commandline;
import org.codehaus.plexus.util.cli.DefaultConsumer;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.Os;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.FileUtils;
/**
* @author <a href="mailto:jubu@volny.cz">Juraj Burian</a>
* @version $Id: AbstractAPTMojo.java 949569 2010-05-30 20:44:27Z bommel $
*/
public abstract class AbstractAPTMojo extends AbstractMojo
{
/**
* PATH_SEPARATOR.
*/
private static final String PATH_SEPARATOR = System.getProperty( "path.separator" );
/**
* FILE_SEPARATOR.
*/
private static final String FILE_SEPARATOR = System.getProperty( "file.separator" );
/**
* Integer returned by the Apt compiler to indicate success.
*/
private static final int APT_COMPILER_SUCCESS = 0;
/**
* class in tools.jar that implements APT
*/
private static final String APT_ENTRY_POINT = "com.sun.tools.apt.Main";
/**
* method used for apt.
*/
private static final String APT_METHOD_NAME = "process";
/**
* old method used for apt.
*/
private static final String APT_METHOD_NAME_OLD = "compile";
/**
* store info about modification of system classpath for Apt compiler
*/
private static boolean isClasspathModified;
/**
* Working directory when APT compiler is forked
* @parameter default-value="${basedir}"
* @since 1.0.10
*/
private File workingDir;
/**
* A List of targetFiles for SingleSourceTargetMapping
*
* @parameter
*/
private List targetFiles;
/**
* enables resource filtering for generated resources
*
* @parameter default-value="false"
*/
private boolean resourceFiltering;
/**
* targetPath for generated resources
*
* @parameter
*/
private String resourceTargetPath;
/**
* Whether to include debugging information in the compiled class files. The
* default value is true.
*
* @parameter expression="${maven.compiler.debug}" default-value="true"
* @readonly
*/
private boolean debug;
/**
* Comma separated list of "-A" options: Next two examples are equivalent:
*
* <pre>
* <A>-Adebug,-Aloglevel=3</A>
* </pre>
* <pre>
* <A>debug, loglevel=3</A>
* </pre>
*
* @parameter alias="A"
*/
private String aptOptions;
/**
* Output source locations where deprecated APIs are used
*
* @parameter
*/
private boolean showDeprecation;
/**
* Output warnings
*
* @parameter
*/
private boolean showWarnings;
/**
* The -encoding argument for the Apt
*
* @parameter
*/
private String encoding;
/**
* run Apt in verbode mode
*
* @parameter expression="${verbose}" default-value="false"
*/
private boolean verbose;
/**
* The -nocompile argument for the Apt
*
* @parameter default-value="false"
*/
private boolean nocompile;
/**
* The granularity in milliseconds of the last modification date for testing
* whether a source needs recompilation
*
* @parameter expression="${lastModGranularityMs}" default-value="0"
*/
private int staleMillis;
/**
* Name of AnnotationProcessorFactory to use; bypasses default discovery
* process
*
* @parameter
*/
private String factory;
/**
* Temporary directory that contain the files from the plugin.
*
* @parameter expression="${project.build.directory}/maven-apt-plugin"
* @required
* @readonly
*/
private File tempRoot;
/**
* The directory to run the compiler from if fork is true.
*
* @parameter expression="${basedir}"
* @required
* @readonly
*/
private File basedir;
/**
* Allows running the compiler in a separate process.
* If "false" it uses the built in compiler, while if "true" it will use an executable.
*
* @parameter default-value="false"
*/
private boolean fork;
/**
* Force apt call without staleness checking.
*
* @parameter default-value="false"
*/
private boolean force;
/**
* The maven project.
*
* @parameter expression="${project}"
* @required
* @readonly
*/
private MavenProject project;
/**
* The maven project.
* @return MavenProject
*/
public MavenProject getProject()
{
return project;
}
/**
* Force apt call without staleness checking.
* @return force
*/
public boolean isForce()
{
return force;
}
/**
* run Apt in verbode mode
* @return verbose
*/
public boolean isVerbose()
{
return verbose;
}
/**
* The granularity in milliseconds of the last modification date for testing
* whether a source needs recompilation
* @return staleMillis
*/
public int getStaleMillis()
{
return staleMillis;
}
/**
* A List of targetFiles for SingleSourceTargetMapping
* @return a List of TargetFiles
*/
protected List getTargetFiles()
{
return targetFiles;
}
/**
* enables resource filtering for generated resources
* @return resourceFiltering
*/
protected boolean isResourceFiltering()
{
return resourceFiltering;
}
/**
* targetPath for generated resources
* @return resouceTargetPath
*/
protected String getResourceTargetPath()
{
return resourceTargetPath;
}
/**
* classpath elements.
* @return a List of classPathElements
*/
protected abstract List getClasspathElements();
/**
* The source directories containing the sources to be compiled.
* @return a List of CompileSourceRoots
*/
protected abstract List getCompileSourceRoots();
/**
* The extra source directories containing the source to be processed.
* @return a List of AptSourceRoots
*/
protected abstract List getAptSourceRoots();
/**
* The directory where compiled classes go.
* @return outputDirector
*/
protected abstract File getOutputDirectory();
/**
* The directory where generated code go.
* @return generated
*/
protected abstract String getGenerated();
/**
*
* @return a SourceInclusionScanner
*/
protected abstract SourceInclusionScanner getSourceInclusionScanner();
/**
* execute
* @throws MojoExecutionException
*/
public void execute() throws MojoExecutionException
{
getLog().debug( "Using apt compiler" );
Commandline cmd = new Commandline();
int result = APT_COMPILER_SUCCESS;
StringWriter writer = new StringWriter();
// Use reflection to be able to build on all JDKs:
try
{
// init comand line
setAptCommandlineSwitches( cmd );
setAptSpecifics( cmd );
setStandards( cmd );
setClasspath( cmd );
List sourceFiles = new ArrayList();
if ( !fillSourcelist( sourceFiles ) )
{
if ( getLog().isDebugEnabled() )
{
getLog().debug( "there are not stale sources." );
}
return;
}
else
{
if ( fork )
{
if ( !tempRoot.exists() )
{
tempRoot.mkdirs();
}
File file = new File( tempRoot , "files" );
if ( !getLog().isDebugEnabled() )
{
file.deleteOnExit();
}
try
{
FileUtils.fileWrite( file.getAbsolutePath(),
StringUtils.join( sourceFiles.iterator(), "\n" ) );
cmd.createArgument().setValue( '@' + file.getPath() );
}
catch ( IOException e )
{
throw new MojoExecutionException( "Unable to write temporary file for command execution", e );
}
}
else
{
Iterator sourceIt = sourceFiles.iterator();
while ( sourceIt.hasNext() )
{
cmdAdd( cmd, (String) sourceIt.next() );
}
}
}
if ( fork )
{
if ( getLog().isDebugEnabled() )
{
getLog().debug( "Working dir: " + workingDir.getAbsolutePath() );
}
cmd.setWorkingDirectory( workingDir.getAbsolutePath() );
cmd.setExecutable( getAptPath() );
if ( getLog().isDebugEnabled() )
{
getLog().debug( "Invoking apt with cmd " + Commandline.toString( cmd.getShellCommandline() ) );
}
CommandLineUtils.StringStreamConsumer err = new CommandLineUtils.StringStreamConsumer();
try
{
int exitCode = CommandLineUtils.executeCommandLine( cmd, new DefaultConsumer(), err );
getLog().error( err.getOutput() );
if ( exitCode != 0 )
{
throw new MojoExecutionException( "Exit code: " + exitCode + " - " + err.getOutput() );
}
}
catch ( CommandLineException e )
{
throw new MojoExecutionException( "Unable to execute apt command", e );
}
}
else
{
// we need to have tools.jar in lasspath
// due to bug in Apt compiler, system classpath must be modified but in future:
// TODO try separate ClassLoader (see Plexus compiler api)
if ( !isClasspathModified )
{
URL toolsJar = new File( System.getProperty( "java.home" ),
"../lib/tools.jar" ).toURL();
Method m = URLClassLoader.class.getDeclaredMethod( "addURL",
new Class[] { URL.class } );
m.setAccessible( true );
m.invoke( this.getClass().getClassLoader()
.getSystemClassLoader(), new Object[] { toolsJar } );
isClasspathModified = true;
}
Class c = this.getClass().forName( APT_ENTRY_POINT ); // getAptCompilerClass();
Object compiler = c.newInstance();
if ( getLog().isDebugEnabled() )
{
getLog().debug( "Invoking apt with cmd " + cmd.toString() );
}
try
{
Method compile = c.getMethod( APT_METHOD_NAME, new Class[] {
PrintWriter.class, ( new String[] {} ).getClass() } );
result = ( ( Integer ) //
compile.invoke( compiler, new Object[] { new PrintWriter( writer ),
cmd.getArguments() } ) ).intValue();
}
catch ( NoSuchMethodException e )
{
// ignore
Method compile = c.getMethod( APT_METHOD_NAME_OLD, new Class[] {
( new String[] {} ).getClass(), PrintWriter.class } );
result = ( ( Integer ) //
compile.invoke( compiler, new Object[] {
cmd.getArguments(), new PrintWriter( writer ) } ) ).intValue();
}
}
}
catch ( Exception ex )
{
throw new MojoExecutionException( "Error starting apt compiler", ex );
}
finally
{
if ( result != APT_COMPILER_SUCCESS )
{
throw new MojoExecutionException( this, "Compilation error.", writer.getBuffer().toString() );
}
if ( getLog().isDebugEnabled() )
{
String r = writer.getBuffer().toString();
if ( 0 != r.length() )
{
getLog().debug( r );
}
getLog().debug( "Apt finished." );
}
}
}
/**
*
* @param cmd
*/
private void setAptCommandlineSwitches( Commandline cmd )
{
if ( null == aptOptions )
{
return;
}
StringTokenizer tokenizer = new StringTokenizer( aptOptions.trim(), "," );
while ( tokenizer.hasMoreElements() )
{
String option = tokenizer.nextToken().trim();
if ( !option.startsWith( "-A" ) )
{
option = "-A" + option;
}
cmdAdd( cmd, option );
}
}
/**
*
* @param cmd
* @throws MojoExecutionException
*/
private void setAptSpecifics( Commandline cmd ) throws MojoExecutionException
{
try
{
String g = basedir.getAbsolutePath() + FILE_SEPARATOR
+ getGenerated();
File generatedDir = new File( g );
cmdAdd( cmd, "-s", generatedDir.getCanonicalPath() );
if ( !generatedDir.exists() )
{
generatedDir.mkdirs();
}
}
catch ( Exception e )
{
throw new MojoExecutionException( //
"Generated directory is invalid.", e );
}
if ( nocompile )
{
cmdAdd( cmd, "-nocompile" );
}
if ( null != factory && 0 != factory.length() )
{
cmdAdd( cmd, "-factory", factory );
}
}
/**
*
* @param cmd
* @throws MojoExecutionException
*/
private void setStandards( Commandline cmd ) throws MojoExecutionException
{
if ( debug )
{
cmdAdd( cmd, "-g" );
}
if ( !showWarnings )
{
cmdAdd( cmd, "-nowarn" );
}
if ( showDeprecation )
{
cmdAdd( cmd, "-depecation" );
}
if ( null != encoding )
{
cmdAdd( cmd, "-encoding", encoding );
}
if ( verbose )
{
cmdAdd( cmd, "-verbose" );
}
// add sourcepath directory
setSourcepath(cmd);
// add output directory
try
{
if ( !getOutputDirectory().exists() )
{
getOutputDirectory().mkdirs();
}
cmdAdd( cmd, "-d", getOutputDirectory().getCanonicalPath() );
}
catch ( Exception ex )
{
throw new MojoExecutionException( //
"Output directory is invalid.", ex );
}
}
private void setSourcepath(Commandline cmd) {
StringBuffer buffer = new StringBuffer();
for ( Iterator it = getCompileSourceRoots().iterator(); it.hasNext();)
{
buffer.append( it.next() );
if ( it.hasNext() )
{
buffer.append( PATH_SEPARATOR );
}
}
if ( getAptSourceRoots() != null)
{
if (buffer.length() > 0 && getAptSourceRoots().size() > 0)
{
buffer.append( PATH_SEPARATOR );
}
for ( Iterator it = getAptSourceRoots().iterator(); it.hasNext();)
{
buffer.append( it.next() );
if ( it.hasNext() )
{
buffer.append( PATH_SEPARATOR );
}
}
}
cmdAdd( cmd, "-sourcepath", buffer.toString() );
}
/**
*
* @param cmd
* @return
* @throws MojoExecutionException
*/
private boolean fillSourcelist( List cmd ) throws MojoExecutionException
{
boolean has = false;
// sources ....
Iterator it = getCompileSourceRoots().iterator();
if ( getLog().isDebugEnabled() )
{
getLog().debug( "Checking sourcepath" );
}
while ( it.hasNext() )
{
File srcFile = new File( (String) it.next() );
has = addIncludedSources( srcFile, cmd, has );
}
List aptSourcesRoots = getAptSourceRoots();
if ( aptSourcesRoots != null )
{
it = aptSourcesRoots.iterator();
while ( it.hasNext() )
{
File srcFile = new File( (String) it.next() );
has = addIncludedSources( srcFile, cmd, has );
}
}
return has;
}
/**
*
* @param srcFile
* @param cmd
* @param has
* @return
* @throws MojoExecutionException
*/
private boolean addIncludedSources( File srcFile, List cmd, boolean has ) throws MojoExecutionException
{
if ( getLog().isDebugEnabled() )
{
getLog().debug( "Checking sourcepath in " + srcFile );
}
if ( srcFile.isDirectory() )
{
Collection sources = null;
try
{
sources = //
getSourceInclusionScanner().getIncludedSources( srcFile,
getOutputDirectory() );
}
catch ( Exception ex )
{
throw new MojoExecutionException(
"Can't agregate sources.", ex );
}
if ( getLog().isDebugEnabled() )
{
getLog().debug(
"sources from: " + srcFile.getAbsolutePath() );
String s = "";
for ( Iterator jt = sources.iterator(); jt.hasNext();)
{
s += jt.next() + "\n";
}
getLog().debug( s );
}
Iterator jt = sources.iterator();
while ( jt.hasNext() )
{
File src = (File) jt.next();
if ( fork )
{
cmd.add( quotedPathArgument( src.getAbsolutePath() ) );
}
else
{
cmd.add( src.getAbsolutePath() );
}
has = true;
}
}
return has;
}
/**
*
* @param cmd
* @throws MojoExecutionException
* @throws DependencyResolutionRequiredException
*/
private void setClasspath( Commandline cmd ) throws MojoExecutionException, DependencyResolutionRequiredException
{
StringBuffer buffer = new StringBuffer();
for ( Iterator it = getClasspathElements().iterator(); it.hasNext();)
{
buffer.append( it.next() );
if ( it.hasNext() )
{
buffer.append( PATH_SEPARATOR );
}
}
cmdAdd( cmd, "-classpath", buffer.toString() );
}
/**
*
* @param cmd
* @param arg
*/
private void cmdAdd( Commandline cmd, String arg )
{
/**
* OBSOLETE
* if( true == getLog().isDebugEnabled() ) { getLog().debug(
* arg ); }
*/
cmd.createArgument().setValue( arg );
//cmd.add( arg );
}
/**
*
* @param cmd
* @param arg1
* @param arg2
*/
private void cmdAdd( Commandline cmd, String arg1, String arg2 )
{
/**
* OBSOLETE
* if( true == getLog().isDebugEnabled() ) { getLog().debug(
* arg1 + " " + arg2 ); }
*/
cmdAdd( cmd, arg1 );
cmdAdd( cmd, arg2 );
}
/**
* Get the path of apt tool depending the OS.
*
* @return the path of the apt tool
*/
private String getAptPath()
{
String aptCommand = "apt";
if ( Os.isFamily( "windows" ) )
{
aptCommand = "apt.exe";
}
File aptExe;
// For IBM's JDK 1.2
if ( Os.isName( "aix" ) )
{
aptExe = new File( System.getProperty( "java.home" ) + "/../sh", aptCommand );
}
else if ( Os.isFamily( "unix" ) && Os.isFamily( "mac" ) )
{
aptExe = new File( System.getProperty( "java.home" ) + "/bin", aptCommand );
}
else
{
aptExe = new File( System.getProperty( "java.home" ) + "/../bin", aptCommand );
}
getLog().debug( "Apt executable=[" + aptExe.getAbsolutePath() + "]" );
return aptExe.getAbsolutePath();
}
/**
*
* @param value
* @return quotedPathArgument
*/
private String quotedPathArgument( String value )
{
String path = value;
if ( !StringUtils.isEmpty( path ) )
{
path = path.replace( '\\', '/' );
if ( path.indexOf( "\'" ) != -1 )
{
String split[] = path.split( "\'" );
path = "";
for ( int i = 0; i < split.length; i++ )
{
if ( i != split.length - 1 )
{
path = path + split[i] + "\\'";
}
else
{
path = path + split[i];
}
}
}
path = "'" + path + "'";
}
return path;
}
}