dev@glassfish.java.net

Maven-built deployment plans confuse DeploymentPlanArchive and break asadmin deploy --deploymentplan.

From: Owen Jacobson <owen.jacobson_at_grimoire.ca>
Date: Wed, 1 Sep 2010 23:14:05 -0400

(I mistakenly sent this to dev_at_glassfish.org first -- apologies if this comes through twice.)

Hi there,

I just solved something that's been blocking me for a couple of days, and I thought you guys might appreciate knowing about it. First, a reproduction case:

1. Build a deployment plan archive (per http://docs.sun.com/app/docs/doc/820-7693/gijyb?l=en&a=view) with Maven. The following POM will do:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
   <modelVersion>4.0.0</modelVersion>

   <groupId>com.example.glassfish</groupId>
   <artifactId>deployment-plan</artifactId>
   <version>1.0-SNAPSHOT</version>
</project>

2. Note the contents of the deployment plan. In my case:

$ jar tf test-environment-1.0-SNAPSHOT.jar
META-INF/
META-INF/MANIFEST.MF
account.war.sun-web.xml
root.war.sun-web.xml
sun-application.xml
META-INF/maven/
META-INF/maven/ca.grimoire/
META-INF/maven/ca.grimoire/test-environment/
META-INF/maven/ca.grimoire/test-environment/pom.xml
META-INF/maven/ca.grimoire/test-environment/pom.properties

3. Try to deploy an application with this deployment plan.

Under Glassfish 3.0.1, the deploy command fails with

$ bin/asadmin deploy \
   --deploymentplan test-environment-1.0-SNAPSHOT.jar \
   ~/Development/grimoire/grimoire/target/grimoire-1.0-SNAPSHOT.ear
com.sun.enterprise.admin.cli.CommandException: remote failure: Exception while deploying the app : java.lang.NullPointerException

and in the logs, the following stack trace:

[#|2010-08-30T19:58:30.661-0400|SEVERE|glassfish3.0.1|javax.enterprise.system.core.com.sun.enterprise.v3.server|_ThreadID=21;_ThreadName=Thread-1;|Exception while deploying the app
java.lang.NullPointerException
        at com.sun.enterprise.util.shared.ArchivistUtils.copyWithoutClose(ArchivistUtils.java:58)
        at com.sun.enterprise.deployment.archivist.Archivist.copyJarElements(Archivist.java:1469)
        at com.sun.enterprise.deployment.archivist.Archivist.copyInto(Archivist.java:1670)

I've reproduced the Archivist.copyJarElements here, with annotations:

   protected void copyJarElements(ReadableArchive in, WritableArchive out, Vector ignoreList)
           throws IOException {

       /* entries is generated *by in* -oj */
       Enumeration entries = in.entries();
/* ... */
       if (entries != null) {
           for (; entries.hasMoreElements();) {
               String anEntry = (String) entries.nextElement();
               if (ignoreList == null || !ignoreList.contains(anEntry)) {
                   InputStream is = in.getEntry(anEntry);
                   OutputStream os = out.putNextEntry(anEntry);
                   /* Oops! 'is' is null here, so copyWithoutClose NPEs out. -oj */
                   ArchivistUtils.copyWithoutClose(is, os);
                   is.close();
                   out.closeEntry();
               }
           }
       }
   }

Digging around a bit lead me to com.sun.enterprise.deployment.deploy.shared.DeploymentPlanArchive, the ReadableArchive implementation that handles deployment plans. The entries() and getEntry() methods contain some medium-hairy code designed to munge paths like foo.jar.sun-ejb.xml, and designed specifically to ignore META-INF/MANIFEST.MF. However, Maven's extra archive metadata confuses it in such a way that entries() includes Entry objects for pom.xml and pom.properties, but getEntry() will return null for those entries.

I think this is a bug, but I don't know what the right fix is. It's easy to work around, though: telling maven not to include Maven descriptors in the resulting JAR is simple:

           <plugin>
               <artifactId>maven-jar-plugin</artifactId>

               <configuration>
                   <archive>
                       <addMavenDescriptor>false</addMavenDescriptor>
                   </archive>
               </configuration>
           </plugin>

and, absent those two files (and their containing directories), asadmin deploy is quite happy.

Maybe getEntries() should ignore unexpected files in META-INF? I suspect something like this will happen if you sign your deployment plan, too.

-o