stevel 2005/03/23 08:51:42 Modified: src/main/org/apache/tools/ant/taskdefs defaults.properties SignJar.java src/etc/testcases/taskdefs signjar.xml src/testcases/org/apache/tools/ant/taskdefs SignJarTest.java Added: src/main/org/apache/tools/ant/taskdefs AbstractJarSignerTask.java VerifyJar.java Log: Factor out a base classs and add a verification task, a verification which cannot rely on return codes as a success metric, as the program returns 0 even for invalid jars. Hence the disabled test Revision Changes Path 1.166 +1 -0 ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties Index: defaults.properties =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/defaults.properties,v retrieving revision 1.165 retrieving revision 1.166 diff -u -r1.165 -r1.166 --- defaults.properties 22 Feb 2005 15:58:36 -0000 1.165 +++ defaults.properties 23 Mar 2005 16:51:42 -0000 1.166 @@ -207,6 +207,7 @@ ildasm=org.apache.tools.ant.taskdefs.optional.dotnet.Ildasm apt=org.apache.tools.ant.taskdefs.Apt schemavalidate=org.apache.tools.ant.taskdefs.optional.SchemaValidate +verifyjar=org.apache.tools.ant.taskdefs.VerifyJar # deprecated ant tasks (kept for back compatibility) starteam=org.apache.tools.ant.taskdefs.optional.scm.AntStarTeamCheckOut 1.52 +68 -261 ant/src/main/org/apache/tools/ant/taskdefs/SignJar.java Index: SignJar.java =================================================================== RCS file: /home/cvs/ant/src/main/org/apache/tools/ant/taskdefs/SignJar.java,v retrieving revision 1.51 retrieving revision 1.52 diff -u -r1.51 -r1.52 --- SignJar.java 23 Mar 2005 15:06:48 -0000 1.51 +++ SignJar.java 23 Mar 2005 16:51:42 -0000 1.52 @@ -48,41 +48,11 @@ * @ant.task category="java" * @since Ant 1.1 */ -public class SignJar extends Task { +public class SignJar extends AbstractJarSignerTask { private static final FileUtils FILE_UTILS = FileUtils.getFileUtils(); /** - * The name of the jar file. - */ - protected File jar; - - /** - * The alias of signer. - */ - protected String alias; - - /** - * The url or path of keystore file. - */ - private String keystore; - - /** - * password for the store - */ - protected String storepass; - - /** - * type of store,-storetype param - */ - protected String storetype; - - /** - * password for the key in the store - */ - protected String keypass; - - /** * name to a signature file */ protected String sigfile; @@ -93,11 +63,6 @@ protected File signedjar; /** - * verbose output - */ - protected boolean verbose; - - /** * flag for internal sf signing */ protected boolean internalsf; @@ -113,21 +78,6 @@ private boolean preserveLastModified; /** - * redirector used to talk to the jarsigner program - */ - private RedirectorElement redirector; - - /** - * The maximum amount of memory to use for Jar signer - */ - private String maxMemory; - - /** - * the filesets of the jars to sign - */ - protected Vector filesets = new Vector(); - - /** * Whether to assume a jar which has an appropriate .SF file in is already * signed. */ @@ -169,11 +119,6 @@ /** * error string for unit test verification: [EMAIL PROTECTED] */ - public static final String WARN_JAR_AND_FILESET = "nested filesets will be ignored if the jar attribute has" - + " been specified."; - /** - * error string for unit test verification: [EMAIL PROTECTED] - */ public static final String ERROR_BAD_MAP = "Cannot map source file to anything sensible: "; /** * error string for unit test verification: [EMAIL PROTECTED] @@ -182,11 +127,6 @@ /** * error string for unit test verification: [EMAIL PROTECTED] */ - public static final String ERROR_NO_SOURCE = "jar must be set through jar attribute " - + "or nested filesets"; - /** - * error string for unit test verification: [EMAIL PROTECTED] - */ public static final String ERROR_NO_ALIAS = "alias attribute must be set"; /** * error string for unit test verification: [EMAIL PROTECTED] @@ -194,75 +134,6 @@ public static final String ERROR_NO_STOREPASS = "storepass attribute must be set"; /** - * name of JDK program we are looking for - */ - protected static final String JARSIGNER_COMMAND = "jarsigner"; - - /** - * Set the maximum memory to be used by the jarsigner process - * - * @param max a string indicating the maximum memory according to the JVM - * conventions (e.g. 128m is 128 Megabytes) - */ - public void setMaxmemory(String max) { - maxMemory = max; - } - - /** - * the jar file to sign; required - * - * @param jar the jar file to sign - */ - public void setJar(final File jar) { - this.jar = jar; - } - - /** - * the alias to sign under; required - * - * @param alias the alias to sign under - */ - public void setAlias(final String alias) { - this.alias = alias; - } - - /** - * keystore location; required - * - * @param keystore the keystore location - */ - public void setKeystore(final String keystore) { - this.keystore = keystore; - } - - /** - * password for keystore integrity; required - * - * @param storepass the password for the keystore - */ - public void setStorepass(final String storepass) { - this.storepass = storepass; - } - - /** - * keystore type; optional - * - * @param storetype the keystore type - */ - public void setStoretype(final String storetype) { - this.storetype = storetype; - } - - /** - * password for private key (if different); optional - * - * @param keypass the password for the key (if different) - */ - public void setKeypass(final String keypass) { - this.keypass = keypass; - } - - /** * name of .SF/.DSA file; optional * * @param sigfile the name of the .SF/.DSA file @@ -281,15 +152,6 @@ } /** - * Enable verbose output when signing ; optional: default false - * - * @param verbose if true enable verbose output - */ - public void setVerbose(final boolean verbose) { - this.verbose = verbose; - } - - /** * Flag to include the .SF file inside the signature; optional; default * false * @@ -319,16 +181,6 @@ } /** - * Adds a set of files to sign - * - * @param set a set of files to sign - * @since Ant 1.4 - */ - public void addFileset(final FileSet set) { - filesets.addElement(set); - } - - /** * Optionally sets the output directory to be used. * * @param destDir the directory in which to place signed jars @@ -436,88 +288,67 @@ throw new BuildException(ERROR_MAPPER_WITHOUT_DEST); } - //init processing logic; this is retained through our execution(s) - redirector = createRedirector(); + beginExecution(); - //special case single jar handling with signedjar attribute set - if (hasJar && hasSignedJar) { - // single jar processing - signOneJar(jar, signedjar); - //return here. - return; - } + try { + //special case single jar handling with signedjar attribute set + if (hasJar && hasSignedJar) { + // single jar processing + signOneJar(jar, signedjar); + //return here. + return; + } - //the rest of the method treats single jar like - //a nested fileset with one file + //the rest of the method treats single jar like + //a nested fileset with one file - if (hasJar) { - //we create a fileset with the source file. - //this lets us combine our logic for handling output directories, - //mapping etc. - FileSet sourceJar = new FileSet(); - sourceJar.setFile(jar); - sourceJar.setDir(jar.getParentFile()); - addFileset(sourceJar); - } - //set up our mapping policy - FileNameMapper destMapper; - if (hasMapper) { - destMapper = mapper; - } else { - //no mapper? use the identity policy - destMapper = new IdentityMapper(); - } + Vector sources = createUnifiedSources(); + //set up our mapping policy + FileNameMapper destMapper; + if (hasMapper) { + destMapper = mapper; + } else { + //no mapper? use the identity policy + destMapper = new IdentityMapper(); + } - //at this point the filesets are set up with lists of files, - //and the mapper is ready to map from source dirs to dest files - //now we iterate through every JAR giving source and dest names - // deal with the filesets - for (int i = 0; i < filesets.size(); i++) { - FileSet fs = (FileSet) filesets.elementAt(i); - //get all included files in a fileset - DirectoryScanner ds = fs.getDirectoryScanner(getProject()); - String[] jarFiles = ds.getIncludedFiles(); - File baseDir = fs.getDir(getProject()); - - //calculate our destination directory; it is either the destDir - //attribute, or the base dir of the fileset (for in situ updates) - File toDir = hasDestDir ? destDir : baseDir; - - //loop through all jars in the fileset - for (int j = 0; j < jarFiles.length; j++) { - String jarFile = jarFiles[j]; - //determine the destination filename via the mapper - String[] destFilenames = destMapper.mapFileName(jarFile); - if (destFilenames == null || destFilenames.length != 1) { - //we only like simple mappers. - throw new BuildException(ERROR_BAD_MAP + jarFile); + //at this point the filesets are set up with lists of files, + //and the mapper is ready to map from source dirs to dest files + //now we iterate through every JAR giving source and dest names + // deal with the filesets + for (int i = 0; i < sources.size(); i++) { + FileSet fs = (FileSet) sources.elementAt(i); + //get all included files in a fileset + DirectoryScanner ds = fs.getDirectoryScanner(getProject()); + String[] jarFiles = ds.getIncludedFiles(); + File baseDir = fs.getDir(getProject()); + + //calculate our destination directory; it is either the destDir + //attribute, or the base dir of the fileset (for in situ updates) + File toDir = hasDestDir ? destDir : baseDir; + + //loop through all jars in the fileset + for (int j = 0; j < jarFiles.length; j++) { + String jarFile = jarFiles[j]; + //determine the destination filename via the mapper + String[] destFilenames = destMapper.mapFileName(jarFile); + if (destFilenames == null || destFilenames.length != 1) { + //we only like simple mappers. + throw new BuildException(ERROR_BAD_MAP + jarFile); + } + File destFile = new File(toDir, destFilenames[0]); + File jarSource = new File(baseDir, jarFile); + signOneJar(jarSource, destFile); } - File destFile = new File(toDir, destFilenames[0]); - File jarSource = new File(baseDir, jarFile); - signOneJar(jarSource, destFile); } + } finally { + endExecution(); } } /** - * Create the redirector to use, if any. - * - * @return a configured RedirectorElement. - */ - private RedirectorElement createRedirector() { - RedirectorElement result = new RedirectorElement(); - StringBuffer input = new StringBuffer(storepass).append('\n'); - if (keypass != null) { - input.append(keypass).append('\n'); - } - result.setInputString(input.toString()); - result.setLogInputString(false); - return result; - } - - /** * Sign one jar. * <p/> * The signing only takes place if [EMAIL PROTECTED] #isUpToDate(File, File)} indicates @@ -540,71 +371,47 @@ } long lastModified = jarSource.lastModified(); - final ExecTask cmd = new ExecTask(this); - cmd.setExecutable(JavaEnvUtils.getJdkExecutable(JARSIGNER_COMMAND)); - cmd.setTaskType(JARSIGNER_COMMAND); - - if (maxMemory != null) { - cmd.createArg().setValue("-J-Xmx" + maxMemory); - } + final ExecTask cmd = createJarSigner(); - if (null != keystore) { - // is the keystore a file - cmd.createArg().setValue("-keystore"); - String location; - File keystoreFile = getProject().resolveFile(keystore); - if (keystoreFile.exists()) { - location = keystoreFile.getPath(); - } else { - // must be a URL - just pass as is - location = keystore; - } - cmd.createArg().setValue(location); - } - if (null != storetype) { - cmd.createArg().setValue("-storetype"); - cmd.createArg().setValue(storetype); - } + setCommonOptions(cmd); + + bindToKeystore(cmd); if (null != sigfile) { - cmd.createArg().setValue("-sigfile"); - cmd.createArg().setValue(sigfile); + addValue(cmd, "-sigfile"); + String value = this.sigfile; + addValue(cmd, value); } //DO NOT SET THE -signedjar OPTION if source==dest //unless you like fielding hotspot crash reports if (null != target && !jarSource.equals(target)) { - cmd.createArg().setValue("-signedjar"); - cmd.createArg().setValue(target.getPath()); - } - - if (verbose) { - cmd.createArg().setValue("-verbose"); + addValue(cmd, "-signedjar"); + addValue(cmd, target.getPath()); } if (internalsf) { - cmd.createArg().setValue("-internalsf"); + addValue(cmd, "-internalsf"); } if (sectionsonly) { - cmd.createArg().setValue("-sectionsonly"); + addValue(cmd, "-sectionsonly"); } //add -tsa operations if declared addTimestampAuthorityCommands(cmd); //JAR source is required - cmd.createArg().setValue(jarSource.getPath()); + addValue(cmd, jarSource.getPath()); //alias is required for signing - cmd.createArg().setValue(alias); + addValue(cmd, alias); log("Signing JAR: " + jarSource.getAbsolutePath() +" to " + target.getAbsolutePath() + " as " + alias); - cmd.setFailonerror(true); - cmd.addConfiguredRedirector(redirector); + cmd.execute(); // restore the lastModified attribute @@ -621,12 +428,12 @@ */ private void addTimestampAuthorityCommands(final ExecTask cmd) { if(tsaurl!=null) { - cmd.createArg().setValue("-tsa"); - cmd.createArg().setValue(tsaurl); + addValue(cmd, "-tsa"); + addValue(cmd, tsaurl); } if (tsacert != null) { - cmd.createArg().setValue("-tsacert"); - cmd.createArg().setValue(tsacert); + addValue(cmd, "-tsacert"); + addValue(cmd, tsacert); } } 1.1 ant/src/main/org/apache/tools/ant/taskdefs/AbstractJarSignerTask.java Index: AbstractJarSignerTask.java =================================================================== /* * Copyright 2000-2005 The Apache Software Foundation * * 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. * */ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.Task; import org.apache.tools.ant.util.JavaEnvUtils; import org.apache.tools.ant.types.FileSet; import org.apache.tools.ant.types.RedirectorElement; import java.io.File; import java.util.Vector; /** * This is factored out from [EMAIL PROTECTED] SignJar}; a base class that can be used * for both signing and verifying JAR files using jarsigner */ public abstract class AbstractJarSignerTask extends Task { /** * The name of the jar file. */ protected File jar; /** * The alias of signer. */ protected String alias; /** * The url or path of keystore file. */ protected String keystore; /** * password for the store */ protected String storepass; /** * type of store,-storetype param */ protected String storetype; /** * password for the key in the store */ protected String keypass; /** * verbose output */ protected boolean verbose; /** * The maximum amount of memory to use for Jar signer */ protected String maxMemory; /** * the filesets of the jars to sign */ protected Vector filesets = new Vector(); /** * name of JDK program we are looking for */ protected static final String JARSIGNER_COMMAND = "jarsigner"; /** * redirector used to talk to the jarsigner program */ private RedirectorElement redirector; /** * error string for unit test verification: [EMAIL PROTECTED] */ public static final String ERROR_NO_SOURCE = "jar must be set through jar attribute " + "or nested filesets"; /** * Set the maximum memory to be used by the jarsigner process * * @param max a string indicating the maximum memory according to the JVM * conventions (e.g. 128m is 128 Megabytes) */ public void setMaxmemory(String max) { maxMemory = max; } /** * the jar file to sign; required * * @param jar the jar file to sign */ public void setJar(final File jar) { this.jar = jar; } /** * the alias to sign under; required * * @param alias the alias to sign under */ public void setAlias(final String alias) { this.alias = alias; } /** * keystore location; required * * @param keystore the keystore location */ public void setKeystore(final String keystore) { this.keystore = keystore; } /** * password for keystore integrity; required * * @param storepass the password for the keystore */ public void setStorepass(final String storepass) { this.storepass = storepass; } /** * keystore type; optional * * @param storetype the keystore type */ public void setStoretype(final String storetype) { this.storetype = storetype; } /** * password for private key (if different); optional * * @param keypass the password for the key (if different) */ public void setKeypass(final String keypass) { this.keypass = keypass; } /** * Enable verbose output when signing ; optional: default false * * @param verbose if true enable verbose output */ public void setVerbose(final boolean verbose) { this.verbose = verbose; } /** * Adds a set of files to sign * * @param set a set of files to sign * @since Ant 1.4 */ public void addFileset(final FileSet set) { filesets.addElement(set); } /** * init processing logic; this is retained through our execution(s) */ protected void beginExecution() { redirector = createRedirector(); } /** * any cleanup logic */ protected void endExecution() { redirector = null; } /** * Create the redirector to use, if any. * * @return a configured RedirectorElement. */ private RedirectorElement createRedirector() { RedirectorElement result = new RedirectorElement(); StringBuffer input = new StringBuffer(storepass).append('\n'); if (keypass != null) { input.append(keypass).append('\n'); } result.setInputString(input.toString()); result.setLogInputString(false); return result; } /** * these are options common to signing and verifying * @param cmd command to configure */ protected void setCommonOptions(final ExecTask cmd) { if (maxMemory != null) { cmd.createArg().setValue("-J-Xmx" + maxMemory); } if (verbose) { cmd.createArg().setValue("-verbose"); } } /** * bind to a keystore if the attributes are there * @param cmd command to configure */ protected void bindToKeystore(final ExecTask cmd) { if (null != keystore) { // is the keystore a file cmd.createArg().setValue("-keystore"); String location; File keystoreFile = getProject().resolveFile(keystore); if (keystoreFile.exists()) { location = keystoreFile.getPath(); } else { // must be a URL - just pass as is location = keystore; } cmd.createArg().setValue(location); } if (null != storetype) { cmd.createArg().setValue("-storetype"); cmd.createArg().setValue(storetype); } } /** * create the jarsigner executable task * @return a task set up with the executable of jarsigner, failonerror=true * and bound to our redirector */ protected ExecTask createJarSigner() { final ExecTask cmd = new ExecTask(this); cmd.setExecutable(JavaEnvUtils.getJdkExecutable(JARSIGNER_COMMAND)); cmd.setTaskType(JARSIGNER_COMMAND); cmd.setFailonerror(true); cmd.addConfiguredRedirector(redirector); return cmd; } /** * clone our filesets vector, and patch in the jar attribute as a new * fileset, if is defined * @return a vector of FileSet instances */ protected Vector createUnifiedSources() { Vector sources = (Vector)filesets.clone(); if (jar != null) { //we create a fileset with the source file. //this lets us combine our logic for handling output directories, //mapping etc. FileSet sourceJar = new FileSet(); sourceJar.setFile(jar); sourceJar.setDir(jar.getParentFile()); sources.add(sourceJar); } return sources; } /** * add a value argument to a command * @param cmd command to manipulate * @param value value to add */ protected void addValue(final ExecTask cmd, String value) { cmd.createArg().setValue(value); } } 1.1 ant/src/main/org/apache/tools/ant/taskdefs/VerifyJar.java Index: VerifyJar.java =================================================================== /* * Copyright 2000-2005 The Apache Software Foundation * * 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. * */ package org.apache.tools.ant.taskdefs; import org.apache.tools.ant.BuildException; import org.apache.tools.ant.DirectoryScanner; import org.apache.tools.ant.types.FileSet; import java.util.Vector; import java.io.File; /** * JAR verification task. * For every JAR passed in, we fork jarsigner to verify * that it is correctly signed. This is more rigorous than just checking for * the existence of a signature; the entire certification chain is tested * @since Ant 1.7 */ public class VerifyJar extends AbstractJarSignerTask { /** * no file message [EMAIL PROTECTED] */ public static final String ERROR_NO_FILE = "Not found :"; /** * certification flag */ private boolean certificates=false; /** * Ask for certificate information to be printed * @param certificates */ public void setCertificates(boolean certificates) { this.certificates = certificates; } /** * verify our jar files * @throws BuildException */ public void execute() throws BuildException { //validation logic final boolean hasFileset = filesets.size() > 0; final boolean hasJar = jar != null; if (!hasJar && !hasFileset) { throw new BuildException(ERROR_NO_SOURCE); } beginExecution(); try { Vector sources = createUnifiedSources(); for (int i = 0; i < sources.size(); i++) { FileSet fs = (FileSet) sources.elementAt(i); //get all included files in a fileset DirectoryScanner ds = fs.getDirectoryScanner(getProject()); String[] jarFiles = ds.getIncludedFiles(); File baseDir = fs.getDir(getProject()); //loop through all jars in the fileset for (int j = 0; j < jarFiles.length; j++) { String jarFile = jarFiles[j]; File jarSource = new File(baseDir, jarFile); verifyOneJar(jarSource); } } } finally { endExecution(); } } /** * verify a JAR. * @param jar * @throws BuildException if the file could not be verified */ private void verifyOneJar(File jar) { if(!jar.exists()) { throw new BuildException(ERROR_NO_FILE+jar); } final ExecTask cmd = createJarSigner(); setCommonOptions(cmd); bindToKeystore(cmd); //verify special operations addValue(cmd, "-verify"); if(certificates) { addValue(cmd, "-certs"); } //JAR is required addValue(cmd, jar.getPath()); log("Verifying JAR: " + jar.getAbsolutePath()); cmd.execute(); } } 1.9 +26 -0 ant/src/etc/testcases/taskdefs/signjar.xml Index: signjar.xml =================================================================== RCS file: /home/cvs/ant/src/etc/testcases/taskdefs/signjar.xml,v retrieving revision 1.8 retrieving revision 1.9 diff -u -r1.8 -r1.9 --- signjar.xml 23 Mar 2005 15:06:48 -0000 1.8 +++ signjar.xml 23 Mar 2005 16:51:42 -0000 1.9 @@ -24,6 +24,13 @@ <signjar alias="testonly" keystore="testkeystore" storepass="apacheant"/> </presetdef> + + <presetdef name="verify-base"> + <verifyjar keystore="testkeystore" + storepass="apacheant"/> + </presetdef> + + <presetdef name="sign"> <sign-base jar="${test.jar}" /> @@ -174,5 +181,24 @@ <sign tsaurl="http://localhost:0/" /> </target> + + <target name="testVerifyJar" depends="basic"> + <verify-base jar="${test.jar}"/> + </target> + + <target name="testVerifyJarUnsigned" depends="jar"> + <verify-base jar="${test.jar}"/> + </target> + + <target name="testVerifyFileset" depends="basic"> + <verify-base > + <fileset file="${test.jar}" /> + </verify-base> + </target> + + <target name="testVerifyNoArgs"> + <verify-base /> + </target> + </project> 1.13 +20 -7 ant/src/testcases/org/apache/tools/ant/taskdefs/SignJarTest.java Index: SignJarTest.java =================================================================== RCS file: /home/cvs/ant/src/testcases/org/apache/tools/ant/taskdefs/SignJarTest.java,v retrieving revision 1.12 retrieving revision 1.13 diff -u -r1.12 -r1.13 --- SignJarTest.java 23 Mar 2005 15:36:10 -0000 1.12 +++ SignJarTest.java 23 Mar 2005 16:51:42 -0000 1.13 @@ -17,14 +17,7 @@ package org.apache.tools.ant.taskdefs; -import java.io.File; -import java.io.FileReader; -import java.io.IOException; -import java.util.Date; -import java.util.Vector; -import java.util.Enumeration; import org.apache.tools.ant.BuildFileTest; -import org.apache.tools.ant.Project; import org.apache.tools.ant.util.JavaEnvUtils; /** @@ -156,4 +149,24 @@ assertLogContaining("java.net.ConnectException"); } } + + public void testVerifyJar() { + executeTarget("testVerifyJar"); + } + + public void testVerifyNoArgs() { + expectBuildExceptionContaining("testVerifyNoArgs", + "no args", + AbstractJarSignerTask.ERROR_NO_SOURCE); + } + + public void NotestVerifyJarUnsigned() { + expectBuildException("testVerifyJarUnsigned", + "unsigned JAR file"); + } + + public void testVerifyFileset() { + executeTarget("testVerifyFileset"); + } + }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]