
org.apache.maven.plugin.jar.JarSignVerifyMojo Maven / Gradle / Ivy
package org.apache.maven.plugin.jar;
/*
* 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.InputStream;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.StringTokenizer;
import org.apache.commons.lang.SystemUtils;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.logging.Log;
import org.codehaus.plexus.util.StringUtils;
import org.codehaus.plexus.util.cli.CommandLineException;
import org.codehaus.plexus.util.cli.CommandLineUtils;
import org.codehaus.plexus.util.cli.Commandline;
import org.codehaus.plexus.util.cli.StreamConsumer;
/**
* Checks the signature of a signed jar using jarsigner.
*
* @author Jerome Lacoste
* @version $Id: JarSignVerifyMojo.java 802529 2009-08-09 12:05:35Z bentmann $
* @goal sign-verify
* @phase package
* @requiresProject
* @todo refactor the common code with javadoc plugin
* @requiresDependencyResolution runtime
* @deprecated As of version 2.3, this goal is no longer supported in favor of the dedicated maven-jarsigner-plugin.
*/
public class JarSignVerifyMojo
extends AbstractMojo
{
/**
* The working directory in which the jarsigner executable will be run.
*
* @parameter expression="${workingdir}" default-value="${basedir}"
* @required
*/
private File workingDirectory;
/**
* Directory containing the generated JAR.
*
* @parameter expression="${project.build.directory}"
* @required
* @readonly
*/
private File basedir;
/**
* Name of the generated JAR (without classifier and extension).
*
* @parameter alias="jarname" expression="${project.build.finalName}"
* @required
*/
private String finalName;
/**
* Path of the signed jar. When specified, the finalName is ignored.
*
* @parameter expression="${jarpath}"
*/
private File jarPath;
/**
* Check certificates. Requires {@link #setVerbose(boolean)}.
* See options.
*
* @parameter expression="${checkcerts}" default-value="false"
*/
private boolean checkCerts;
/**
* Enable verbose
* See options.
*
* @parameter expression="${verbose}" default-value="false"
*/
private boolean verbose;
/** When true
this will make the execute() operation fail,
* throwing an exception, when verifying a non signed jar.
* Primarily to keep backwards compatibility with existing code, and allow reusing the
* bean in unattended operations when set to false
.
*
* @parameter expression="${errorWhenNotSigned}" default-value="true"
**/
private boolean errorWhenNotSigned = true;
/**
* Is the jar signed ? output property set by the execute call. The value will be accessible
* when execute() ends and if errorWhenNotSigned has been set to false.
**/
private boolean signed;
File getJarFile()
{
if ( jarPath != null )
{
return jarPath;
}
else
{
return AbstractJarMojo.getJarFile( basedir, finalName, null);
}
}
public void execute()
throws MojoExecutionException
{
List arguments = new ArrayList();
Commandline commandLine = new Commandline();
commandLine.setExecutable( getJarsignerPath() );
arguments.add( "-verify" );
addArgIf( arguments, this.verbose, "-verbose" );
addArgIf( arguments, this.checkCerts, "-certs" );
arguments.add( getJarFile() );
for ( Iterator it = arguments.iterator() ; it.hasNext() ; )
{
commandLine.createArgument().setValue( it.next().toString() );
}
commandLine.setWorkingDirectory( workingDirectory.getAbsolutePath() );
getLog().debug("Executing: " + commandLine );
LineMatcherStreamConsumer outConsumer = new LineMatcherStreamConsumer( "jar verified." );
final StringBuffer errBuffer = new StringBuffer();
StreamConsumer errConsumer = new StreamConsumer()
{
public void consumeLine(String line)
{
errBuffer.append( line );
getLog().warn( line );
}
};
try
{
int result = executeCommandLine( commandLine, null, outConsumer, errConsumer );
if ( result != 0 )
{
throw new MojoExecutionException("Result of " + commandLine
+ " execution is: \'" + result + "\'." );
}
signed = outConsumer.matched;
if ( !signed && errorWhenNotSigned )
{
throw new MojoExecutionException( "Verify failed: " + outConsumer.firstOutLine );
}
}
catch ( CommandLineException e )
{
throw new MojoExecutionException( "command execution failed", e );
}
}
// checks if a consumed line matches
// also keeps track of the first consumed line.
class LineMatcherStreamConsumer
implements StreamConsumer
{
private String toMatch;
private boolean matched;
private String firstOutLine;
LineMatcherStreamConsumer( String toMatch )
{
this.toMatch = toMatch;
}
public void consumeLine(String line)
{
if ( firstOutLine == null )
{
firstOutLine = line;
}
matched = matched || toMatch.equals( line );
getLog().info( line );
}
}
// taken from JavadocReport then slightly refactored
// should probably share with other plugins that use $JAVA_HOME/bin tools
/**
* Get the path of jarsigner tool depending the OS.
*
* @return the path of the jarsigner tool
*/
private String getJarsignerPath()
{
return getJDKCommandPath( "jarsigner", getLog() );
}
private static String getJDKCommandPath( String command, Log logger )
{
String path = getJDKCommandExe(command).getAbsolutePath();
logger.debug( command + " executable=[" + path + "]" );
return path;
}
private static File getJDKCommandExe( String command )
{
String fullCommand = command + ( SystemUtils.IS_OS_WINDOWS ? ".exe" : "" );
File exe;
// For IBM's JDK 1.2
if ( SystemUtils.IS_OS_AIX )
{
exe = new File( SystemUtils.getJavaHome() + "/../sh", fullCommand );
}
else if ( SystemUtils.IS_OS_MAC_OSX )
{
exe = new File( SystemUtils.getJavaHome() + "/bin", fullCommand );
}
else
{
exe = new File( SystemUtils.getJavaHome() + "/../bin", fullCommand );
}
return exe;
}
// Helper methods. Could/should be shared e.g. with JavadocReport
/**
* Convenience method to add an argument to the command line
* conditionally based on the given flag.
*
* @param arguments
* @param b the flag which controls if the argument is added or not.
* @param value the argument value to be added.
*/
private void addArgIf( List arguments, boolean b, String value )
{
if ( b )
{
arguments.add( value );
}
}
/**
* Convenience method to add an argument to the command line
* if the the value is not null or empty.
*
* Moreover, the value could be comma separated.
*
* @param arguments
* @param key the argument name.
* @param value the argument value to be added.
* @see #addArgIfNotEmpty(java.util.List,String,String,boolean)
*/
private void addArgIfNotEmpty( List arguments, String key, String value )
{
addArgIfNotEmpty( arguments, key, value, false );
}
/**
* Convenience method to add an argument to the command line
* if the the value is not null or empty.
*
* Moreover, the value could be comma separated.
*
* @param arguments
* @param key the argument name.
* @param value the argument value to be added.
* @param repeatKey repeat or not the key in the command line
*/
private void addArgIfNotEmpty( List arguments, String key, String value, boolean repeatKey )
{
if ( !StringUtils.isEmpty( value ) )
{
arguments.add( key );
StringTokenizer token = new StringTokenizer( value, "," );
while ( token.hasMoreTokens() )
{
String current = token.nextToken().trim();
if ( !StringUtils.isEmpty( current ) )
{
arguments.add( current );
if ( token.hasMoreTokens() && repeatKey )
{
arguments.add( key );
}
}
}
}
}
//
// methods used for tests purposes - allow mocking and simulate automatic setters
//
protected int executeCommandLine( Commandline commandLine, InputStream inputStream,
StreamConsumer systemOut, StreamConsumer systemErr )
throws CommandLineException
{
return CommandLineUtils.executeCommandLine( commandLine, inputStream, systemOut, systemErr );
}
public void setWorkingDir( File workingDir )
{
this.workingDirectory = workingDir;
}
public void setBasedir( File basedir )
{
this.basedir = basedir;
}
// hiding for now - I don't think this is required to be seen
/*
public void setFinalName( String finalName )
{
this.finalName = finalName;
}
*/
public void setJarPath( File jarPath )
{
this.jarPath = jarPath;
}
public void setCheckCerts( boolean checkCerts )
{
this.checkCerts = checkCerts;
}
public void setVerbose( boolean verbose )
{
this.verbose = verbose;
}
/**
* Is the JAR file signed ? Output property set by the {@link #execute()} call.
*
* @return true
if the jar was signed, false
otherwise.
*/
public boolean isSigned()
{
return signed;
}
/**
* Sets a boolean that is to determine if an exception should be thrown when
* the JAR file being verified is unsigned. If you just what to check if a
* JAR is unsigned and then act on the result, then you probably want to
* set this to true
.
*/
public void setErrorWhenNotSigned( boolean errorWhenNotSigned )
{
this.errorWhenNotSigned = errorWhenNotSigned;
}
}