3

We have multi module Maven project with following modules:

  • Commons
  • Model
  • Repository
  • Service
  • Web

We've googled around and we didn't found a solution how to make one executable jar when project has this kind of structure.

Here is the parent pom.xml:

<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>...</groupId>
    <artifactId>...</artifactId>
    <version>1.0-SNAPSHOT</version>
    <modules>
        <module>Model</module>
        <module>Web</module>
        <module>Service</module>
        <module>Repository</module>
        <module>Common</module>
    </modules>
    <packaging>pom</packaging>

    <name>Api</name>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>

        <mssql.jdbc.driver.version>4.2</mssql.jdbc.driver.version>

        <apache.commons.lang.version>3.3.2</apache.commons.lang.version>
        <apache.commons.collection.utils.version>4.1</apache.commons.collection.utils.version>
    </properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.4.1.RELEASE</version>
    </parent>

    <dependencies>

        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-envers</artifactId>
        </dependency>

        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
        </dependency>

    </dependencies>

</project>

So, very basic parent pom.xml. We know that we can't use Spring Boot Maven plugin when packaging is pom, so we've defined it in pom.xml from Web module:

<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">
    <parent>
        <artifactId>...</artifactId>
        <groupId>...</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <groupId>...</groupId>
    <artifactId>...</artifactId>
    <packaging>jar</packaging>

    <name>...</name>

    <dependencies>

        <dependency>
            <groupId>...</groupId>
            <artifactId>Service</artifactId>
            <version>${project.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <version>1.4.1.RELEASE</version>
                <configuration>
                    <mainClass>...App</mainClass>
                </configuration>
                <!--<executions>-->
                    <!--<execution>-->
                        <!--<goals>-->
                            <!--<goal>repackage</goal>-->
                        <!--</goals>-->
                    <!--</execution>-->
                <!--</executions>-->
            </plugin>
        </plugins>
    </build>

</project>

The rest of pom.xmls from other modules are pretty much the same: Service's pom.xml includes Repository.jar, Repository includes Model and Common.

Problem 1: when we run mvn install, plugin creates fat jar in target folder of web module. But when we try to run it with java -jar name-of-jar it gives java.lang.NoClassDefFoundError about some class from Service module - and you can see from Web's pom.xml that Service module is included (there is also a Service.jar packed as library in Web.jar)

Problem 2: what's interesting is that Service.jar inside of Web.jar also contains pretty much all the dependencies that are already present in Web.jar - basically they are duplicated.

Did anyone managed to create executable fat jar using Spring Boot Maven plugin and Maven Module project structure?

Nikola Zarić
  • 865
  • 1
  • 9
  • 28
  • I have a hunch the devtools may be the root of it all (hence my answer). NoClassDefFound means that the class was found but failed during its initialization. I'm guessing it could be caused by duplicated jars included in the WAR and the Service.jar. A mismatch of classes could be available in different classloader and cause the NoClassDefFound ?. Again, if the error persist even after moving devtools, please provide the stacktrace. – alexbt Sep 25 '16 at 20:18
  • Yes, this solved both problems, thank you. – Nikola Zarić Sep 26 '16 at 08:43

1 Answers1

1

Problem 1 & 2

You should not have devtools as dependency in your parent pom. Move the following to the web/spring-boot module:

<dependency>
   <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-devtools</artifactId>
</dependency>

If you look at devtools's pom, it includes the following:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-autoconfigure</artifactId>
</dependency>

So your service jar ends up with some spring boot dependencies.

Community
  • 1
  • 1
alexbt
  • 16,415
  • 6
  • 78
  • 87