Hi Jerome,

Thanks for the webstart plugin. I have also ran into the problem where JARs that are signed twice don't work with Web Start. My solution in the past has been to not sign the JARs that are already signed. However, it would be nice to be able to remove the signature from a JAR that is already signed without loosing other important info in the manifest, and then resign it. It seems to me that the JDK should have functionality to do this. You can use jarsigner to sign a JAR but not unsign it.

I've experimented with Maven2 and your webstart plugin by making modifications locally to support not signing a JAR that is already signed and it has worked for me so far. I've been planning to email you about the changes I made to see if you want to incorporate something like them into Maven2 and your plugin.

I modified the JarSignVerifyMojo so that you can use it to test if a JAR is already signed. I changed JarSignVerifyMojo so that it can be configured to set a boolean instead of throwing an Exception when a JAR isn't signed.

public class JarSignVerifyMojo
   extends AbstractMojo
{
...
   /** Is the JAR signed? */
   private boolean signed;

   /** Should an exception be thrown when the JAR is not signed? */
   private boolean errorWhenNotSigned = true;

...

   public void setErrorWhenNotSigned( boolean errorWhenNotSigned ) {
       this.errorWhenNotSigned = errorWhenNotSigned;
   }

   public boolean isSigned() {
       return signed;
   }

...

   public void execute() throws MojoExecutionException {
...
       try
       {
int result = executeCommandLine( commandLine, null, outConsumer, errConsumer ); if ( result != 0 )
           {
               throw new MojoExecutionException( "Result of " + commandLine
                   + " execution is: \'" + result + "\'." );
           }

           if ( !outConsumer.matched )
           {
               signed = false;

               if ( errorWhenNotSigned )
               {
throw new MojoExecutionException( "Verify failed: " + outConsumer.firstOutLine );
               }
           }
           else
           {
               signed = true;
           }
       }
       catch ( CommandLineException e )
       {
throw new MojoExecutionException( "command execution failed", e );
       }
   }
...
}

