42

I have a multi-module Maven project and in one module I want to create two artifacts during the build:

  • The main artifact which is a jar library that some of the other modules will depend on.
  • An executable jar file that executes some helper functions. No other modules depends on this and its only intended for the user to run it manually in certain situations.

Here is the code that I use to configure the maven-assembly-plugin plugin:

<plugin>
    <artifactId>
        maven-assembly-plugin
    </artifactId>
    <version>2.4</version>
    <executions>
      <execution>
        <id>dist-assembly</id>
        <phase>package</phase>
        <goals>
          <goal>single</goal>
        </goals>

        <configuration>
          <finalName>bso</finalName>
          <descriptorRefs>
            <descriptorRef>jar-with-dependencies</descriptorRef>
          </descriptorRefs>
          <finalName>helper-${project.version}</finalName>
          <appendAssemblyId>false</appendAssemblyId>
          <archive>
            <manifest>
              <mainClass>HelperMain<mainClass>
            </manifest>
          </archive>
        </configuration>

      </execution>
    </executions>
  </plugin>

I am setting appendAssemblyId to false because otherwise -jar-with-dependencies will be appended to the final name and I do not see the need for it. Omitting it gives a cleaner and easier to use file name.

When I run mvn integration-test then I receive the following warnings:

[WARNING] Configuration options: 'appendAssemblyId' is set to false, and 'classifier' is missing. Instead of attaching the assembly file: [...]/target/helper-5.0.0-SNAPSHOT.jar, it will become the file for main project artifact.

NOTE: If multiple descriptors or descriptor-formats are provided for this project, the value of this file will be non-deterministic!

[WARNING] Replacing pre-existing project main-artifact file: [...]/target/my.module-5.0.0-SNAPSHOT.jar with assembly file: [...]/target/helper-5.0.0-SNAPSHOT.jar

There are two things which irritate me:

  1. Despite the fact that the warning claims it will replace my.module-5.0.0-SNAPSHOT.jar with helper-5.0.0-SNAPSHOT.jar it does not actually do so and when the build has finished both files still have different sizes.

  2. Why does the warning about replacing the artifact appear at all?

  3. It seems that classifier is deprecated why does the warning ask me to use it?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
lanoxx
  • 12,249
  • 13
  • 87
  • 142

2 Answers2

66

That is because you are mis-interpreting the warnings.


Let's recap. A Maven project that is not of type pom will always produce, by default, what is called a main artifact. For a JAR, this artifact is the result of packaging the compiled sources into a JAR; for a WAR, it is the result of building the web-application.

What is important to remember is that this artifact is attached to the project: this terminology is useful when the project is installed (with mvn install), deployed (with mvn deploy) or released (with the maven-release-plugin). Attached means that this artifact will be installed / deployed / released when the project is. Not all files generated during a Maven build (basically, everything under the target folder) are; only the files that were attached. As such, you can very well create a lot of files under target but have a single installed artifact.

Alongside this main artifact, you may want your build to produce other artifacts to install or deploy. That's the concept of additional or secondary attached artifacts. The main examples are the Javadoc or the sources: typically when a project is released, its Javadoc and its sources also are. And that is where the notion classifier kicks in.

In a Maven repository, each and every file has to follow the same naming convention: artifactId-version(-classifier).type. Every secondary artifact will have the same GAV (group id, artifact id, version) as the main artifact so if you want to put inside a Maven repo 1 main artifact and 1 attached artifact (like it would be the case for a main JAR along with its JAR Javadoc and JAR sources), you need some way to distinguish them. Which is what the classifier is for: distinguish secondary artifacts from the main artifact.


