5

I've a project that has three modules: A, B and COMMON. I would like to put the common logic inside the COMMON module (the model for example), and then the two other modules with a dependency to it.

The two modules (A, B) will be build separately to create two different jars.

I'm testing it trying to put the log dependency inside the COMMON module, and then build the A project. It will build correctly but if I try to run the jar with "java -jar a.jar" it fails with a NoClassDefFound:

Exception in thread "main" java.lang.NoClassDefFoundError: org/slf4j/LoggerFactory

The root pom:

<modules>
    <module>common</module>
    <module>a</module>
</modules>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.12</version>
        </dependency>
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.1.3</version>
        </dependency>
    </dependencies>
</dependencyManagement>

The common pom:

<dependencies>
    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
    </dependency>
    <dependency>
        <groupId>ch.qos.logback</groupId>
        <artifactId>logback-classic</artifactId>
    </dependency>
</dependencies>

and the a pom:

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                        <classpathPrefix>lib/</classpathPrefix>
                        <mainClass>bla.bla.Main</mainClass>
                    </manifest>
                </archive>
            </configuration>
        </plugin>
    </plugins>
</build>

<dependencies>
    <dependency>
        <groupId>bla.blu</groupId>
        <artifactId>common</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>
luboskrnac
  • 23,973
  • 10
  • 81
  • 92
Enrichman
  • 11,157
  • 11
  • 67
  • 101
  • 1
    Unless you build a uber-jar or you export the dependencies explicitly this will not work. My current preference is to use the magic [Spring Boot Maven plugin](http://docs.spring.io/spring-boot/docs/current/reference/html/build-tool-plugins-maven-plugin.html) which packages a jar with all its libraries without extracting them and packing them into one jar. – Boris the Spider Sep 02 '15 at 11:33

2 Answers2

2

By default Maven doesn't pack all the dependencies into your JAR. Therefore it is missing. It is expected from you to provide that jar on classpath when you run it.

Other option is to create so called fat jar. This is SO thread how to do that

BTW, I backup @Boris the Spider comment about Spring Boot. Go that way and a lot of problems will be solved for you out of the box.

EDIT - reaction to 1st comment:

Normally the common dependency should be deployed into the artifact reporitory (Nexus, Artifactory) and taken from there as any of your third party dependencies.

As you are not doing this, you need to have common dependency in local repository during the build A. So put Spring-boot-maven-plugin into A build and build fat jar with your multi-module project.

Community
  • 1
  • 1
luboskrnac
  • 23,973
  • 10
  • 81
  • 92
  • Pretty cool! I've added Spring Boot and everything seems to work "automagically". It seems that I'm not able anyway to build "each" module. If I try to "package" from the root pom it works. But if I try to package COMMON, and then to package A, it will raise a "Could not resolve dependencies" for COMMON. Is that ok? Do I have to build everything? – Enrichman Sep 02 '15 at 12:09
0

You need to include the COMMON module in the classpath when trying to run something from A.jar that depends on common functionality. Simply declaring it as a dependency will not include the common classes in the a.jar artifact (you would need to use the assembly maven plugin if you want that).

Besides, all the transitive dependencies brought by the common jar should also be listed in the classpath.

So you have to run it like java -cp common.jar:slf4j-a.b.c.jar:logback-x.y.jar[:...] -jar a.jar.

Maven itself and any IDE that knows maven will do that for you, so you won't have to do it by hand. For building a single jar to run in production, you should consider using the assembly or the shade plugin and build a "fat" (or uber-) jar that will include all the dependencies in a single file.

Costi Ciudatu
  • 37,042
  • 7
  • 56
  • 92
  • Unless `common.jar` is a uber-jar this will not work I don't think. – Boris the Spider Sep 02 '15 at 11:34
  • @BoristheSpider Obviously, the dependencies of `common.jar` itself should also be listed in the classpath. If that is your point, you're totally right. I was just trying to simplify things and assume there are *no transitive dependencies*, just to answer the specific question; but of course, other `NoClassDefFoundError`s are very likely to occur with the suggested command line. – Costi Ciudatu Sep 02 '15 at 11:39
  • But this question is _specifically about_ transitive dependencies (slf4j and logback) so the simplification has negated the usefulness of the answer... – Boris the Spider Sep 02 '15 at 11:40
  • @BoristheSpider Yeah, my bad! I think I was paying little attention to the actual missing class and focused on the "common" jar part. I'll try to edit the answer. Thanks! – Costi Ciudatu Sep 02 '15 at 11:43