11

We are attempting to move several multi-module applications to maven, and having some problems.

Each module is stored independently in cvs. We have manifest files for each application, which list the modules required for that application (and optionally the version). Not all modules are in maven form.

So application 'customer_care' has the following manifest:

 <manifest>
 <module id="MY_api"/>
 <module id="custcare_webapp"/>
 </manifest>

Similarly, the application 'core batch' has a manifest like this:

 <manifest>
 <module id="MY_api"/>
 <module id="core"/>
 <module id="batch"/><!--NB this is a non-maven module -->
 </manifest>

I have started 'mavenising' our code, so the MY_api project has a pom.xml with dependencies defined, including one on another internal code module 'central_config'. I have specified version RELEASE.

The problem

This all works fine, until I need to create a frozen manifest. I can specify a version for each module:

 <manifest>
 <module id="MY_api" version="0.123.0"/>
 <module id="core" version="0.456.0"/>
 <module id="batch" version="0.789.0"/><!--NB this is a non-maven module -->
 </manifest>

BUT this build is not reproducible, because the version of the 'centralconfig' dependency in MY_api is 'RELEASE'. So if someone releases a new version of 'centralconfig', then next time we build this frozen manifest, it's different.

So why don't we use hard-coded versions of dependencies like central-config? Because then, we would have to update perhaps 10 or 20 pom files every time someone updates centralconfig to a new version. Everything which depends on central config, and everything which depends on that, would need its pom.xml updating and to be re-released. Not only is this lots of work, I don't know how I could programmatically and reliably identify every module which declares a dependency on central config.

A possible solution?

Could I define 'centralconfig.version' in one place, and then refer to it in all my modules? If so, where should I do this? I don't know much about parent poms but I feel they might provide a solution.

Update

It seems that using a parent pom is the way to go. But according to this question: Can maven projects have multiple parents? , it's not possible for a maven child project to have multiple parents.

So then how can the MY_api module be a child of both custcare_webapp and core_batch?

Update

I've concluded that maven doesn't meet my requirements, and we've gone back to using our 12-year old home-grown solution build using ant and CVS.

Community
  • 1
  • 1