Let's go back to your example now. Your Maven project, which is of jar packaging, will produce by default a main JAR artifact called my.module-5.0.0-SNAPSHOT.jar; still by default, this main JAR is attached to the project (and ready to be installed / deployed). Now you're configuring the maven-assembly-plugin to create a new JAR artifact (called helper-5.0.0-SNAPSHOT.jar but it really doesn't matter). The Assembly Plugin, by default, attaches to the project the artifact it produces. So you end up with 2 attached artifacts

  1. having the same artifact id of my.module; the fact that the file on disk inside the target folder is named helper for one is irrelevant, only the GAV coordinates matter
  2. having the same version of 5.0.0-SNAPSHOT
  3. having the same packaging of JAR

and no classifier to distinguish them. This is what raises the warning: you end up attaching to the project a secondary artifact that effectively replaces the main one, simply because it has the same coordinates. So the result is:

  1. Both files having different names on disk inside target, but that is irrelevant because
  2. Both share the same coordinates so only 1 will survive.

It is the one produced by the Assembly Plugin that will win the conflict, and replace the attached main artifact.

If you want to convince yourself of all that, run mvn clean install on the project and check your local repo. You will notice that only the jar-with-dependencies artifact will be installed. The other one (the main artifact) went poof.

You can also configure a <distributionManagement>:

<distributionManagement>
    <repository>
        <id>local-repo-test</id>
        <url>file://...</url>
    </repository>
</distributionManagement>

and invoke mvn clean deploy. You can then check that the only deployed artifact will be the jar-with-dependencies.


Final note: Yes, the classifier parameter of the Assembly Plugin is deprecated, because you should just use the assembly id as classifier.

Community
  • 1
  • 1
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • 7
    Tunaki, thanks a lot for your explanations. They really helped me to understand whats going on. I now realize that if I set `attach` to `false` then the warning goes away and I kind of get the desired result, but it also implies that the artifact will not be installed. So I have two more questions: 1. Should I just avoid setting the `appendAssemblyId` to false and instead live with the resulting file name? 2. Would you recommend that I do not use the `finalName` property? My original intension was just to have a more user-friendly name for the executable jar, am I not supposed to do that? – lanoxx Jul 20 '16 at 15:19
  • 1
    @lanoxx 1. If you want to install and release the 2 artifacts then you will have to use a classifier so yes, you must keep `appendAssemblyId` to `true`. 2. It all depends on how your users going to get the executable JAR. If they download it from a Maven repo that you deploy to, the name is forced by the repo itself based on the coordinates so you really have no say in the matter, and you'll have to live with it. The finalName is only used for the local file inside `target`. – Tunaki Jul 20 '16 at 15:30
  • @lanoxx (continued) If they get it from somewhere else, then you can rename it there to the name you want. Maven repos are mainly for fetching dependencies really. See also the mails http://maven.40175.n5.nabble.com/What-is-the-purpose-of-lt-finalName-gt-really-td113492.html and http://maven.40175.n5.nabble.com/Jar-finalName-to-m2-repository-td2639096.html. I will also mention that you could also potentially "ignore" the warning: let the Assembly Plugin replace the main artifact; maybe you don't need it anyway. – Tunaki Jul 20 '16 at 15:30
  • another thought I had today. If the name given in finalName is unique for the project and given the fact that the name used for the artifact installation still uses the standard maven GAVC coordinates, then actually I would say its not really necessary to append the assemblyId to the local name (in the target folder), as long as its still appended during artifact installation. – lanoxx Jul 21 '16 at 10:39
  • @lanoxx Yeah but AFAIK the assembly id (so the classifier of the artifact) will always be appended into the local file name. – Tunaki Jul 21 '16 at 10:46
0

If you want use the pre-defined decriptor jar-with-dependencies but have a classifier that is different from "jar-with-dependencies", you can simply copy the "jar-with-dependencies" format into your own assembly file and re-name the id to whatever classifier you'd like.

Here is the pre-defined descriptor format (source: https://maven.apache.org/plugins/maven-assembly-plugin/descriptor-refs.html#jar-with-dependencies):

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.2.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 https://maven.apache.org/xsd/assembly-2.2.0.xsd">
  <!-- TODO: a jarjar format would be better -->
  <id>jar-with-dependencies</id>
  <formats>
    <format>jar</format>
  </formats>
  <includeBaseDirectory>false</includeBaseDirectory>
  <dependencySets>
    <dependencySet>
      <outputDirectory>/</outputDirectory>
      <useProjectArtifact>true</useProjectArtifact>
      <unpack>true</unpack>
      <scope>runtime</scope>
    </dependencySet>
  </dependencySets>
</assembly>

Copy this into your own assembly.xml, and rename the id:

<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.2.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.2.0 https://maven.apache.org/xsd/assembly-2.2.0.xsd">
  <!-- TODO: a jarjar format would be better -->
  <id>my-classifier</id>
  <formats>
    <format>jar</format>
  </formats>
  <includeBaseDirectory>false</includeBaseDirectory>
  <dependencySets>
    <dependencySet>
      <outputDirectory>/</outputDirectory>
      <useProjectArtifact>true</useProjectArtifact>
      <unpack>true</unpack>
      <scope>runtime</scope>
    </dependencySet>
  </dependencySets>
</assembly>

Now, if you set <appendAssemblyId> to true, you will get the same jar as before, but with a custom classifier.