1

I am fairly new to working with Maven to build my Java projects, and have run into a situation I don't know how to handle.

I have a Java application that has 3 dependencies, let's call them a, b, and c. However, c will be a different artifact depending on the platform we are building on, so I've used profiles to achieve this. Here is a snippet from my pom.xml:

<profiles>
  <profile>
    <id>win32</id>
    <activation>
      <os>
        <family>windows</family>
        <arch>x86</arch>
      </os>
    </activation>
    <dependencies>
      <dependency>
        <groupId>com.seanbright</groupId>
        <artifactId>c-win32-x86</artifactId>
        <version>1.0.0</version>
      </dependency>
    </dependencies>
  </profile>
  <profile>
    <id>win64</id>
    <activation>
      <os>
        <family>windows</family>
        <arch>amd64</arch>
      </os>
    </activation>
    <dependencies>
      <dependency>
        <groupId>com.seanbright</groupId>
        <artifactId>c-win32-x86_64</artifactId>
        <version>1.0.0</version>
      </dependency>
    </dependencies>
  </profile>
</profiles>

The a and b artifacts are listed as dependencies at the POM level as they are platform agnostic and aren't activated along with the profile. They aren't shown here for the sake of brevity.

Now I want to build an executable JAR of my project, and include a, b, and c in a lib/ directory along side the generated JAR from my code, so I would end up with something like this:

target/my-project-1.0.0.jar
target/lib/a-1.0.0.jar
target/lib/b-1.0.0.jar
target/lib/c-1.0.0.jar

The manifest in my-project-1.0.0.jar will have the appropriate classpath so that it can be double clicked on and the application will launch. I use the dependency:copy-dependencies and jar:jar goals to make all of this work:

<build>
  <plugins>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-dependency-plugin</artifactId>
      <version>2.7</version>
      <executions>
        <execution>
          <id>copy-dependencies</id>
          <goals>
            <goal>copy-dependencies</goal>
          </goals>
          <configuration>
            <outputDirectory>${project.build.directory}/lib</outputDirectory>
            <overWriteReleases>false</overWriteReleases>
            <overWriteSnapshots>false</overWriteSnapshots>
            <overWriteIfNewer>true</overWriteIfNewer>
            <includeScope>runtime</includeScope>
          </configuration>
        </execution>
      </executions>
    </plugin>
    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-jar-plugin</artifactId>
      <version>2.4</version>
      <configuration>
        <archive>
          <manifest>
            <mainClass>com.seanbright.myproject.Launch</mainClass>
            <addClasspath>true</addClasspath>
            <classpathPrefix>lib/</classpathPrefix>
          </manifest>
        </archive>
      </configuration>
    </plugin>
  </plugins>
</build>

And... it works. The only problem, is that c is copied to the lib/ directory (and added to the Class-Path in the manifest) as c-win32-x86-1.0.0.jar or c-win32-x86_64-1.0.0.jar depending on the active profile, and I want it to end up as c-1.0.0.jar instead.

Using dependency:copy with destFileName instead of dependency:copy-dependencies results in the correct filename, but the entry in the Class-Path still refers to the "fully qualified" artifact name (i.e. lib/c-win32-x86-1.0.0.jar).

Am I going about this the wrong way? Is there an easier way to accomplish what I am trying to do?

Sean Bright
  • 118,630
  • 17
  • 138
  • 146

1 Answers1

1

The Set Up The Classpath:Altering The Classpath: Using a Custom Classpath Format told us as the following: -

At times, you may have dependency archives in a custom format within your own archive, one that doesn't conform to any of the above classpath layouts. If you wish to define a custom layout for dependency archives within your archive's manifest classpath, try using the <classpathLayoutType> element with a value of 'custom', along with the <customClasspathLayout> element, like this:

<project>
  ...
  <build>
    <plugins>
      <plugin>
         <artifactId>maven-war-plugin</artifactId>
         <configuration>
           <archive>
             <manifest>
               <addClasspath>true</addClasspath>
               <classpathLayoutType>custom</classpathLayoutType>
               <customClasspathLayout>WEB-INF/lib/$${artifact.groupIdPath}/$${artifact.artifactId}-$${artifact.version}$${dashClassifier?}.$${artifact.extension}</customClasspathLayout>
             </manifest>
           </archive>
         </configuration>
      </plugin>
    </plugins>
  </build>
  ...
  <dependencies>
    <dependency>
      <groupId>commons-lang</groupId>
      <artifactId>commons-lang</artifactId>
      <version>2.1</version>
    </dependency>
    <dependency>
      <groupId>org.codehaus.plexus</groupId>
      <artifactId>plexus-utils</artifactId>
      <version>1.1</version>
    </dependency>
  </dependencies>
  ...
</project>

This classpath layout is a little more involved than the previous examples. To understand how the value of the <customClasspathLayout> configuration is interpreted, it's useful to understand the rules applied when resolving expressions within the value:

  1. If present, trim off the prefix 'artifact.' from the expression.
  2. Attempt to resolve the expression as a reference to the Artifact using reflection (eg. 'artifactId' becomes a reference to the method 'getArtifactId()').
  3. Attempt to resolve the expression as a reference to the ArtifactHandler of the current Artifact, again using reflection (eg. 'extension' becomes a reference to the method 'getExtension()').
  4. Attempt to resolve the expression as a key in the special-case Properties instance, which contains the following mappings:
    • 'dashClassifier': If the Artifact has a classifier, this will be '- $artifact.classifier', otherwise this is an empty string.
    • 'dashClassifier?': This is a synonym of 'dashClassifier'.
    • 'groupIdPath': This is the equivalent of '$artifact.groupId', with all '.'characters replaced by '/'.

The manifest classpath produced using the above configuration would look like this:

Class-Path: WEB-INF/lib/org/codehaus/plexus/plexus-utils-1.1.jar WEB-INF/lib/commons-lang/commons-lang-2.1.jar

I hope this may help.

Charlee Chitsuk
  • 8,847
  • 2
  • 56
  • 71
  • I'd like to upvote this answer, but it's literally a copy/paste of a page I had already looked at that doesn't answer my question. Thank you though. – Sean Bright Mar 29 '13 at 11:45