A7X A7X - 6 days ago 5
Java Question

Maven: compile a project using shade-plugin specifying main class and properties file

New to Maven, I was trying to compile a project using Maven with maven-shade-plugin (as it seems the best plugin to build a fat jar out there). I tried to specify my main class to make a runnable jar file and some .properties files which contains translation strings.

Compilation and build seems passed, according to netbeans output, but I can't run it as following (assuming the jar built by Maven is renamed "program"):

/usr/bin/java -cp program.jar bot.Main
> could not find or load main class bot.Main


this is my project file structure:

project files

and this is my pom.xml file:

<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany</groupId>
<artifactId>mavenproject1</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<build>
<resources>
<resource>
<directory>src/main/java/resources</directory>
<includes>
<include>**/*.properties</include>
</includes>
</resource>
</resources>
<plugins>

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<classpathPrefix>lib/</classpathPrefix>
<mainClass>bot.Main</mainClass>
</manifest>
</archive>
<shadedArtifactAttached>true</shadedArtifactAttached>
<shadedClassifierName>launcher</shadedClassifierName>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>

<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<executions>
<execution>
<goals>
<goal>java</goal>
</goals>
</execution>
</executions>
<configuration>
<mainClass>bot.Main</mainClass>
</configuration>
</plugin>

</plugins>
</build>
<dependencies>
<dependency>
<groupId>org.telegram</groupId>
<artifactId>telegrambots</artifactId>
<version>2.4.0</version>
<classifier>jar-with-dependencies</classifier>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
</project>


Are resources included properly and why can't I run my program using java -jar command without specifying main class? It says me "invalid or corrupt jar file" which this should mean that is not runnable.

Also, why doesn't neither start specifying Main classpath?

Answer

The issue is with your configuration of the Shade Plugin, which is currently

<configuration>
  <archive>
    <manifest>
      <addClasspath>true</addClasspath>
      <classpathPrefix>lib/</classpathPrefix>
      <mainClass>bot.Main</mainClass>
    </manifest>
  </archive>
  <shadedArtifactAttached>true</shadedArtifactAttached>
  <shadedClassifierName>launcher</shadedClassifierName>
</configuration>

There is no <archive> parameter to the shade goal. The fact that you're using a configuration element that is non-existent is not an error, the configuration will just be ignored, and this explains why your main class isn't being set in the Manifest.

To build an executable JAR with the Shade Plugin, you need to provide the ManifestResourceTransformer as a transformers. The correct configuration would be:

<configuration>
  <transformers>
    <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
      <mainClass>bot.Main</mainClass>
    </transformer>
  </transformers>
  <shadedArtifactAttached>true</shadedArtifactAttached>
  <shadedClassifierName>launcher</shadedClassifierName>
</configuration>

Note that with this configuration, the shaded JAR will not replace the main JAR. shadedArtifactAttached is set to true, which means that the shaded JAR will be attached to the project as a secondary artifact. It will be distinguished from the main JAR with its classifier of launcher, i.e. the shadedClassifierName parameter.

After running mvn clean package on this project, you will then have 2 JARs created:

  • mavenproject1-1.0-SNAPSHOT.jar, which is the main JAR. This JAR only consists of the compiled Java sources of your application. It is not executable and does not contain all of the dependencies' classes in it.
  • mavenproject1-1.0-SNAPSHOT-launcher.jar is the shaded attached JAR, which was constructed by the Shade plugin. This one is executable and contains the dependencies' classes.

This means that if you want to launch your application as an executable JAR, you have to launch the -launcher.jar, and not the other one, with

java -jar mavenproject1-1.0-SNAPSHOT-launcher.jar

As a side-note, both JARs will contain your resources that are in <directory>src/main/java/resources</directory>, because they are resources of the project itself, as declared with the <resource> element. However, it would be preferable to respect the standard directory layout and place the resources in src/main/resources instead.

Comments