Multi-module maven project - use one module just as a dependency - spring

I have given the following structure (Maven + Spring). For the DependencyService I'm only interested in some models (Java classes, which I want to reuse and not only copy and paste). Due to some docker stuff I can't run the DependencyService from eclipse, I have to set up several things. Thats the reason why I only want to use the classes and don't include the whole project.
Given the following structure:
MainFolder
|
----MainApplication
|
----Backend1
|
----pom.xml
|
----Backend2
|
----pom.xml
|
----DependencyService
|
----someModelsAreInHere
|
----pom.xml
|
----pom.xml
At the moment the relevant parts of the pom look like the following:
MainFolder Pom
<!-- Inherit defaults from Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.0</version>
</parent>
<modules><module>Backend1</module><module>Backend2</module></modules>
...
<!-- root pom settings -->
<groupId>GROUP</groupId>
<artifactId>MainProject</artifactId>
<version>${revision}</version>
<packaging>pom</packaging>
<name>MainProjectName</name>
...
<!-- Manages Dependencies -->
<dependencyManagement>
<dependencies>
<dependency> <!-- Import the dependency here -->
<groupId>GROUP.MainProject</groupId>
<artifactId>DependencyService</artifactId>
<version>${dependency.service.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
... </project>
Backend1 as an example,Backend1, Backend2 and DependencyService's pom.xml look very similar.
<!-- Inherit defaults from MainFolder -->
<parent>
<artifactId>MainProject</artifactId>
<groupId>GROUP</groupId>
<version>${revision}</version>
</parent>
<!-- data-processor pom settings -->
<groupId>GROUP</groupId>
<artifactId>backend1</artifactId>
<name>Backend1Name</name>
<packaging>jar</packaging>
<version>${revision}</version>
<!-- Add required dependencies -->
<dependencies>
<!-- include DependencyService here but JUST for the classes, not for compiling! -->
<dependency>
<groupId>GROUP.MainProject</groupId>
<artifactId>dependencyService</artifactId>
</dependency>
</dependencies>
Question
How can I include DependencyService to use the classes in there for Backend1 and Backend2 but NOT compile it when I compile Backend1 and Backend2? (Because it won't compile without dockers etc...)

Related

Is it possible for a module of a multi module project to use the repository folder of another?

I was following a tutorial to learn multi-modules in maven, but what was being presented raised a question:
Is there a possibility to create a multi-module project where only one module accesses the database and the others use this connection for their respective controllers?
Basically what I want is to use the same repository folder to scan with each datasourceconfig in all modules.
The structure I imagined and even started to implement was:
|--main
| |--moduleOne
| |--|--Java
| |-----|-DataSource
| |-----|-models
| |-----|-repositories
| |--|--Resources
| |-----|-application.properties
| |--moduleN
| |--|--Java
| |-----|-controller
| |-----|-service
| |--|--Resources
| |-----|-application.properties
The first module run perfectyly, the other need a conetion and I did It, but when I run, this module use a h2 database or not connect because moduleOne is using repository folder.
How can I fix for that services access those repositories?
POM ModuleOne
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>sig</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>moduleOne</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>moduleOne</name>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!-- SQL DEPENDENCIES -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
<build>
<finalName>moduleOne</finalName>
</build>
</project>
POM ModuleN
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>sig</artifactId>
<version>0.0.1-SNAPSHOT</version>
<relativePath>../</relativePath> <!-- lookup parent from repository -->
</parent>
<artifactId>moduleN</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>moduleN</name>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<!-- MODULE ON WHICH IT DEPENDS -->
<dependency>
<groupId>com.example.sig</groupId>
<artifactId>moduleOne</artifactId>
<version>0.0.1-SNAPSHOT</version>
</dependency>
</dependencies>
<build>
<finalName>moduleN</finalName>
</build>
</project>
"The bean 'RepositoryX', defined in
br.example.sig.moduleone.repositories.RepositoryX defined in
EnableJpaRepositories declared on ServiceX, could not be registered. A
bean with that name has already been defined in
br.example.sig.moduleone.repositories.RepositoryX defined in
EnableJpaRepositories declared on DatasourceConfig and overriding is
disabled.
Consider renaming one of the beans or enabling overriding by setting
spring.main.allow-bean-definition-overriding=true"
I had a service with the annotations "#EntityScan" and "#EnableJpaRepositories" pointing to the same directory as my datasource in one of the modules, I put it there and forgot it.
I deleted the annotations, that was what generated the conflict and not the use for different modules.

How does Maven handle transitive dependencies inherited from parent?

Given the parent and child pom below and lib1 and lib2 both include the class foo.bar.Test.
parent pom
<project>
<modelVersion>4.0.0</modelVersion>
<groupId>foo</groupId>
<artifactId>foo-parent</artifactId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>my.transitive</groupId>
<artifactId>lib1</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
child pom
<project>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>foo</groupId>
<artifactId>foo-parent</artifactId>
<version>1.0</version>
</parent>
<artifactId>foo-child</artifactId>
<dependencies>
<dependency>
<groupId>my.transitive</groupId>
<artifactId>lib2</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
If I include foo-child as a dependency in myApp and instantiate foo.bar.Test, which version of the class would Maven resolve to? And why?
my app pom
<project>
<modelVersion>4.0.0</modelVersion>
<artifactId>myApp</artifactId>
<groupId>myApp</groupId>
<version>1.0</version>
<dependencies>
<dependency>
<groupId>foo</groupId>
<artifactId>foo-child</artifactId>
<version>1.0</version>
</dependency>
</dependencies>
</project>
The short answer would be: it depends on which one is found on the classpath first.
Having 2 of the same classes packaged with an application is not ideal as it can lead to many difficult to debug errors... If your myApp project is just going to be a jar then it would be best to compile it using the same library as what is going to be available to it at runtime.
I believe maven uses the order it is written to the pom to build. One way of looking at this would be to run the following command for myApp:
mvn dependency:tree -Dverbose
This will print the dependencies in the order that they should appear on the classpath per spec. You can always use exclusions to exclude any inherited library you might not want. Hope this helps.

Avoid wrong version interpolation if child's pom version is different from those of the parent's aggregator pom and its sub modules

Problem description
We have a Maven aggregator pom with some child poms (modules) all having the same version:
pom.xml (parent zoo, version 2.0.0)
|-- pom.xml (child module cat, version 2.0.0)
|-- pom.xml (child module dog, version 2.0.0)
|-- ...
Within the dependency management section all children are declared with the project version to ease declaration of dependencies.
The parent pom looks like
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.0</version>
<packaging>pom</packaging>
<modules>
<module>cat</module>
<module>dog</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<version>${project.version}</version>
</dependency>
<!-- other child modules go here -->
</dependencies>
</dependencyManagement>
The child poms are defined as
<parent>
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.0</version>
</parent>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>dog</artifactId>
</dependency>
</dependencies>
There is another pom which declares the parent pom as its parent too (inheritance) but is not listed as sub module in this parent (no aggregation). This pom has a different version.
<parent>
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.0</version>
</parent>
<groupId>com.acme</groupId>
<artifactId>boo</artifactId>
<version>1.0.0</version>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>dog</artifactId>
</dependency>
</dependencies>
Actually we have expected that the version of the dependency com.acme.dog is pulled from the dependency management section of the parent pom com.acme.zoo and is equal to 2.0.0. However the Maven documentation on project interpolation and variables says
One factor to note is that these variables are processed after inheritance as outlined above. This means that if a parent project uses a variable, then its definition in the child, not the parent, will be the one eventually used.
That is: in the reactor build the variable ${project.version} used in the dependency management section of the parent pom com.acme.zoo is evaluated with respect to com.acme.bar and equal to 1.0.0 what is not as intended.
Note
There is a workaround with using a variable in the parent pom which has to be kept in sync with the parent pom versions. However, this solution is incompatible with the Maven Release Plugin.
Question
How can we achieve the desired behaviour
aggregator pom with children having the same version
declaration of children in the dependency management section to ensure that all dependencies have the same version
use of inheritance together with different versions
compatibility with maven-release-plugin
without the pitfalls of project interpolation of variables?
The maven release plugin is able to change the versions of the dependencies managed in the parent pom.
So if you define your maven parent like this:
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>cat</module>
<module>dog</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<version>2.0.0-SNAPSHOT</version>
</dependency>
<!-- other child modules go here -->
</dependencies>
</dependencyManagement>
As you see the versions of the parent and the managed dependency are the same. I set them to a SNAPSHOT version because the release plugin will create the final versions on release:perform
Your child poms can stay as you had them.
Because in your setup, your parent project is also the reactor you can then call
mvn release:perform -DautoVersionSubmodules=true
which will update the version of the parent in all submodules when you run this command. That option is essentially the same as if you run
mvn versions:update-child-modules
meaning it will change the child poms.
After you run the mvn release:perform command your parent pom will look like this:
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>cat</module>
<module>dog</module>
</modules>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<version>2.0.1-SNAPSHOT</version>
</dependency>
<!-- other child modules go here -->
</dependencies>
</dependencyManagement>
and your child poms like this
<parent>
<groupId>com.acme</groupId>
<artifactId>zoo</artifactId>
<version>2.0.1-SNAPSHOT</version>
</parent>
<groupId>com.acme</groupId>
<artifactId>cat</artifactId>
<dependencies>
<dependency>
<groupId>com.acme</groupId>
<artifactId>dog</artifactId>
</dependency>
</dependencies>
The final versions will only exist in the tag created by the release:prepare command.
PS: You may define other versions for the final and the next development version when they are prompted after running the release:prepare command.
The simplest solution is modify pom of zoo and replace <version>${project.version}</version> with <version>2.0.0</version>
Please note:
when you change version to next number, for example 2.0.1, with
versions-maven-plugin, dependency management section will be also
updated.
Spring use simplest solution, see
http://central.maven.org/maven2/org/springframework/spring-framework-bom/4.2.7.RELEASE/spring-framework-bom-4.2.7.RELEASE.pom
Summary: using <version>${project.version}</version> in dependency management is wrong idea.
From Maven Introduction to the pom : http://maven.apache.org/guides/introduction/introduction-to-the-pom.html
Project Inheritance > Example 1 > The Solution
Alternatively, if we want the groupId and / or the version of your
modules to be the same as their parents, you can remove the groupId
and / or the version identity of your module in its POM.
<project>
<parent>
<groupId>com.mycompany.app</groupId>
<artifactId>my-app</artifactId>
<version>1</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>my-module</artifactId>
</project>
My approach to that is to track it in the child POM. It's a bit less typing overall, close to where the actual dependency lives and is low maintenance for most projects. YMMV
<dependencies>
...
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>foo-sibling</artifactId>
<version>${project.version}</version>
</dependency>
...
</dependencies>

Maven: Reuse single version definition when referencing parent pom

I have the following pom definition (bottom).
I have many child poms (50 projects), requiring me to update all the poms on each release, for example, when moving from 1.0 to 1.1.
How can I define the version in a single place, and reuse it in all the poms?
EDIT- Some motivation about the request: I'd like to make as little footprint as possible when switching version. As little files to change. As little commits to push. Etc.
EDIT - Cannot use parent properties before the parent is loaded.
<parent>
<groupId>info.fastpace</groupId>
<artifactId>parent</artifactId>
<version>1.0-SNAPSHOT</version>
</parent>
<artifactId>child-1</artifactId>
I can use parent's properties and reference the parent using relative path instead of version. Example:
Parent:
<groupId>info.fastpace</groupId>
<artifactId>parent</artifactId>
<version>${global.version}</version>
<properties>
<!-- Unique entry point for version number management -->
<global.version>1.0-SNAPSHOT</global.version>
</properties>
Child:
<parent>
<groupId>info.fastpace</groupId>
<artifactId>parent</artifactId>
<version>${global.version}</version>
<relativePath>..</relativePath>
</parent>
<artifactId>child-1</artifactId>
Disadvantage: Requires the parent pom to exist in the file system and make all developers use the same relative file structure.
See more info here.
You can use maven properties to build a single version numbering scheme.
Like this:
<properties>
<my.version>1.1.2-SNAPSHOT</my.version>
</properties>
And then reference it like this:
<version>${my.version}</version>
Look here for more information:
Maven version with a property
The use of properties is recommended when you have multiple dependencies of the same release. For example:
<project>
...
<properties>
...
<dep.jooq.version>3.7.3</dep.jooq.version>
...
</properties>
...
<dependencies>
...
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq</artifactId>
<version>${dep.jooq.version}</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-meta</artifactId>
<version>${dep.jooq.version}</version>
</dependency>
<dependency>
<groupId>org.jooq</groupId>
<artifactId>jooq-codegen</artifactId>
<version>${dep.jooq.version}</version>
</dependency>
...
</dependencies>
...
</project>
Instead, if you have to use the same dependency in different points in the POM file or if you are in module and you would use the same dependency version of the parent, I suggest to use the following way:
<project>
...
<dependencyManagement>
<dependencies>
...
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<version>1.0</version>
</dependency>
...
<dependencies>
</dependencyManagement>
...
<dependencies>
...
<!-- The following block could be in a module -->
<dependency>
<groupId>group-a</groupId>
<artifactId>artifact-a</artifactId>
<!-- It is no more ncessary to use the version -->
</dependency>
...
<dependencies>
...
</project>

Multi-module maven build : different result from parent and from module

I am migrating an application from ant build to maven 3 build.
This app is composed by :
A parent project specifying all the modules to build
A project generating classes with jaxb and building a jar with them
A project building an ejb project
3 projects building war modules
1 project building an ear
Here is an extract from my parent pom :
<groupId>com.test</groupId>
<artifactId>P</artifactId>
<packaging>pom</packaging>
<version>04.01.00</version>
<modules>
<module>../PValidationJaxb</module> <-- jar
<module>../PValidation</module> <-- ejb
<module>../PImport</module> <-- war
<module>../PTerminal</module> <-- war
<module>../PWebService</module> <-- war
<module>../PEAR</module> <-- ear
</modules>
I have several problems which I think have the same origin, probably a dependency management issue that I cannot figure out :
The generated modules are different depending on if I build from the parent pom or a single module. Typically if I build PImport only, the generated war is similar to what I had with my ant build and if I build from the parent pom, my war took 20MB, a lot of dependencies from other modules had been added. Both wars are running well.
My project PWebService has unit tests to be executed during the build. It is using mock-ejb which has cglib as dependency. Having a problem of ClassNotFound with this one, I had to exclude it and add a dependency to cglib-nodep (see last pom extract). If I then build only this module, it is working well. But if I build from the parent project, it fails because other dependencies in other modules also had an implicit dependency on cglib. I had to exclude it in every modules pom and add the dependency to cglib-nodep everywhere to make it run.
Do I miss something important in my configuration ?
The PValidation pom extract :
It is creating a jar containing an ejb with interfaces generated by xdoclet, as well as a client jar.
<parent>
<groupId>com.test</groupId>
<artifactId>P</artifactId>
<version>04.01.00</version>
</parent>
<artifactId>P-validation</artifactId>
<packaging>ejb</packaging>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>P-jaxb</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate</artifactId>
<version>3.2.5.ga</version>
<exclusions>
<exclusion>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
</dependency>
...
[other libs]
...
</dependencies>
<build>
...
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-ejb-plugin</artifactId>
<configuration>
<ejbVersion>2.0</ejbVersion>
<generateClient>true</generateClient>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>xdoclet-maven-plugin</artifactId>
...
The PImport pom extract :
It depends on both Jaxb generated jar and the ejb client jar.
<parent>
<groupId>com.test</groupId>
<artifactId>P</artifactId>
<version>04.01.00</version>
</parent>
<artifactId>P-import</artifactId>
<packaging>war</packaging>
<dependencies>
<dependency>
<groupId>com.test</groupId>
<artifactId>P-jaxb</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.test</groupId>
<artifactId>P-validation</artifactId>
<version>${project.version}</version>
<type>ejb-client</type>
</dependency>
...
[other libs]
...
</dependencies>
The PWebService pom extract :
<parent>
<groupId>com.test</groupId>
<artifactId>P</artifactId>
<version>04.01.00</version>
</parent>
<artifactId>P-webservice</artifactId>
<packaging>war</packaging>
<properties>
<jersey.version>1.14</jersey.version>
</properties>
<dependencies>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-servlet</artifactId>
<version>${jersey.version}</version>
</dependency>
<dependency>
<groupId>com.rte.etso</groupId>
<artifactId>etso-validation</artifactId>
<version>${project.version}</version>
<type>ejb-client</type>
</dependency>
...
[other libs]
...
<dependency>
<groupId>org.mockejb</groupId>
<artifactId>mockejb</artifactId>
<version>0.6-beta2</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>cglib</groupId>
<artifactId>cglib-full</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2.2</version>
<scope>test</scope>
</dependency>
</dependencies>
Many thanks
Solution after modification of the configuration :
When I got the projects already mavenized, it didnt respect the folder layout convention, but as it was declared in the pom where to find the sources, I thought it would be working.
Anyway, i changed it to match the recommended structure.
To build a single module I was executing mvn clean install directly at its level. It is this way I obtained a different result (which is in fact the one I wanted).
Anyway, my problem is solved, I put all the dependencies of the PValidation project as provided, as I am only including the generated client in other modules and they dont require all what is needed for the implementation.
But still I dont get why I had different result for the same configuration.
The first important thing is you should create the structure of your project appropriate to the modules structure which means having the following folder structure:
+-- parent
+-- PValidationJaxb
+-- PValidation
+-- PImport
+-- PTerminal
+-- PWebService
+-- PEAR
This means having a pom.xml which contains the modules definition in the parent folder.
if you follow the above recommendation you can simplify the modules list to the following:
<modules>
<module>PValidationJaxb</module> <-- jar
<module>PValidation</module> <-- ejb
<module>PImport</module> <-- war
<module>PTerminal</module> <-- war
<module>PWebService</module> <-- war
<module>PEAR</module> <-- ear
</modules>
Furthermore a best practice in Maven is to use lowercase artifacts which mean in your case pvalidationjaxb instead of PValidationJaxb.
An other important thing is your version which does NOT follow the Maven conventions. Furthermore your version will be from the Maven point of view a release which is not the case you are doing development on this. In Maven you should use a so called SNAPSHOT for such purposes like 1.0.0-SNAPSHOT.
I hope you have followed the folder layout recommendation of Maven which says to put production code (which will be packaged into the resulting jar) into src/main/java whereas test code into src/test/java.
The problem you described having different dependencies sounds weired. The question is how have you tried to build a single moduel? This can usualy be achieved by using the following from the parent location:
mvn -pl module clean package
The problem with your unit tests sounds like a missing dependencies etc. but here is the questions how have you tried to run the unit tests and have you configured maven-surefire-plugin ? Or do you have integration tests? This is only a guess cause i don't see any configuration of Maven plugins in your poms.

Resources