warpedjavaguy

Observations of everyday programming phenomena

How I defeated the maven-release-plugin in a flat structured multi module project

Rules are made to be broken so that paradoxes can be created.

Maven is a handy tool and has a lot of available plugins. It adopts the convention over configuration philosophy and provides a standard build lifecycle out of the box. This makes it very easy to automate a build and release process without writing a single line of script. But there’s a catch! It only works if you do things the “maven way” and follow the maven rules.

Most projects are made up of one or more multiple smaller projects. In the maven world, such projects are called multi module projects. A multi module project has a parent project and one or more nested child projects known as modules. When you build the parent project the child projects are also built. The recommended maven way of structuring a multi module project is to mirror the parent child hierarchy using a nested project structure.

So maven recommends that you create a parent project that contains the child projects using a physical project structure like this:

workspace/parent/pom.xml
workspace/parent/child1/pom.xml
workspace/parent/child2/pom.xml

Modules are then declared in the parent POM like this:

<modules>
  <module>child1</module>
  <module>child2</module>
</modules>

This was good for maven but it was is not good for eclipse. The eclipse IDE does not support nested projects. This was clearly a problem! I wanted to import all my projects (parent and children) into eclipse but the nested structure made this impossible. So I decided to use a flat project structure instead and moved all my child projects out of the parent project.

Now my parent and child projects were organised in a flat physical structure like this:

workspace/parent/pom.xml
workspace/child1/pom.xml
workspace/child2/pom.xml

And I then redefined the maven modules in the parent POM like this:

<modules>
  <module>../child1</module>
  <module>../child2</module>
</modules>

Now I could import all my projects into eclipse. This worked well and life was good until I decided to use the maven release plugin to automate the release process. I learned the hard way that the release plugin only supports the nested project structure recommended by maven. Reverting back to the nested structure was not an option. I had broken a maven rule and was being punished for it! I needed a paradoxical solution that would support both the nested and flat structures at the same time. It was then that I realised that my parent POM was responsible for two things: POM inheritance and module composition. It served two “parental” roles. In one role it provided all the common properties, dependencies, plugins, and profiles to all children through inheritance and in the other it defined itself as the parent project of all child projects. In OO terms, this was akin to defining a superclass that contains a list of all its subclasess.

My parent POM had violated the single responsibility principle. So I decided to split it up into two separate parent POMs. I removed the modules declaration from the original POM in my parent project. This POM was now purely to be used for inheritance purposes only. All child POMs continued to reference this POM as the parent POM. Nothing changed there. I then created a new POM that inherited this modified POM and aggregated all the other child POMs. I placed this new top level POM file in the workspace root alongside all my existing projects. My flat project structure now had a top level POM file that defined all the child projects as modules.

The final project structure looked like this:

workspace/pom.xml
workspace/parent/pom.xml
workspace/child1/pom.xml
workspace/child2/pom.xml

The workspace/parent/pom.xml was inherited by all child POMs and also the top level workspace/pom.xml. It was the parent POM for inheritance purposes. The top level workspace/pom.xml aggregated all the child projects into one container project. It was the (root) parent POM for composition purposes. It defined the parent and child modules like this:

<parent>
  <groupId>?</groupId>
  <artifactId>?</artifactId>
  <version>?</version>
  <relativePath>parent/pom.xml</relativePath>
</parent>
<modules>
  <module>parent</module>
  <module>child1</module>
  <module>child2</module>
</modules>

Both the maven release plugin and the eclipse IDE were happy with this structure. It was flat enough for eclipse and hierarchical enough for the maven release plugin.

Note: After experiencing and resolving this problem first hand I later discovered that the issue has already been reported and discussed here and mentioned at the very bottom of the maven eclipse plugin page. But I still cannot find any mention of this limitation on the maven release plugin page itself. I suspect that this is a well known issue in the maven community. If anyone is aware of any fixes or better solutions, please let me know. Interestingly also the title of this issue suggests that the problem has been fixed but the actual contents therein state otherwise.

Sample POM snippets – Posted on 22 Aug 2011 by request

workspace/pom.xml (The top level root POM)

  <parent> 
    <groupId>maven.demo</groupId> 
    <artifactId>parent</artifactId> 
    <version>1.0.0-SNAPSHOT</version>
    <relativePath>parent/pom.xml</relativePath>
  </parent> 

  <groupId>maven.demo</groupId>
  <artifactId>root</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>

  <modules>
    <module>parent</module>
    <module>child1</module>
    <module>child2</module>
  </modules>

workspace/parent/pom.xml (The parent POM)

  <groupId>maven.demo</groupId>
  <artifactId>parent</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>pom</packaging>

workspace/child1/pom.xml (The child1 POM)

  <parent> 
    <groupId>maven.demo</groupId> 
    <artifactId>parent</artifactId> 
    <version>1.0.0-SNAPSHOT</version>
    <relativePath>../parent/pom.xml</relativePath>
  </parent> 

  <groupId>maven.demo</groupId>
  <artifactId>child1</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>jar</packaging>

