13

I appologize for the length of this post, but I had trouble making it more concise without presenting the picture. I've recently inherited the job of build-master for a maven 3.0 multi-module project. The problem is that the structure of the project/modules is a disaster. From the way things are currently stored in Source Control (we use RTC) to the pom structure of the modules, I'm tearing my hair out trying to get a full build cycle completed each time.

As a project hierarchy goes, all the modules are stored "flat"; ie: everything is at the same level. I have a parent pom, and all modules depend on the parent. However, the parent is at the same level as all my other modules.

Ex:

c:\dev\MyEarProject
 + parent-pom
   - pom.xml
 + module1
   - pom.xml (depends on parent-pom)
   - src
   -   main
   -     ...
 + module2
   - pom.xml (depends on parent-pom)
   - src
   -   main
   -     ...
 + module3
   - pom.xml (depends on parent-pom)
   - src
   -   main
   -     ...

The parent pom defines all the modules required to build the project, as well as a bunch of properties for artifact version numbers used throughout the different submodules:

<modules>
  <module>../module1</module>
  <module>../module2</module>
  <module>../module3</module>
</modules>

<properties>
    <org.springframework.version>3.0.5.RELEASE</org.springframework.version>
    <slf4j.version>1.6.4</slf4j.version>
    <repositoryAddress>${snapshots.repo.url}</repositoryAddress>
    <my.hibernate-module.dao.impl>1.2.3</my.hibernate-module.dao.impl>
    <my.hibernate-module.dao.api>1.2.3</my.hibernate-module.dao.api>
</properties>

Each module's pom, in turn, depends on the parent pom via the pom's artifact number:

<parent>
    <groupId>com.cws.cs.lendingsimulationservice</groupId>
    <artifactId>parent-pom</artifactId>
    <version>1.0.6</version>
</parent>

To make things even more confusing, the actual artifact name may, or may not (depending on the module), match the module path. For example, module1 may be located in path c:\dev\MyEarProject\module1 but have artifact name hibernate-module. However, due to the way it is stored in RTC, the directory is called module1 when it is checked-out.

The easiest way to build everything, of course, is to go into c:\dev\MyEarProject\parent-pom\ and run mvn clean deploy. This works fine when in SNAPSHOT mode as the SNAPSHOT repo allows for multiple deployments of the same artifact version. But in release mode, this fails.

This structure is causing 2 problems for me.

  1. Everytime I need to make a version change to a property in the parent, I have to update the parent-pom version number, and all the child modules parent pom's version, and all the child modules version themselves (since the parent changed).
  2. Whenever I need to deploy a release cycle, mvn will throw an error if one of the modules has not changed since the last cycle and consequently cannot be redeployed to the same repo (the repo does not allow overwriting existing artifacts)

So I'm looking for the best way to restructure this project to avoid these problems. For the parent pom, I know I can use a relative path to point to the parent instead. However, given the "flat" structure of the modules, is this a recommended approach (ie: the parent pom relative path would be ../parent-pom/pom.xml - seems a little odd to me)? Additionally, given that the versioning control of the parent is independent of the modules, would using a relative path not just open the door to additional confusion (ie: there would be no way to know which version of the parent pom is associated with which version of the submodule).

Secondly, how can I build the entire ear without encountering the deploy errors I am having? Since the artifact already exists in the repo, I don't need to rebuild and redeploy it. I tried using --projects but with the number of modules involved, it gets extremely difficult to manage.

Eric B.
  • 23,425
  • 50
  • 169
  • 316

3 Answers3

13

The first thing I really recommend is to restructure the projects folders ...which means to have the projects folder represent the structure which means NOT flatten the structure.

  +-- parent-pom (pom.xml)
       +--- module1 (pom.xml)
       +--- module2 (pom.xml)
       +--- module3 (pom.xml)

As a result of that the modules section your parent will be simplified like this:

<modules>
  <module>module1</module>
  <module>module2</module>
  <module>module3</module>
</modules>

Furthermore the parent entries in your modules can be simplified as well like this:

<parent>
  <groupId>com.cws.cs.lendingsimulationservice</groupId>
  <artifactId>parent-pom</artifactId>
  <version>1.0.6</version>
</parent>

...which brings me to the next point:

If all your current project define their parent as above this is simply wrong, cause will try to find the parent within the repository and not in a upper level folder. In other words this is causing of much of your problems with releasing etc.

If we would fix this problem it has to look like this which I can't recommend:

<parent>
  <groupId>com.cws.cs.lendingsimulationservice</groupId>
  <artifactId>parent-pom</artifactId>
  <version>1.0.6</version>
  <relativePath>../parent-pom/pom.xml</relativePath>
</parent>

An other thing which I observe is that you don't use SNAPTSHOT's which will be replaced by the release plugin during the release phase. And in relationship to that it will automatically change all versions in the appropriate parents etc.

In ideal case your modules should look like this:

<parent>
  <groupId>com.cws.cs.lendingsimulationservice</groupId>
  <artifactId>parent-pom</artifactId>
  <version>1.0.6</version>
</parent>

<artifactId>module-1</artifactId>
<!-- No Version or groupId -->

Cause all modules will inherit the version and groupId from their parent. Sometimes it's useful or needed to change a modules groupId but it's an exception.

On thing I reread is about the separate versioning of the parent. This simply does not make sense, cause it's the parent of their modules so put it into the same structure and of course the same VCS.

If you want to make some configuration/plugins versions, dependencies which should be used for other projects as well than make a separate corporate pom.xml which is a separate project and will be separately released etc.

After you finished your structure changes you can simply go into the parent-pom directory and do mvn clean package or mvn release:prepare release:perform from that folder and everything will simpler.

Lii
  • 11,553
  • 8
  • 64
  • 88
khmarbaise
  • 92,914
  • 28
  • 189
  • 235
  • 2
    Unfortunately, restructuring to have a tree structure is not an option given the way that RTC and the components/modules have been set up for this project. So I have to live with the current strcuture/layout and make the best out of the situation. Putting the version at the parent level to be inherited by all the child modules essentially means rebuilding/retesting/redeploying all modules (which can be expensive) even if there was only a change in a single module. Essentially, I lose the ability to independently version one module. Although I agree; it would resolve my deploy problem. – Eric B. May 04 '12 at 18:42
  • 1
    If you like to continue fighting against maven it's your turn but i recommend to change the structure cause it will make your life easier. But if the idea about deploying every module separately does not make sense. Either they are related are the are not. If they are not than you should make them really separated and don't use a multi-module build. – khmarbaise May 04 '12 at 20:58
  • 1
    I am having trouble understanding how to do this with the version number is defined in the parent only. In my current setup, I can change the interface in a module without affecting the rest of the build until it is time to reintegrate the new version. With the version only in the parent, changing the interface of a module will break rest of the build since all modules will rely on the source tree and not a pre-deployed binary. How do I avoid this problem? I still need to individually version each module, do I not? – Eric B. May 10 '12 at 19:37
  • 1
    If you really need separate versions which means in other word separate releases you need to have separate modules and not a multi-module build. – khmarbaise May 11 '12 at 07:23
  • If we would fix this problem it has to look like this which i can't recommend - so what DO you recommend? – Kalpesh Soni May 18 '16 at 16:14
2

If you're publishing your POM, you'll have to release any updates but you don't need to modify POM versions by hand - you can update versions automatically using the versions plugin or the release plugin. I tend to prefer the release plugin as it'll commit to SCM for you too.

mvn versions:set 

http://mojo.codehaus.org/versions-maven-plugin/

mvn release:prepare release:perform

http://maven.apache.org/plugins/maven-release-plugin/

Your repository manager may also allow overwriting an existing version but it's better practice to just release a new version.

I tend to prefer the flat module structure as it allows use of the parent folder to store common files e.g. checkstyle configuration. I also find it useful to share the group id across modules then name the module directory the same as the artifactId.

cvanes
  • 31
  • 3
  • I haven't used the release plugin in quite a while, but I don't understand how it will resolve my problem when I need to progress an individual module to a new version. If module3 needs updating (goes from 1.1 to 1.2-SNAPSHOT), and module1 depends on module3, then I update module3 pom, parent-pom property, parent-pom version, module3 parent version and finally module1 parent-version, and module1 version. How can the release plugin accomplish all that for me? Note that module2 is not required to change anything at this point. – Eric B. May 04 '12 at 18:48
  • The versions plugin should be able to update the dependencies for you then you can just commit and deploy the new artifacts. – cvanes May 04 '12 at 21:44
2

You are presenting contradicting requirements. You want to restructure your project but can't move things around. You want to simplify your deployment and release cycles but do not want to use a single version.

Given that changes in one module will inevitably affect all of the dependent modules, I would use a simple version'ing scheme where all the sub-modules inherit their parent's version. maven release:prepare and release cycles become simple. Use a release notes to track your changes and justify skipping unnecessary testing of unchanged modules (changes to a version do not change the build/binary output of the build process so you can use that as your primary argument).

Good luck with your project.

Pierre
  • 1,329
  • 2
  • 12
  • 21