Java tutorial
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 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.apache.maven.project.MavenProject; import org.apache.maven.project.MavenProjectHelper; import org.apache.maven.artifact.handler.ArtifactHandler; 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; /** * Signs a JAR using jarsigner. * * @author <a href="jerome@coffeebreaks.org">Jerome Lacoste</a> * @version $Id: JarSignMojo.java 802529 2009-08-09 12:05:35Z bentmann $ * @goal sign * @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 JarSignMojo extends AbstractMojo { /** * Set this to <code>true</code> to disable signing. * Useful to speed up build process in development environment. * * @parameter expression="${maven.jar.sign.skip}" default-value="false" */ private boolean skip; /** * 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 jar to sign. When specified, the finalName is ignored. * * @parameter alias="jarpath" default-value="${project.build.directory}/${project.build.finalName}.${project.packaging}" */ private File jarPath; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * @parameter expression="${keystore}" */ private String keystore; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * @parameter expression="${storepass}" */ private String storepass; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * @parameter expression="${keypass}" */ private String keypass; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * @parameter expression="${sigfile}" * @todo make a File? */ private String sigfile; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * <p/> * Not specifying this argument will sign the jar in-place (your original jar is going to be overwritten). * * @parameter expression="${signedjar}" */ private File signedjar; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * The corresponding option in the command line is -storetype. * * @parameter expression="${type}" */ private String type; /** * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * @parameter expression="${alias}" * @required */ private String alias; /** * Automatically verify a jar after signing it. * <p/> * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * @parameter expression="${verify}" default-value="false" */ private boolean verify; /** * Skip attaching the signed artifact. By default the signed artifact is attached. * This is not a Mojo parameter as we shouldn't need this when using this mojo. * Just needed when reusing the implementation. See MJAR-84 for discussions. */ private boolean skipAttachSignedArtifact; /** * Enable verbose. * See <a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/jarsigner.html#Options">options</a>. * * @parameter expression="${verbose}" default-value="false" */ private boolean verbose; /** * @component */ private MavenProjectHelper projectHelper; /** * The Maven project. * * @parameter expression="${project}" * @required * @readonly */ private MavenProject project; /** * Classifier to use for the generated artifact. * If not specified, the generated artifact becomes the primary artifact. * * @parameter expression="${classifier}" */ private String classifier; public void execute() throws MojoExecutionException { if (skip) { getLog().info("Skipping JAR signing for file: " + getJarFile().getAbsolutePath()); return; } if (project != null) { ArtifactHandler artifactHandler = project.getArtifact().getArtifactHandler(); if (artifactHandler != null && !"java".equals(artifactHandler.getLanguage())) { getLog().debug("Not executing jar:sign as the project is not a Java module"); return; } } // we use this mojo to check if there's a need to sign. // If we sign and if we need to verify, we reuse it to check the signature JarSignVerifyMojo verifyMojo = createJarSignVerifyMojo(); verifyMojo.setWorkingDir(workingDirectory); verifyMojo.setBasedir(basedir); File signedJarFile = signedjar != null ? signedjar : getJarFile(); verifyMojo.setVerbose(verbose); verifyMojo.setJarPath(signedJarFile); if (signedJarFile.exists()) { verifyMojo.setErrorWhenNotSigned(false); verifyMojo.execute(); } if (verifyMojo.isSigned()) { getLog().info("JAR " + signedJarFile.getAbsoluteFile() + " is already signed. Skipping."); return; } signJar(); if (this.verify) { verifyMojo.setErrorWhenNotSigned(true); verifyMojo.execute(); } } protected JarSignVerifyMojo createJarSignVerifyMojo() { return new JarSignVerifyMojo(); } File getJarFile() { if (jarPath != null) { return jarPath; } else { return AbstractJarMojo.getJarFile(basedir, finalName, null); } } void signJar() throws MojoExecutionException { List arguments = new ArrayList(); Commandline commandLine = new Commandline(); commandLine.setExecutable(getJarsignerPath()); addArgIf(arguments, verbose, "-verbose"); // I believe Commandline to add quotes where appropriate, although I haven't tested it enough. // FIXME addArgIfNotEmpty will break those parameters containing a space. // Look at webapp:gen-keystore for a way to fix that addArgIfNotEmpty(arguments, "-keystore", this.keystore); addArgIfNotEmpty(arguments, "-storepass", this.storepass); addArgIfNotEmpty(arguments, "-keypass", this.keypass); addArgIfNotEmpty(arguments, "-signedjar", this.signedjar); addArgIfNotEmpty(arguments, "-storetype", this.type); addArgIfNotEmpty(arguments, "-sigfile", this.sigfile); arguments.add(getJarFile()); addArgIf(arguments, alias != null, this.alias); for (Iterator it = arguments.iterator(); it.hasNext();) { commandLine.createArgument().setValue(it.next().toString()); } commandLine.setWorkingDirectory(workingDirectory.getAbsolutePath()); createParentDirIfNecessary(signedjar); if (signedjar == null) { getLog().debug("Signing JAR in-place (overwritting original JAR)."); } if (getLog().isDebugEnabled()) { getLog().debug("Executing: " + purgePassword(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) { errBuffer.append(line); getLog().warn(line); } }; try { int result = executeCommandLine(commandLine, inputStream, outConsumer, errConsumer); if (result != 0) { throw new MojoExecutionException( "Result of " + purgePassword(commandLine) + " execution is: \'" + result + "\'."); } } catch (CommandLineException e) { throw new MojoExecutionException("command execution failed", e); } // signed in place, no need to attach if (signedjar == null || skipAttachSignedArtifact) { return; } if (classifier != null) { projectHelper.attachArtifact(project, "jar", classifier, signedjar); } else { project.getArtifact().setFile(signedjar); } } private String purgePassword(Commandline commandLine) { String out = commandLine.toString(); if (keypass != null && out.indexOf(keypass) != -1) { out = StringUtils.replace(out, keypass, "******"); } return out; } private void createParentDirIfNecessary(File file) { if (file != null) { File fileDir = 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 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 <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,Object,boolean) */ private void addArgIfNotEmpty(List arguments, String key, Object value) { addArgIfNotEmpty(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 repeatKey repeat or not the key in the command line */ private void addArgIfNotEmpty(List arguments, String key, Object value, boolean repeatKey) { if (value != null && !StringUtils.isEmpty(value.toString())) { arguments.add(key); StringTokenizer token = new StringTokenizer(value.toString(), ","); 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 setWorkingDir(File workingDir) { this.workingDirectory = workingDir; } public void setBasedir(File basedir) { this.basedir = basedir; } public void setKeystore(String keystore) { this.keystore = keystore; } public void setKeypass(String keypass) { this.keypass = keypass; } public void setSignedJar(File signedjar) { this.signedjar = signedjar; } public void setAlias(String alias) { this.alias = alias; } // 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 setStorepass(String storepass) { this.storepass = storepass; } public void setSigFile(String sigfile) { this.sigfile = sigfile; } public void setType(String type) { this.type = type; } public void setVerbose(boolean verbose) { this.verbose = verbose; } public void setSkipAttachSignedArtifact(boolean skipAttachSignedArtifact) { this.skipAttachSignedArtifact = skipAttachSignedArtifact; } public void setProject(MavenProject project) { this.project = project; } public void setVerify(boolean verify) { this.verify = verify; } }