mavenmaven-2

How do I setup maven sub-projects with inter-dependencies?


I'm having a hard time setting up a set of related maven projects with interdependencies between them.

Here's my simplified case:

pom.xml     -- parent pom
\base\
    pom.xml
    src\main\java\Base.java     -- this is just a simple class
\derived\
    pom.xml
    src\main\java\Derived.java  -- this is a simple class that needs Base class to compile successfully, and has a main function

My goals are:

  1. be able to compile, on my machine, all projects:
    • i.e. mnv clean compile is successful in \
  2. be able to compile, on my machine, just one project:
    • i.e. mnv clean compile is successful in \base\ and \derived\ (though this may not work by design: Inter Project Dependencies in Maven)
      [edit: found the answer for this one: base needs to be published locally before derived in compiled: i.e. in \base, do mvn clean compile install instead of doing just doing mvn clean compile. Once this is done, doing mvn clean compile in \derived works fine. Still, it would be great to do this without touching global state, i.e. without having to install base -- so if anyone knows a way of achieving this, please add it as an answer]
  3. be able to run derived project on my machine (mvn exec:run or equivalent) direcly from the source tree:
    • i.e. mvn exec:run in \derived\ should compile (if needed) and run the derived.jar
  4. the "shared component" use case: push the base artifact to the shared repository, where other people can consume it as a maven dependency (i.e. compile time dependency):
    • i.e. mvn clean compile ??? will push this to the shared repository specified in ~/.m2/config.xml
  5. the "image directory" use case: push the derived artifact and its depedencies to a local directory, where it can be run via "java -jar ..." or it can exposed as an ftp/http share for other people to get it. I.e., use cases like those:
    • mvn clean compile ??? will push derived.jar and dependencies (like base.jar) to ~/.m2/maven.repo/.../derived or equivalent, and then I can cd ~/.m2/maven.repo/.../derived and run java -jar derived.jar to run it.
    • mvn clean compile ??? will push base.jar to ~/.m2/maven.repo/.../base (or derived.jar to its corresponding dir), which is already exposed as a download point via local web or ftp server.

How do I do the goals above?

Here's the relevant section from parent pom:

...
<modelVersion>4.0.0</modelVersion>
<groupId>com.foo</groupId>
<artifactId>parentpom</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>

<name>parentpom</name>

<modules>
    <module>base</module>
    <module>derived</module>
</modules>
...

Here's the relevant section from base pom.xml:

...
<parent>
    <groupId>com.foo</groupId>
    <artifactId>parentpom</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>
<groupId>com.foo.base</groupId>
<artifactId>base</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>base</name>
...

Here's the relevant section from derived pom.xml:

...
<parent>
    <groupId>com.foo</groupId>
    <artifactId>parentpom</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</parent>

<modelVersion>4.0.0</modelVersion>
<groupId>com.foo.derived</groupId>
<artifactId>derived</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>derived</name>

<dependencies>
    <dependency>
        <groupId>com.foo</groupId>
        <artifactId>base</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </dependency>
</dependencies>

...

Thank you in advance for your help.


Solution

  • Your parent pom looks good but your module/derived poms don't: There are several issues which will produce some problems etc.

    First you are using different groupId's for the derived/base module. If you have a multi-module build you shouldn't do this. Furthermore, the version of derived/base is inherited by the parent and shouldn't be set explicit which means you should have something like the following:

    <modelVersion>...</...>
    ...
    <parent>
        <groupId>com.foo</groupId>
        <artifactId>parentpom</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    
    <artifactId>base</artifactId>
    <packaging>jar</packaging>
    
    <name>base</name>
    

    The groupId is usually inherited. But sometimes you have a larger number of modules that may be sub-modules of sub-modules which results in a structure like this:

    +--- parent (pom.xml)
           +--- mod1 (pom.xml)
                  +--- mod11 (pom.xml)
                  +--- mod12 (pom.xml)
           +--- mod2 (pom.xml)
    

    In Such cases, the mod1 itself is a a pom packaging module:

    <modelVersion>...</...>
    ...
    <groupId>com.company.project<groupId>  
    <artifactId>parent</artifactId>
    <packaging>pom</packaging>
    <modules>
      <module>mod1</module>
      <module>mod2</module>
    </modules>
    

    mod1:

    <parent>
        <groupId>com.company.project<groupId>  
        <artifactId>parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    
    <groupId>com.company.project.mod1</groupId>
    <artifactId>mod1</artifactId>
    <packaging>pom</packaging>
    
    <modules>
      <module>mod11</module>
      <module>mod12</module>
    </modules>
    

    mod11:

    <parent>
        <groupId>com.company.project.mod1</groupId>
        <artifactId>mod1</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    
    <artifactId>mod11</artifactId>
    <packaging>pom</packaging>
    
    <modules>
      <module>mod11</module>
      <module>mod12</module>
    </modules>
    

    If you need to make a dependency between modules the solution is:

    <parent>
        <groupId>com.company.project.mod1</groupId>
        <artifactId>mod1</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    
    <artifactId>mod11</artifactId>
    <packaging>jar</packaging>
    
    <dependencies>
      <dependency>
        <!-- This shouldn't be a dep which is packaging: pom -->
        <groupId>${project.groupId}</groupId>
        <artifactId>mod2</artifactId>
        <version>${project.version}</version>
      </dependency>
    
    </dependencies>    
    

    To compile the whole project with all sub modules in one just go to parent folder and:

    mvn clean package

    will compile etc. If you like to compile only a single project you can do this by (from parent):

    mvn -pl mod1
    

    which will work only if you have installed the whole project once before. Another solution is:

    mvn -am -pl mod1
    

    To make the artifacts acessible for others the simplest solution is to install a repository manager and do:

    mvn deploy

    All others can use and artifact as a SNAPSHOT dependency.