4

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?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
A7X
  • 119
  • 2
  • 12

1 Answers1

10

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.

Community
  • 1
  • 1
Tunaki
  • 132,869
  • 46
  • 340
  • 423
  • thank you! But when I try to launch it, I get `unable to access jarfile` error. Is this something still related to shade plugin settings? – A7X Nov 27 '16 at 13:35
  • 1
    @A7X It sounds like you didn't run the `java -jar` command at the right place. The JAR isn't in the directory you launched it. If you're in your project, it is inside the `target` folder, not the root folder. This isn't related to the Shade plugin though. – Tunaki Nov 27 '16 at 13:38
  • yea it was my fault. By the way, I'm still getting MissingResourceBundleException can't find bundle for resources.strings locale en_US. I include them in my project using `ResourceBundle.getBundle("resources.strings", new Locale("en", "US"), loader);` is it because I'm passing the wrong filepath to resources? Should it be the fullpath? – A7X Nov 27 '16 at 13:49
  • @A7X This is a very different error, and it means that the shaded JAR is now correct. I'll take a guess at this issue: your resources are inside `src/main/java/resources`, which is very unconventional. Your IDE placed `src/main/java` as a source folder (Maven convention), which means that the resources are access with `resources.strings` (prefixing with `resources.` since it's a folder on the classpath). But now, in your uber jar, you have told Maven that `src/main/java/resources` was a source folder, which it upholds. So to access your resources, you must have `"strings"` alone. – Tunaki Nov 27 '16 at 13:53
  • thank you. It didn't solve this other issue but I accept your answer as it solved the main one :) – A7X Nov 27 '16 at 14:14