package org.codehaus.mojo.keytool; /* * Copyright 2005-2008 The Codehaus. * * Licensed 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 org.apache.commons.lang.SystemUtils; 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; import java.io.File; import java.io.InputStream; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.StringTokenizer; /** * Generates a keystore. * <p/> * Implemented as a wrapper around the SDK <code>keytool -genkey</code> command. * <pre> * -genkey [-v] [-protected] * [-alias <alias>] * [-keyalg <keyalg>] [-keysize <keysize>] * [-sigalg <sigalg>] [-dname <dname>] * [-validity <valDays>] [-keypass <keypass>] * [-keystore <keystore>] [-storepass <storepass>] * [-storetype <storetype>] [-providerName <name>] * [-providerClass <provider_class_name> [-providerArg <arg>]] ... * </pre> * * @author <a href="jerome@coffeebreaks.org">Jerome Lacoste</a> * @version $Id$ * @goal genkey * @phase package * @requiresProject * @todo refactor the common code with javadoc plugin * @see <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html">keystore documentation</a>. */ public class GenkeyMojo extends AbstractKeyToolMojo { /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${keyalg}" */ private String keyalg; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${keysize}" */ private String keysize; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${sigalg}" */ private String sigalg; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${storetype}" */ private String storetype; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${storepass}" */ private String storepass; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${keypass}" */ private String keypass; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${validity}" */ private String validity; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${dname}" * @todo split this up? */ private String dname; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${alias}" */ private String alias; /** * Enable verbose * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html#Commands">options</a>. * * @parameter expression="${verbose}" default-value="false" */ private boolean verbose; public void execute() throws MojoExecutionException { List arguments = new ArrayList(); Commandline commandLine = new Commandline(); commandLine.setExecutable( getKeytoolPath() ); arguments.add( "-genkey" ); addArgIf( arguments, this.verbose, "-v" ); // I believe Commandline to add quotes where appropriate, although I haven't tested it enough. addArgIfNotEmpty( arguments, "-dname", this.dname ); addArgIfNotEmpty( arguments, "-alias", this.alias ); addArgIfNotEmpty( arguments, "-keypass", this.keypass ); addArgIfNotEmpty( arguments, "-keystore", this.keystore ); addArgIfNotEmpty( arguments, "-storepass", this.storepass ); addArgIfNotEmpty( arguments, "-validity", this.validity ); addArgIfNotEmpty( arguments, "-keyalg", this.keyalg ); addArgIfNotEmpty( arguments, "-keysize", this.keysize ); addArgIfNotEmpty( arguments, "-sigalg", this.sigalg ); addArgIfNotEmpty( arguments, "-storetype", this.storetype ); for ( Iterator it = arguments.iterator(); it.hasNext(); ) { commandLine.createArgument().setValue( it.next().toString() ); } if ( workingDirectory != null ) { commandLine.setWorkingDirectory( workingDirectory.getAbsolutePath() ); } createParentDirIfNecessary( keystore ); getLog().debug( "Executing: " + commandLine ); // jarsigner may ask for some input if the parameters are missing or incorrect. // This should take care of it and make it fail gracefully final InputStream inputStream = new InputStream() { public int read() { return -1; } }; StreamConsumer outConsumer = new StreamConsumer() { public void consumeLine( String line ) { getLog().info( line ); } }; final StringBuffer errBuffer = new StringBuffer(); StreamConsumer errConsumer = new StreamConsumer() { public void consumeLine( String line ) { getLog().warn( line ); errBuffer.append( line ); } }; try { int result = executeCommandLine( commandLine, inputStream, outConsumer, errConsumer ); if ( result != 0 ) { throw new MojoExecutionException( "Result of " + commandLine + " execution is: \'" + result + "\': " + errBuffer.toString() + "." ); } } catch ( CommandLineException e ) { throw new MojoExecutionException( "command execution failed", e ); } } private void createParentDirIfNecessary( final String file ) { if ( file != null ) { final File fileDir = new File( file ).getParentFile(); if ( fileDir != null ) { // not a relative path boolean mkdirs = fileDir.mkdirs(); getLog().debug( "mdkirs: " + mkdirs + " " + fileDir ); } } } // 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 getKeytoolPath() { return getJDKCommandPath( "keytool", 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 ) // what about IS_OS_MAC_OS ?? { 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 <code>command line</code> * 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 <code>command line</code> * if the the value is not null or empty. * <p/> * 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 ) { // FIXME we need to improve this API // addArgIfNotEmpty( arguments, key, value, false ); addArgIfNotEmpty2( arguments, key, value, false ); } /** * Convenience method to add an argument to the <code>command line</code> * if the the value is not null or empty. * <p/> * Moreover, the value could be comma separated. * * @param arguments * @param key the argument name. * @param value the argument value to be added. * @param ignored */ private void addArgIfNotEmpty2( List arguments, String key, String value, boolean ignored ) { if ( !StringUtils.isEmpty( value ) ) { arguments.add( key ); arguments.add( value ); } } /** * Convenience method to add an argument to the <code>command line</code> * if the the value is not null or empty. * <p/> * 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 stream1, StreamConsumer stream2 ) throws CommandLineException { return CommandLineUtils.executeCommandLine( commandLine, inputStream, stream1, stream2 ); } public void setKeypass( String keypass ) { this.keypass = keypass; } public void setStorepass( String storepass ) { this.storepass = storepass; } public void setAlias( String alias ) { this.alias = alias; } public void setKeyalg( String keyalg ) { this.keyalg = keyalg; } public void setSigalg( String sigalg ) { this.sigalg = sigalg; } public void setKeysize( String keysize ) { this.keysize = keysize; } public void setStoretype( String storetype ) { this.storetype = storetype; } public void setValidity( String validity ) { this.validity = validity; } public void setDname( String dname ) { this.dname = dname; } public void setVerbose( boolean verbose ) { this.verbose = verbose; } }