Then I modified JarSignMojo to check if a JAR is signed before signing it. I also modified JarSignMojo to have no default for the signedJar variable so that I could have the resulting signed JAR be the same JAR as the one identified by jarPath. If I set signedJar and jarPath to the same value I get a JVM error. However, if signedJar is empty I achieve the same result with no JVM error. And my signed JAR gets installed into the repository and can subsequently be included as a dependency in another project. I think that the default for JarSignMojo should be that if no configuration is given (other than the key to sign with) then the JAR generated by JarMojo gets signed (in other words the default signed JAR isn't ${project.build.directory}/${project.build.finalName}/signed/${project.build.finalName}.${project.packaging}, but is ${project.build.directory}/${project.build.finalName}.${project.packaging}). By the way, I used ${project.packaging} because the JAR being signed could be a WAR, EAR, etc.

public class JarSignMojo
   extends AbstractMojo
{
...
   /**
    * Set this to 'true' to bypass jar signing.
    *
    * @parameter expression="${maven.jar.sign.skip}"
    */
   private boolean skip;

...

   /**
    * 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="${signedjar}"
    * @todo make a File?
    */
   private File signedjar;

...

   public void execute()
       throws MojoExecutionException
   {
       if ( skip )
       {
getLog().info( "Skipping JAR signing for file " + getJarFile().getAbsolutePath() );
           return;
       }

       File signedJarFile = null;
if ( signedjar != null && signedjar.trim().length() != 0 )
       {
           signedJarFile = new File( signedjar );
       }
       else
       {
           signedJarFile = getJarFile();
       }

       JarSignVerifyMojo verifyJar = new JarSignVerifyMojo();

       if ( signedJarFile.exists() ) {
           verifyJar.setWorkingDir( workingDirectory );
           verifyJar.setBasedir( basedir );
           verifyJar.setJarPath( signedJarFile );
           verifyJar.setVerbose( verbose );
           verifyJar.setErrorWhenNotSigned( false );
           verifyJar.execute();
       }

       // Don't sign the JAR if it is already signed.
       if ( !verifyJar.isSigned() ) {
           signJar();
if ( verify ) {
               verifyJar.setErrorWhenNotSigned( true );
               verifyJar.execute();
           }
       }
       else
       {
getLog().info( "JAR " + getJarFile().getAbsolutePath() + " is already signed." );
       }
   }
...
}

Also, I didn't like the idea of having to use a Velocity template to insert dependencies into the JNLP file, so used DOM to parse the JNLP file and insert dependencies. That way the JNLP file can simply be treated as a JNLP file.

public class JnlpMojo
   extends AbstractMojo
{

...

   public void execute()
       throws MojoExecutionException
   {
...
       try
       {
           copyResources(workDirectory);
           Document jnlpDoc = parseJnlp();
           String mainClass = getMainClass(jnlpDoc);

           artifactWithMainClass = null;

           processDependencies(mainClass);
...
           processJnlpFile(jnlpDoc);
...
   }

...

   /**
    * Parses the JNLP XML document specified by the [EMAIL PROTECTED] #jnlpFile}
* parameter, or the first JNLP file found in the JNLP resources directory.
    *
    * @return The XML document as a DOM Document.
    */
   protected Document parseJnlp() throws MojoExecutionException {
       Document jnlpDoc = null;

       if (jnlpFile == null || jnlpFile.length() == 0) {
           File[] jnlpFiles = resourcesDirectory.listFiles(jnlpFileFilter);
           jnlpFile = jnlpFiles[0];
       }

       try {
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
           DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
           jnlpDoc = docBuilder.parse(jnlpFile);
       } catch (Exception e) {
           getLog().debug(e.toString());
throw new MojoExecutionException("Could not parse the JNLP deployment descriptor", e);
       }

       return jnlpDoc;
   }

   /**
* Returns the Java class containing the main method as defined by the JNLP document.
    */
protected String getMainClass(Document jnlpDoc) throws MojoExecutionException { NodeList nodeList = jnlpDoc.getElementsByTagName("application-desc");

       if (nodeList == null || nodeList.length == 0) {
throw new MojoExecutionException("JNLP file is missing application-desc tag.");
       }

       Node resources = nodeList.item(0);
       NamedNodeMap attributes = resources.getAttributes();

if (attributes == null || attributes.getNamedItem("main-class") == null) { throw new MojoExecutionException("JNLP file is missing main-class attribute.");
       }

return resources.getAttributes().getNamedItem("main-class").getNodeValue();
   }

   /**
    * Inserts dependencies into the JNLP file.
    */
protected void processJnlpFile(Document jnlpDoc) throws MojoExecutionException {
       if (jnlpFile == null || jnlpFile.length() == 0) {
           File[] jnlpFiles = resourcesDirectory.listFiles(jnlpFileFilter);
           jnlpFile = jnlpFiles[0];
       }

       Node resources = jnlpDoc.getElementsByTagName("resources").item(0);

       for (int i = 0; i < packagedJnlpArtifacts.size(); i++) {
           Artifact artifact = (Artifact) packagedJnlpArtifacts.get(i);
           Element jar = jnlpDoc.createElement("jar");
           jar.setAttribute("href", artifact.getFile().getName());
           resources.appendChild(jar);
       }

       try {
TransformerFactory transformerFactory = TransformerFactory.newInstance();
           Transformer transformer = transformerFactory.newTransformer();
           transformer.transform(new DOMSource(jnlpDoc),
new StreamResult(new File(workDirectory, jnlpFile.getName())));
       } catch (Exception e) {
           getLog().debug(e.toString());
throw new MojoExecutionException("Could not process the JNLP deployment descriptor", e);
       }
   }
...
}

Thanks,
Richard Allen


jerome lacoste wrote:

Hi all,

One of the last thing I want to fix before making the m2 webstart
plugin beta is related to signing jars that are already signed.

I haven't had to face the issue yet, but it seems that there's a
potential issue when dealing with including such dependencies in a
webstart application.

In particular see:

http://jira.codehaus.org/browse/MOJO-7#action_49160

and the relevant m1 jnlp issues:
http://jira.codehaus.org/browse/MPJNLP-20
http://jira.codehaus.org/browse/MPJNLP-28

I would appreciate feedback (and better: test cases) to avoid
releasing an m2 plugin that doesn't solve these problems.

Cheers,

Jerome

---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to