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]