mdarwin
  • 1,684
  • 7
  • 28
  • 72
  • I have always had problems with multimodule projects in Maven. Finally, I switched to Gradle. I actually have a post on SO ([http://stackoverflow.com/questions/17709707/gradle-multi-project-distribution/23701271#23701271]) that might help you get started on multi-module projects using that tool. The other answers there are helpful too. Not trying to toot my horn or Gradle's, just hoping this might help. Understood if Gradle is not an option. – sparc_spread May 15 '15 at 11:18
  • @sparc_spread, thanks for the tip. I'm not averse to using gradle, but there's a bit of a learning curve, and having put lots of time and effort into getting maven working, it would be a shame to throw it all away and start again with gradle. I feel like there is a solution with maven, but it's just out of reach! – mdarwin May 15 '15 at 11:28
  • No problem - agreed on all. Hopefully you'll have some answers soon. – sparc_spread May 15 '15 at 11:31
  • 1
    I don't really understand the problem description to be honest. From what I can gather, it seems to me like using a proper maven multi-module setup, which indeed requires the usage of a parent pom where you do the dependencyManagement, is all that is necessary. But I assume I'm just missing the point. – Gimby May 15 '15 at 11:59
  • @Gimby could you elaborate on your idea? What I don't understand is this: if I specify a version of MY_api in the parent poms for both customer care and core batch, how will that get updated in both poms each time I release a new version of MY_api? – mdarwin May 15 '15 at 13:07
  • (Tip) Install non-maven libraries in a single separate folder: >groupId>thirdparty.batch. Then the repository folder `thirdparty` can be put und managed version control. – Joop Eggen May 15 '15 at 14:10
  • @mdarwin The point is that I don't get your idea. If you do proper Maven release management, Maven manages the updating of versions for you. I have no idea from your description up to what point you are using Maven or what you have learned so far; I can gather that you don't know yet how parent modules work, and as such I think that lack of understanding is basically what causes you to not understand the rest either. – Gimby May 18 '15 at 12:20
  • I think I understand what a parent pom is, but I don't know where I should put it. There is no common root directory - these modules exist in CVS independently. Would I have one parent pom for the dev branch of each, and then one per release, per app? – mdarwin May 19 '15 at 13:53

2 Answers2

3

One other option that is often better than a parent-structure for managing versions is to import dependencies.

To illustrate how this works you create one project that only contain a pom specifying the versions to use for all your modules:

<project>
 <modelVersion>4.0.0</modelVersion>
 <groupId>test</groupId>
 <artifactId>module-versions</artifactId>
 <packaging>pom</packaging>
 <version>1.0</version>
 <dependencyManagement>
   <dependencies>
     <dependency>
       <groupId>test</groupId>
       <artifactId>a</artifactId>
       <version>1.1</version>
     </dependency>
     <dependency>
       <groupId>foo</groupId>
       <artifactId>bar</artifactId>
       <version>2</version>
     </dependency>
   </dependencies>
 </dependencyManagement>
</project>

Then, in all your projects that need to have dependencies to anyhing that you have hard coded versions for you import this project in the following manner:

  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>test</groupId>
        <artifactId>module-versions</artifactId>
        <version>1.0</version>
        <type>pom</type>
        <scope>import</scope>
      </dependency>
    </dependencies>
  </dependencyManagement>

This way you only have to change the module-versions project anytime you release a new version of anything you have a dependency to.

This way you can have multiple "module-versions"-projects to split things up a bit.

Of course, you still have the problem that all project's that want to use the new version must also be released in turn, but that is the cost of using released dependencies in Maven.

K Erlandsson
  • 13,408
  • 6
  • 51
  • 67
  • This actually could work. So every module, instead of having a parent or grand-parent to define module versions, would import effectively a 'latest module versions' list. Ok next question - where do I actually put this project? And can I be sure that maven will update it every time? I've had problems in the past with maven not getting version metadata properly from the repo – mdarwin Jun 05 '15 at 13:52
  • but then when I increment the version number of a module, I have to also update this pom too. Do I also need to increment the version of this pom? Because if so, then I would have to update the pom.xml of all the other modules which depend on it ie all of them... – mdarwin Jun 05 '15 at 13:54
  • @mdarwin yes you would, but you cant escape that when using release dependencies. You can make it easier with tools such as the maven versions plugin. – K Erlandsson Jun 05 '15 at 14:35
1

I think you do need a parent POM. That is a top-level pom.xml that is solely a POM module and has no associated code. You build the entire project by running the mvn command on this pom.xml.

The POM should be in the directory above all the module directories. That is, each of your modules will be in a subdirectory of the directory that holds the master pom.xml

This POM's <packaging> type will be pom. That means it's a POM-only project with no code of its own. It will also have a <modules> tag containing one <module> element for each of your modules. That way, when you run the mvn command, Maven will know to build each of these modules as well. A decent sample parent POM is here.

Set all your dependencies in this POM, using the standard <dependencies> tag. The module POMs will inherit them. (Update: see comments below, is definitely worth exploring the <dependencyManagement> tag instead for the parent POM.)

Finally, each of your module POMs should refer back to the master POM. That way, if you run mvn in one of the module directories (i.e you are just building one module), it will look to the parent for dependencies. You do this with a <parent> tag, which will hold the <groupid> and <artifactid> of the master POM. A good example of a <parent> tag, as well as a good overall review of multi-module projects, is here.

sparc_spread
  • 10,643
  • 11
  • 45
  • 59
  • 1
    To be exact: a "parent" pom in order to hold the `...` dependencies. In the module poms then no `` should appear in a ``. The maven dependency plugin might provide some tooling, like [this](https://maven.apache.org/plugins/maven-dependency-plugin/examples/resolving-conflicts-using-the-dependency-tree.html). – Joop Eggen May 15 '15 at 14:06
  • 1
    Good point. I have gotten away without using `` in the parent POM, but it definitely has some advantages. [Here is a good explanation](http://stackoverflow.com/questions/2619598/differences-between-dependencymanagement-and-dependencies-in-maven) of ``. – sparc_spread May 15 '15 at 14:09
  • thanks for the detailed answer. What I don't understand is this: if I put (move) each module to be a child directory of a single directory, then do the same for the other app, I have forked my code, since MY_api appears in both applications. So we would have two, now independent, copies of MY_api. No? – mdarwin May 19 '15 at 13:57
  • Make MY_api a separate project. On a developer box, running `mvn install` on MY_api will put it into the local repository, where it will be found by other projects on the same box. If you want a project to only use a released MY_api, you need a central repository. The most commonly used one is [Artifactory](http://www.jfrog.com/artifactory/). A good overview of repository managers is [here](https://maven.apache.org/repository-management.html). A comparison of repository managers is [here](http://docs.codehaus.org/display/MAVENUSER/Maven+Repository+Manager+Feature+Matrix). – sparc_spread May 20 '15 at 11:30
  • Thanks. MY_api is a separate project - as a module it's independent. And we already have a maven repo. But how can MY_api be a child of 2 different parents (one for each app), if I need to specify the parent pom location in the pom.xml for MY_api? – mdarwin May 20 '15 at 13:46
  • I think I see what you mean - that is, MY_api wants to use the same centralized list of dependencies as the other separate applications that use MY_api. In that case, I would make the parent POM a fully versioned separate project that goes into the repo (call it "POM-central". Then, each multi-module itself has a parent POM, whose parent POM-central. – sparc_spread May 20 '15 at 14:05
  • Ah ok, so you mean have a 'grand-parent' POM where we do dependencyManagement. And then each app is represented by a child POM. But then what do I put in MY_api/pom.xml as the parent pom? the grand-parent? MY_api is still a child (logically) of *both* applications – mdarwin May 22 '15 at 15:19
  • MY_api has its own parent POM. That parent POM has the grand-parent POM as its parent. – sparc_spread May 22 '15 at 16:00
  • MY_api has 2 logical parents - customer_care and core_batch. Which one do I use as the parent pom and why? – mdarwin May 29 '15 at 10:35