workspace/child2/pom.xml (The child2 POM)

  <parent> 
    <groupId>maven.demo</groupId> 
    <artifactId>parent</artifactId> 
    <version>1.0.0-SNAPSHOT</version>
    <relativePath>../parent/pom.xml</relativePath>
  </parent> 

  <groupId>maven.demo</groupId>  
  <artifactId>child2</artifactId>
  <version>1.0.0-SNAPSHOT</version>
  <packaging>war</packaging>
About these ads

Written by warpedjavaguy

August 8, 2011 at 11:21 pm

Posted in automation, maven

26 Responses

Subscribe to comments with RSS.

  1. Hi,

    Thanks for the post. Btw do we run maven release plugin from parent-pom/pom.xml or root pom.xml?

    Thanks,

    Anonymous

    August 9, 2011 at 2:14 am

    • We run the maven release plugin on the root POM, since it is now the parent that defines all the project modules.

      warpedjavaguy

      August 9, 2011 at 7:09 am

  2. Hi,

    I am not still able to run maven release plugin successfully. Would you mind sharing your contact number or IM id so that we can have a chat in your morning time. that will be greatly appreciated.

    Anonymous

    August 10, 2011 at 4:09 am

    • How about you post your problem here and I’ll see if I can help you?

      warpedjavaguy

      August 10, 2011 at 1:08 pm

  3. Maven release pluton complains that it has snapshot dependencies on parent pom. As you said root pom itself inherits from parent/pom. When I run nvm release:prepare on root pom, it says it has snapshot dependency on parent project and it keeps complaining that. Did you come across? I have made sure that both root pom and parent/pom are of packaging of pom. Please advise.

    Ambati

    August 10, 2011 at 3:07 pm

    • Does your root POM reference the parent POM in the parent element?

      If so, does your parent element contain a relativePath element that points to the parent POM?

      (fyi – I’ve updated the post to make this important detail explicit)

      warpedjavaguy

      August 10, 2011 at 4:01 pm

  4. While eclipse normally does not handle nested projects well, the m2e plugin does allow it to import maven hierarchies directly from an scm. I used to fight this issue in a similar way, but find with m2e, I no longer have to.

    Ian Robertson

    August 14, 2011 at 4:11 pm

    • Thanks Ian, but in my particular case I need the flexibility to be able to build my parent project without also building all the child modules. When I had the nested structure I managed this by declaring the modules in a profile that I only activated via the -P switch when I wanted to build everything (parent and children). How would m2e handle this given that the parent had no child modules unless the profile was active?

      With the flat parent and root POM structure all my problems disappeared.

      warpedjavaguy

      August 14, 2011 at 10:08 pm

  5. I’m not sure I understand your use case. The children extend from the parent, so a change to the parent would normally mean you would want to rebuild of the children as well.

    Also, m2e normally doesn’t trigger maven builds of your projects; rather, it attempts to configure eclipse based off the contents of your poms to build in the same way maven would.

    That said, I have had situations where I wanted to perform a maven release:prepare/perform of my parent module without also releasing all the child modules. Normally, this suggests that the children of the parent do not belong under a common aggregator, in which case the “red-neck” layout where the (extension) parent project is a (directory) sibling of its children makes more sense. The aggregator project can then be used as a convenience to release all modules at once, but it also allows for individual releases as well.

    Ian Robertson

    August 15, 2011 at 12:11 pm

    • My use case is that I have a parallel development environment where individual developers work on individual child modules. If a developer makes a change in the parent, then that developer need only rebuild the parent and the child module that they are working on. Otherwise they would have to rebuild all modules (and have all modules checked out). That would be very impractical.

      So there is no need for my parent POM to be an aggregating POM since there is no need for all developers to build all modules. My top level root POM serves that purpose and individual developers can be oblivious to the fact.

      As far as releasing goes, I run the maven release plugin over the top level root (aggregator) POM which in turn runs over the parent POM and all child POMs. I can also release the parent without releasing the children by running the release plugin over the parent POM only, since it is not an aggregator. And in the same way, I can also release individual child modules too.

      The more I think about it, the more I think that this is the right way. I do not wish to have to depend on m2e to have this capability.

      warpedjavaguy

      August 15, 2011 at 1:38 pm

  6. Nice one. I’ve got a mild maven allergy (from projects we have both worked on) but this will make things easier.

    oopsnullpointer

    August 15, 2011 at 2:10 pm

    • Yeah I know exactly which project you’re talking about. Maven sure has come a long way since the 1.0 days.

      warpedjavaguy

      August 15, 2011 at 8:43 pm

  7. Would it be possible to post some example poms ? This looks like a good idea, but I’m having a hard time getting my artifact and group ids right.

    thanks,
    Peter

    compneuroscientist

    August 21, 2011 at 4:43 am

    • Hi Peter,

      I’ve posted some POM snippets to the bottom of the post. Hope that helps.

      warpedjavaguy

      August 22, 2011 at 12:03 am

  8. [...] Rules are made to be broken so that paradoxes can be created. Maven is a handy tool and has a lot of available plugins. It adopts the convention over configuration philosophy and provides a standard build lifecycle out of the box. This makes it very easy to automate a build and release process without writing a single line of script. But there's a catch! It only works if you do things the "maven way" and follow the maven rules. Most projects are … Read More [...]

  9. Hvala na ovom postu. Imam sličan setup projekata i slične probleme s kojima sam se suočio. Pokušaću sa ovom organizacijom. Dalje na engleskom:

    The tagging part of release plugin is not working as I am expecting it to work. In my project setup (where parent pom is both aggregator and parent and sibling, just like your situation from the start of the article), when I perfrom release:prepare on that parent pom he tags entire repo root in tag repo folder, which is plain wrong. There are other projects which should not be aggregated with this particular group, not to mention that tag folder itself is sibiling to all there project folders.

    Is it possible in any way to make release plugin tag each of the modules it aggregates?

    Rade Martinović

    December 22, 2011 at 2:39 am

    • I get a single tag of the top level of the repository. I don’t know if there is any way to have separate tags in the repository for each of the sub-projects.

      Anonymous

      December 22, 2011 at 4:08 am

    • Unfortunately no. I learnt the hard way too that the release plugin just does not support it.

      The problem is that it only tags the project that you invoke the plugin on. Unless all child modules are nested, there is no way to successfully tag them all. You would have to individually release each project/module separately.

      warpedjavaguy

      December 22, 2011 at 8:14 am

  10. Another thing, if all the projects would share the same version, you don’t need to state it in every child pom since the version, as well as groupId and some other things, are inherited from parent pom.

    Rade Martinović

    December 22, 2011 at 3:07 am

  11. I have a similar set up with a slight difference. I have an intermediate (non-aggregator) parent that sits between a few of my projects and the overall (non-aggregator) parent.
    I.e. the parent-child relationships are like this:
    parent
    |–proj1
    |–proj2
    |–subparent
    |–subproj1
    |–subproj2
    Subparent contains some commmon config used only by subprojX.
    I then have a separate aggregator pom that I run the release:prepare goal against. This works fine if I leave out the sub projects, but if I include one or more subprojects as modules of the aggregator then the release fails, with maven saying it can’t find the subparent pom for the release version in the maven repository (of course it can’t – it won’t be deployed to the repo until I run release:perform). It doesn’t try to download parent, so why would it try to download subparent?
    Has anyone else encountered this problem? Any help greatly appreciated.

    Anonymous

    March 9, 2012 at 2:33 am

    • Have you specified the relativePath in the parent element of the sub project pom?

      For example, in the subprojX pom:

      <parent>
      . . .
      <relativePath>../subparent/pom.xml</relativePath>
      </parent>

      warpedjavaguy

      March 9, 2012 at 8:12 am

      • Thanks for the fast response. I hadn’t set my relative paths; that sounds like it should solve the problem, I’ll give it a try. I was previously avoiding relative paths because they will be different on my CI server (it checks out each project in it’s own individual workspace), but I should be able to get round that with profiles.

        Anonymous

        March 9, 2012 at 9:51 pm

  12. WJG, it sounds like you want a loose collection of projects, just possibly a use case that the Maven people have not considered, or possibly considered but didn’t feel was important. I”m trying to backtrack to your requirement but not getting it tonight.

    The solution that Ian Robertson mentioned above works for me – check out the Maven superproject from SVN using the M2E connector for SCM systems. That works some magic to create little Eclipse projects and keeps JDT happy. I’m ok with that for now.

    For what it’s worth here’s my writeup. http://www.maultech.com/chrislott/blog/20120618_eclipse_maven.html

    Chris L

    June 19, 2012 at 11:12 am

    • Hi Chris,

      M2E certainly does solve the multi module project problem in eclipse and SCM, but it does not solve the ‘flat structured multi-module’ maven-release-plugin problem which is independent of eclipse and is the primary focus of this post.

      warpedjavaguy

      June 29, 2012 at 1:02 am

  13. Any tips on how to solve eclipse project dependency as well?
    Eg: child2 project depends on child1 project.
    Using maven, my child2 depends on the child1 artifcat in the m2 repository.
    If I add child1 into project->properties->java build path->projects I can get rid of the repository dependency.
    But then if I execute maven->update project configuration it toasts my eclipse project dependency.

    cgullcharliemattywix

    June 27, 2012 at 2:24 am

    • Hi,

      I’ve never had that problem. When declared as a maven dependency in the child2 pom, the child1 project will appear as a project dependency under the Maven Dependencies library in the classpath of the child2 project in eclipse. M2E does this for you. There is no need to manually add the project dependency to the classpath. Running maven->update project configuration should regenerate the classpath correctly as I just described. The regeneration is what toasts your manual entry. But that doesn’t matter if it works correctly and generates the child2 project dependency again as I just described.

      Maybe try reinstalling the M2E plugin from the eclipse marketplace. It could be that your plugin is not compatible with your eclipse version. Otherwise check that you have configured the child1 dependency correctly in your child2 pom file.

      warpedjavaguy

      June 29, 2012 at 1:54 am


Comments are closed.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: