Maven multi module project setup with various, different dependencies - maven

Let me just say this first: I am brand new to Maven. That said I searched around but have not found answers to the following question. I found answers to similar questions, just not this scenario. Or maybe I just misunderstood the answers and this can be solved simply wiht a multi module setup.
I'd have the following dependency hierarchy:
database
| |
| +---core
| | |
| | +---business
| | |
| | +------App1
| | |
| | +------App2
| |
| +---------------App3
|
+----------------------App4
I'd like to make it work so that changes only result in new releases of any "upstream" modules/Apps. Is this indeed a simple case of multi-module maven setup or do I need to do something else?

If you want that releasing one component produce a new release of each project, just use maven-release-plugin: http://maven.apache.org/maven-release/maven-release-plugin/.
Documentation
As per doc, this would :
Check that there are no uncommitted changes in the sources
Check that there are no SNAPSHOT dependencies
Change the version in the POMs from x-SNAPSHOT to a new version (you will be prompted for the versions to use)
Transform the SCM information in the POM to include the final destination of the tag
Run the project tests against the modified POMs to confirm everything is in working order
Commit the modified POMs
Tag the code in the SCM with a version name (this will be prompted for)
Bump the version in the POMs to a new value y-SNAPSHOT (these values will also be prompted for)
Commit the modified POMs
Because of the maven multi module structure, they are linked together, and each project would be bumped into a new release.
In a few words, this will :
move version 1.0-SNAPSHOT --> 1.1-SNAPSHOT
tag 1.0
generate 1.0.jar (ou war or anything else)
Plugin usage
Assuming that SCM is correctly defined, and repository and distribution management configured, just add these lines
<project>
[...]
<build>
[...]
<plugins>
[...]
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<version>2.4.2</version>
<!-- optional, basic format is ${project.artifactId}-${project.version}.${project.type}-->
<configuration>
<tagNameFormat>v#{project.version}</tagNameFormat>
</configuration>
</plugin>
[...]
</plugins>
[...]
</build>
[...]
</project>
And call
mvn release:prepare
mvn release:perform
Inheritance vs Dependency
You may consider the two differents Maven approches :
inheritance, that means parent and multi/sub modules
aggregation, in other words : use of dependencies
In a multi-maven project, all your modules, including parent, share the same lifecycle. Releasing one imply releasing all, and so, releasing just one is a non sense.
In your case, you can't modify app 1 to 3 whithout impacting app 4.
If App 4 depends App 1, obviously App 1 can't depends on App 4 (circular references are not allowed).
So, you want to isolate App4 and App1 to 3 lifecycles, you should not use multi-modules, but just share a parent project, or a hierachy of pom like corporate > main project > sub project (not submodule).
After that, just declare a dependency between App 4 and App 1. (... into app4 pom.xml)
Just another thought : the name of your projects and submodules sounds strange. "Classical" hierarchy is often (considering multi business object domain for a large project):
Main Project (sometimes EAR) --> POM
|-- Business Object / DAO --> POM
| |-- Domain 1 --> JAR
| `-- Domain 2 --> JAR
|-- Core (depends on BO) --> JAR
`-- IHM / Web App (depends on core) --> WAR
So, database is rarely at the top of hierarchy.

Related

How to access elements from settings.xml in pom.xml with Maven?

According to https://maven.apache.org/pom.html#properties it should be possible to use ${settings.x} to access the element <settings><x>value</x></settings> from the settings.xml file in the pom.xml file.
However, when I try something like
<profiles>
<profile>
<activation>
<file>
<exists>${settings.localRepository}/path/to/file</exists>
</file>
</activation>
</profile>
</profiles>
in my pom.xml it isn't replaced in the effective pom.xml. When I replace ${settings.localRepository} with ${user.home}/.m2/repository it works fine but that's not what I want. Is there anything I can do to fix that? (Tested with Apache Maven 3.6.0.)
Background information:
I have a dependency that isn't present in an online maven repository and I can't change that. It must be compiled by users and can be installed to the local repository. Instead of doing this manually, I'm trying to do this automatically in my pom.xml. For this I have to ignore the dependency if it's not present in the local repository. Hence the profile that checks if the file is present in the local repository. Without the profile, maven wouldn't even start the life cycle because the dependency can't be resolved. Of course the project won't compile the first time the pom.xml is executed. But all dependencies are automatically installed and the project will compile in a second pass. I know that this isn't a clean solution but I think it's better than telling users to compile and install dependency xy manually before this project can be compiled. I also include a build script that first runs mvn clean initialize to install the dependencies and then mvn clean compile.
Put the source of the external dependency in an own project like:
+- main
+- pom.xml ... <packaging>pom...<module>external...<module>internal
|
+- external
| +- ... Maven dirs as usual ...
| + pom.xml
|
+- internal
+- ... Maven dirs as usual ...
+- pom.xml ... <dependency>external
Such when building main the Maven Reactor takes care of the projects' build order (build external first, then internal in this case) and you can forget about dealing with settings.xml, repositories, profiles or properties.

How to activate a Maven profile per pom.xml?

I have a large Maven project with one master module and lots of children, organized in a nice tree structure. However, some of the modules need special settings which would cause misbehaviour in other modules. For example, there are modules which do stuff on Hadoop which need a Java source level of 1.7, all other Java modules use a source level of 1.8, and now Scala modules are introduced into this tree which compile with scala-maven-plugin which needs special settings for maven-compiler-plugin when compiling mixed Java/Scala modules.
In the past I simply had a master module for the Hadoop stuff which defined the source level of 1.7 and added all necessary Hadoop dependencies. Now I want to avoid defining another master module for Hadoop stuff written in Scala, and yet another module for Non-Hadoop stuff in Scala. I thought I could use profiles, so I defined two profiles in my master POM:
java activates the maven-javadoc-plugin
scala does not activate maven-javadoc-plugin but the maven-scala-plugin and configures it like in the example from its documentation
In the master POM, the java profile is set as active by default, as Java is the default. When a project uses Scala, I want to activate the scala profile, and deactivate java, but setting activeByDefault to false for the java profile has no effect, causing compile errors.
So, is there a way to enable a profile specific to a module without having to set it manually in each pom?
As far as I understood you want to activate profile from child pom based on its configuration. I'm afraid it is not possible. However you can reorganize project tree structure to take advantage of pom inheritance:
pom.xml // general properties for all projects
|-- java-modules
| -- pom.xml // inherits from master pom, configures maven-compile-plugin
| -- java-8-modules
| -- pom.xml // inherits from java-modules pom, uses java 8 compiler
| -- java-8-project-a
| -- java-7-modules
| -- pom.xml // inherits from java-modules pom, uses java 7 compiler
| -- java-7-project-a
|-- scala-modules
-- pom.xml // inherits from master pom, configures maven-scala-plugin
-- scala-project-a
You write one master pom for all modules in top directory, child poms just specify java/scala details.
More about pom inheritance https://maven.apache.org/guides/introduction/introduction-to-the-pom.html#Project_Inheritance
If you want to configure maven profile for each project(module) then you have to add profile setting in the project(module) pom.xml file
<profiles>
<profile>
<activation>
<jdk>1.4</jdk>
</activation>
<your properties goes here>
</profile>
</profiles>
Also if you do not want to execute some profile then you have to add
mvn groupId:artifactId:goal -P !profile-1,!profile-2
in the project pom.xml file
Don't use profile, Mateusz Balbus answer, that is define nested pom-packaged project that define java and scala modules respectively

Maven release plugin - tagbase configuration - tags folder included in project

With each new release I make the tags are included, which eventually leads to this structure:
...VersionExperimentation/tags/VersionExperimentation-0.0.7/tags/VersionExperimentation-0.0.6/tags/VersionExperimentation-0.0.5/tags/VersionExperimentation-0.0.4/tags/VersionExperimentation-0.0.3/tags/VersionExperimentation-0.0.2-beta/tags/VersionExperimentation-0.0.2-alpha/src/main/java
My POM looks like this:
<scm>
<developerConnection>scm:svn:https://[..root svn folder]/VersionExperimentation</developerConnection>
</scm>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-release-plugin</artifactId>
<configuration>
<tagBase>[..root svn folder]/VersionExperimentation/tags</tagBase>
</configuration>
</plugin>
</plugins>
</build>
Obviously the tags should be in the root, not in VersionExperimentation folder, where the project is, which is defined in the developerConnection.
What is the best practice here? Should we have a project within some folder within the SVN root and tags in the same path, where the project folder is?
Like:
./projectFolder
./tagsFolder
where DeveloperConnection is going to point to projectFolder and configuration of release tagbase tag to the tagsFolder?
This would seem to follow the recommended repository layout by svn book, which can be found here
$ svn list file:///var/svn/single-project-repo
trunk/
branches/
tags/
If yes, what should we do, if we have projects, which, unfortunately store the project immediately in the root folder?
Should we create the folders, move the data to the right place and configure the pom appropriately, to store the tags in tags folder and to commit to the trunk/projectFolder?
You should follow the best practices, cause they are not for nothing called best practice. I would suggest to have things like:
repository-root
+-- project1
+-- project2
+-- project3
On the root level in your repository you can insert supplemental folder to your project (organization stuff etc.) if you need but within every project i would recommend to follow the TTB structure of subversion which means having each project:
+-- project1
+--- trunk
+--- tags
+--- branchs
which in consequence will remove the need of configuration in Maven (Convention over configuration).
Apart from the above storing a single project in a single repository is not recommend cause in Subversion it is not neccesary in contradiction to Git where the things are running different.
So in Subversion there is no problem at all storing hundreds or thousands of projects within a single Subversion repository...
On the other hand if you have a really good reason not to go with the defaults you should explain in detail why you need to go different paths.

Share specific PMD rulesets across multi module maven project

I'm trying to share the same pmd configuration across all my submodules. I'm looking for the best way to achieve that
I thought that I could place it in the parent project, like I did it for checkstyle plugin
Parent pom.xml
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-checkstyle-plugin</artifactId>
<version>2.9.1</version>
<configuration>
<configLocation>/src/main/config/checkstyle.xml</configLocation>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-pmd-plugin</artifactId>
<version>2.7.1</version>
<configuration>
<rulesets>
<ruleset>pmd.xml</ruleset>
</rulesets>
<linkXref>true</linkXref>
<sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
<targetJdk>${maven.compiler.target}</targetJdk>
</configuration>
</plugin>
</plugins>
</build>
Multi Module Structure
Here my structure
parent
|-- src
| `-- main
| `-- resources
| |-- pmd.xml
| `-- checkstyle.xml
|-- pom.xml
|-- model
| `-- pom.xml
`-- webapp
`-- pom.xml
Error
With this configuration, I only obtain the following error :
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-pmd-plugin:2.7.1:pmd (default-cli) on project model: An error has occurred in PMD Report report generation. Could not find resource 'pmd.xml'. -> [Help 1]
Solution 1
I tried this solution that works for me, because I only have one level on submodules :
But in a close future, I may have more levels, so I'm not convinced that's THE method
${basedir}/../src/main/resources/pmd.xml
Solution 2
Without writing all the solution, I can use an assembly to zip my config and use it in all my submodule as an dependency. This would work for any levels, but this is overkilling !
Here a link that would explain it: How to include resources from war to another maven project
So I'm looking for a Maven trick, but I don't what or how ! Every advice / clue is welcome.
Have a separate module that contains those generic config files like build-tools. Then you can add this module as a dependency to your plugin config and load it.
I have implemented an example of this with a checkstyle config file across a multiple modules in the ksoap2-android project.
https://github.com/mosabua/ksoap2-android/blob/master/pom.xml
I was reading the documentation for the ruleset tag and it says
The rule sets may reside in the classpath, filesystem or at a URL. For
rule sets that are bundled with the PMD tool, you do not need to
specificy the absolute path of the file. It will be resolved by the
plugin. But if the rule set is a custom rule set, you need to specify
its absolute path.
I solved the problem of sharing my PMD settings across my multi-maven build by specifying the URL to the ruleset file in my parent pom. (My repo was http accessible). Unfortunately this won't work though if you don't have your repo http accessible, but many people may.

Maven maven-glassfish-plugin in a multiproject setup

Short version:
I would like the maven-glassfish-plugin to only be executed in the root project in a hierarchical (inheritance based) maven multiproject setup.
Long version:
Following setup:
project-root
|
+-pom.xml
|
+ ear-module
| |
| +-pom.xml
|
+ jar-module
|
+-pom.xml
All submodules are included in the root project via <modules>...</modules> and all submodules inherit the root project pom.xml.
In my root project pom I include the maven-glassfish-plugin:
...
<plugin>
<groupId>org.glassfish.maven.plugin</groupId>
<artifactId>maven-glassfish-plugin</artifactId>
<version>2.1</version>
<inherited>false</inherited>
<configuration>
<glassfishDirectory>${glassfish.home}</glassfishDirectory>
<passwordFile>${glassfish.home}/masterpassword.txt</passwordFile>
<domain>
<name>${project.name}</name>
<adminPort>4848</adminPort>
<httpPort>8080</httpPort>
<httpsPort>8443</httpsPort>
<iiopPort>3700</iiopPort>
<jmsPort>7676</jmsPort>
</domain>
<components>
<component>
<name>poc.vermittler</name>
<artifact>${project.basedir}/ear-module/target/ear-project-1.0-SNAPSHOT.ear</artifact>
</component>
</configuration>
</plugin>
...
(Note: This is just an simplified version of my pom. It may not run :)
I want to only deploy the ear-module module to glassfish, this is why I added <inherited>false</inherited> section, and depict the modules to be deployed as <components>...</components> in the root pom.
Now the command:
mvn glassfish:deploy
Will deploy the ear to glassfish, all well so far... but then maven will decent recursively to all submodules, which will all fail with:
No plugin found for prefix 'glassfish' in the current project and in the plugin groups [org.apache.maven.plugins, org.codehaus.mojo] available from the repositories
I could tell maven to only run the root project with the -pl option but for my gusto, deploying shouldn't rely on such additional information.
Thanks a lot for your help!
It seems that there is no good solution to this problem:
either the plugin supports a "NOP"/silent discard functionality
or it will fail in all subprojects
Another method could be to create a new subproject (which is not included in the root project by <modules>...</modules> but inherits from the root project) and add dependencies to only the projects that have a deployment artifact.
The plugin can now be included in this subproject without it wanting to run any subproject.
Or for anybody who is lazy: mvn clean package glassfish:redeploy -pl . to selectively only run the root project without descending into child projects.

Resources