LibrariesAndClassPathBuildProblem

From APIDesign

Revision as of 05:57, 16 September 2009 by JaroslavTulach (Talk | contribs)
(diff) ←Older revision | Current revision (diff) | Newer revision→ (diff)
Jump to: navigation, search

In Maven, compared to Ant, I have huge problems to configure the system to do exactly what I want. For example, I want to package my application (two my own JAR files, plus all the dependency JARs) into a single ZIP file that everyone could download. This is not easy at all. The only tool that does something similar seems to be shade plugin. But its functionality is still far from optimal.

It seems to me that development of simple Java desktop applications is not really well supported by current Maven plugins. Packaging to a compound ZIP seemed unsupported, execution of a main method is supported, but not by official Apache's plugin. It seems to me that Java desktop is second class citizen in Maven.

On the other hand one can find tons of tools for server side. Packaging to WAR file is possible (this is almost the ZIP file requested above, and I even wanted to create WAR instead of ZIP, but it does not work, it requires some web.xml file, which I obviously don't have). Execution on glashfish, grizzly, etc. is easy. All these application servers have their own plugins and thus are easy to start with.

All this leads to me following question: is Maven really ready for development of Java desktop applications? And a follow up: Why it is not?

It is Ready! It is Just Different.

Thanks to all your overwhelming responses I managed to find solution of the packaging. I can confirm that Maven can be used to package Java desktop applications. Just give up on shade and use assembly plugin. This is the configuration in my pom.xml file:

<plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.2-beta-2</version>
    <executions>
      <execution>
        <id>create-executable-jar</id>
        <phase>package</phase>
        <goals>
          <goal>single</goal>
        </goals>
        <configuration>
          <descriptors>
            <descriptor>all-zip.xml</descriptor>
          </descriptors>
          <finalName>myapp-${version}</finalName>
        </configuration>
      </execution>
    </executions>
</plugin>
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifest>
                <addClasspath>true</addClasspath>
                <classpathPrefix>lib/</classpathPrefix>
                <mainClass>myapp.Main</mainClass>
            </manifest>
        </archive>
    </configuration>
</plugin>

One also needs to create additional assembly file. In my case it is located next to the pom.xml and named all-zip.xml:

<?xml version="1.0" encoding="UTF-8"?>
<assembly>
 <id>all</id>
  <formats>
    <format>zip</format>
  </formats>
  <dependencySets>
    <dependencySet>
        <useProjectArtifact>false</useProjectArtifact>
        <outputDirectory>lib</outputDirectory>
    </dependencySet>
  </dependencySets>
  <files>
    <file>
      <source>target/myapp-${version}.jar<source>
      <outputDirectory>/</outputDirectory>
    </file>
  </files>
</assembly>

And that is all. I have to admit that at the end this is much simpler than using plain Ant. In Ant it would be an enormous amount of XML code to generate the Class-Path attribute tons of <pathconvert> magic. In Maven this is for almost free (as soon as you know which plugin is the right one to use). My application now has executable main JAR and all the libraries in subfolder lib. It can be executed as java -jar myapp-1.8/myapp-1.8.jar. Perfect!

myapp-1.8/myapp-1.8.jar
myapp-1.8/lib/jersey-core-1.1.0-ea.jar
myapp-1.8/lib/jsr311-api-1.1.jar
myapp-1.8/lib/jersey-server-1.1.0-ea.jar
myapp-1.8/lib/asm-3.1.jar
myapp-1.8/lib/jersey-json-1.1.0-ea.jar
myapp-1.8/lib/jettison-1.1.jar
myapp-1.8/lib/jaxb-impl-2.1.10.jar
myapp-1.8/lib/jackson-asl-0.9.4.jar
myapp-1.8/lib/org-netbeans-libs-freemarker-RELEASE67.jar
myapp-1.8/lib/org-netbeans-modules-queries-RELEASE67.jar
myapp-1.8/lib/org-openide-filesystems-RELEASE67.jar
myapp-1.8/lib/org-openide-util-RELEASE67.jar
myapp-1.8/lib/jersey-client-1.1.0-ea.jar
myapp-1.8/lib/freemarker-2.3.8.jar

I am surprised this is not one of the standard archetypes. As I wrote in Chapter 9, one of the best APIs is wizard. Good wizard can turn any API, regardless how bad, complicated and hidden, into perfectly shining, beautiful star. Good tools make everything easier. Having Java Application Archetype would save me a lot of hours of googling and blogging (if I could find the archetype, of course).

Compare to Ant

Laszlo Kishalmi donated the same code in Ant:

<copy todir="${dist.dir}/lib" flatten="true">
    <fileset dir="${basedir}/lib" includes="**/*.jar"/>
</copy>
<pathconvert property="main.jar.class-path" dirsep="/" pathsep=" ">
     <path><fileset dir="${dist.dir}" includes="lib/*.jar"/></path>
     <map from="${dist.dir}/" to=""/>
</pathconvert>
<jar update="true" jarfile="${dist.dir}/main.jar">
    <manifest>
        <attribute name="Class-Path" value="${main.jar.class-path}"/>
    </manifest>
</jar>

Can you feel the difference? Especially the pathconvert is very close to black magic and voodoo. I know it exists, but whenever I need to use pathconvert I usually need two or three hours to remind myself again about its capabilities. Obviously as soon as I am done I want to cluelessly forget about such nightmare as soon as possible.

Personal tools
buy