I have a multi-module Maven project structure with several layers, something like this:
root-module
module-group
war-module
pom.xml
jar-module1
pom.xml
jar-module2
pom.xml
pom.xml
another-module
pom.xml
etc
pom.xml
pom.xml
module-group, another-module and etc have root-module as their parent.
So root-module/pom.xml has this:
<packaging>pom</packaging>
<modules>
<module>module-group</module>
<module>another-module</module>
<module>etc</module>
</modules>
and each of those modules has this:
<parent>
<!-- artifact coordinates etc -->
<artifactId>root-module</artifactId>
</parent>
Then, war-module, jar-module1 and jar-module2 have module-group as their parent. Thus, module-group/pom.xml also includes:
<modules>
<module>war-module</module>
<module>jar-module1</module>
<module>jar-module2</module>
</modules>
war-module has jar-module1 in its <dependecies>, and jar-module1 depends on jar-module2. War-module has <packaging>war</packaging>, jar-* modules have jar.
So dependency chain is like war-module -> jar-module1 -> jar-module2. Versions for each artifact are defined using ${revision} (I'm using Maven3).
I was trying to introduce the dependency chain from war-module in the profiles in war-module/pom.xml:
<profiles>
<!-- this profile is for building with jar-module1 (jar-module2 is resolved transitively) -->
<profile>
<id>profile1</id>
<dependencies>
<dependency>
<artifactId>jar-module1</artifactId>
</dependency>
</dependencies>
</profile>
<!-- this profile is for building without jar-module1 (straight dependency to jar-module2) -->
<profile>
<id>profile2</id>
<dependencies>
<dependency>
<artifactId>jar-module2</artifactId>
</dependency>
</dependencies>
</profile>
</profiles>
However, when I try to build my project (or just module-group or even jar-module1) the process exits with error while packaging jar-module1:
package org.example.package.from.jarmodule2 doesn't exist.
(that package has the only Java class from jar-module2 referenced in jar-module1). However, I have the target folder in my jar-module2 with correct .jar in it. IntelliJ IDEA resolves the classes correctly, only Maven cannot build it right. What might be the problem?
Also, if I introduce the dependency without using profiles, it seems to package correctly. But I need to build different configurations all the time and would really love to not have to change pom.xml every time.
Related
I have multiple spring boot projects, every project is independent and has its own CICD pipeline.
These projects need some common classes, I have created a separate project with common classes.
Parent
pom.xml (with packaging)
lib-project
pom.xml
project-1
pom.xml
project-2
pom.xml
I can build project easily from the parent directory, it builds all the projects
parent$ mvn clean package
it generates all the jar files and put them in their respective target folder of projects
My problem is I can't initiate this at the parent level, this has to be initiated from within each project from its own pipeline.
and
I cannot use any local or remote repository, to put the dependent jar in m2 using mvn clean install and then refer to it as dependency
I want to build it from the relavent project directory
parent/project-1$ mvn clean package
it shows following error:
Could not resolve dependencies for project com.test.multiple:project-1:jar:0.0.1-SNAPSHOT: Could not find artifact com.test.multiple:lib-project:jar:0.0.1-SNAPSHOT
My expectation stepwise on compilation of project-1
Check if there is a dependency for project-1
Go to ../lib-project
Compile and build it in target folder (or anywhere relative to our project)
Add this generated jar to "project-1" dependency
Compile and build the "project-1" jar file.
Parent Pom Configurations
<project ...>
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.test.multiple</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>lib-project</module>
<module>project-1</module>
</modules>
</project>
** Lib project pom **
<project ...>
<parent>
<groupId>com.test.multiple</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.test.multiple</groupId>
<artifactId>lib-project</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>lib-project</name>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Project-1 pom
<project ...>
<parent>
<groupId>com.test.multiple</groupId>
<artifactId>parent</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<groupId>com.test.multiple</groupId>
<artifactId>project-1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>project-1</name>
<properties>
<java.version>11</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.test.multiple</groupId>
<artifactId>lib-project</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
I have multiple spring boot projects, every project is independent and has its own CICD pipeline.
These projects need some common classes, I have created a separate project with common classes.
Congratulations, your projects are not independent any more!
Given the definitions above, here are the dependencies:
lib-project depends on parent;
project-1 depends on parent;
project-1 depends on lib-project.
Please check Introduction to the POM and Guide to Working with Multiple Modules for the discussion on the dependencies in Maven projects.
I cannot use any local or remote repository, to put the dependent jar in m2 using mvn clean install and then refer to it as dependency
Given this limitation, and dependencies listed above, the POMs and source files of all the projects have to be present on the disk for build purposes. The build process has to start from the parent folder. The build process has to build all modules at once.
Also, please consider using mvn clean verify instead of mvn clean install to avoid populating the local repository with the artifacts you are building.
A maven project isn't designed to build its dependencies on demand. However, jenkins can be configured to build downstream projects when changes are pushed to an upstream dependency.
I have also worked around this by using the -pl option on a parent pom in the relevant jenkinsfile to build a subset of the child projects
Jenkinsfile
clone parent project
mvn clean package -pl core,deployable
Given a library which has different dependencies depending on a profile, say for example
<profile>
<id>default</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<properties>
<envClassifier>cuda-10.1</envClassifier>
</properties>
<dependencies>
<dependency>
<groupId>org.jcuda</groupId>
<artifactId>jcuda</artifactId>
<version>10.1.0</version>
</dependency>
</dependencies>
</profile>
<profile>
<id>cuda-10.0</id>
<properties>
<envClassifier>cuda-10.0</envClassifier>
</properties>
<dependencies>
<dependency>
<groupId>org.jcuda</groupId>
<artifactId>jcuda</artifactId>
<version>10.0.0</version>
</dependency>
</dependencies>
</profile>
I am creating artefacts with different classifiers, say for example
library-1.0.0-cuda-10.0
and
library-1.0.0-cuda-10.1
(here cuda-10.0 and cuda-10.1 are the classifiers for the artefact library-1.0.0).
If another project references this library, say for example as
<dependency>
<groupId>net.finmath</groupId>
<artifactId>library</artifactId>
<version>1.0.0</version>
<classifier>cuda-10.0</classifier>
</dependency>
then the correct artefact is pulled (here library-1.0.0-cuda-10.0.jar) but the dependency tree (via mvn dependency:tree) shows the wrong dependencies of the dependency. In this example it shows jcuda-10.1.0 and not juda-10.0.0.
This issue is due to an artefact having a single pom.xml for all classifiers (and the default profile kicks in).
Question 1: Can you have dedicated pom.xml in the repositories for different classifiers, to reflect the correct dependencies?
Apparently the issue cannot be resolved by setting a profile (-P parameter) on the outer project, because profile selectors are not passed to the poms of the dependencies. It looks as if profiles do not walk down the dependency tree.
Question 2: Is there a way to pass a profile selector to a pom of a dependency to select the right dependencies of the dependency?
I found two options to resolve the problem, but I am not very happy with them.
The first one would be to not have a default profile with a dependency in the pom of the library. In that case the user of the library (here library-1.0.0) has to specify the correct classifier and the correct downstream dependencies. This appears to be cumbersome. Also note that in this case the build of the library would fail without specification of the profile.
It is possible to use the profile to alter the name (or version) of the artefact. Since each version or artefact comes with its own pom, this allows to specify profile specific dependencies to be resolved.
However, I believe there should be a better solution, because otherwise it looks as if the specification of dependencies in profiles makes no sense for artefacts which are libraries, that is, which are themselves dependencies of another projects.
Question 3: What is the Maven way to resolve this issue?
PS: The question arose in this project: http://finmath.net/finmath-lib-cuda-extensions/
I found a lightweight solution for the problem.
You may activate a profile though a property.
Properties may not be passed from a pom to the poms of it's dependencies, but a property which is set on the command line acts on both poms.
Hence, instead of using a profile on the command line, use a property and activate the corresponding profiles in your project's pom and the library's pom.
So in the above example, the (outer) project (which references the library) has a profile
<profile>
<id>cudaversion</id>
<activation>
<property>
<name>cuda.version</name>
</property>
</activation>
<properties>
<finmath-cuda.classifier>cuda-${cuda.version}</finmath-cuda.classifier>
</properties>
</profile>
which sets the classifier of its dependency, that is this project has a dependency to library-1.0.0
<dependency>
<groupId>net.finmath</groupId>
<artifactId>library</artifactId>
<version>1.0.0</version>
<classifier>${finmath-cuda.classifier}</classifier>
</dependency>
And the pom of library-1.0.0 has a profile which is activated by the same property as in
<profile>
<id>cuda-10.0</id>
<activation>
<property>
<name>cuda.version</name>
<value>10.0</value>
</property>
</activation>
<properties>
<envClassifier>cuda-10.0</envClassifier>
</properties>
<dependencies>
<dependency>
<groupId>org.jcuda</groupId>
<artifactId>jcuda</artifactId>
<version>10.0.0</version>
</dependency>
</dependencies>
</profile>
The outer project is then build with mvn -Dcuda.version=10.0 to activate both profiles.
The whole project dependencies are looking like this:
- parent
- child-a
- pom.xml
- child-b
- src.main.java.resources
- mybatis-generator.xml
- pom.xml
- pom.xml
parent, pom.xml
<project>
<packaging>pom</packaging>
<modules>
<module>child-a</module>
<module>child-b</module>
</modules>
</project>
child-b, pom.xml
<project>
<packaging>jar</packaging>
<parent>
<artifactId>parent</artifactId>
<relativePath>../</relativePath>
</parent>
<dependencies>
<dependency>
<artifactId>child-a</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.mybatis.generator</groupId>
<artifactId>mybatis-generator-maven-plugin</artifactId>
<!-- other configs -->
</plugin>
</plugins>
</build>
</project>
When packaging child-b project/module solely,all dependencies were handled properly(child-a was imported as expected).
Problem:
When I'm trying to run mvn mybatis-generator:generate in the directory of child-b:
Error: Failed to execute goal on project service_a: Could not resolve dependencies for project xx.xx.child-b:jar:1.0-SNAPSHOT: Could not find artifact xx.xx.child-a:jar:1.0-SNAPSHOT -> [Help 1]
When you run maven from a nested project, it will only succeed if all the dependencies are available in your local repo. I can reproduce this if I delete a dependency from my local repo and try it on one of my projects.
Snapshot dependencies add another level of complexity in that they expire - meaning maven will look for a newer version if the version in your local repo is not considered current. They may be why a package ran at some point, but later on another maven commend like running the generator failed.
Bottom line...when snapshot dependencies are involved with nested projects, the only reliable way to run maven is to run the entire project (run maven from the parent directory).
I have a multi module project with below hierarchy :
parent-build project pom.xml
...
<groupId>com.my.project</groupId>
<artifactId>parent-build</artifactId>
<packaging>pom</packaging>
<name>parent-build</name>
<version>${version.product}</version>
<properties>
<version.product>1.0</version.product>
</properties>
...
build-all project pom.xml
...
<parent>
<artifactId>parent-build</artifactId>
<groupId>com.my.project</groupId>
<version>${version.product}</version>
<relativePath>../parent-build/pom.xml</relativePath>
</parent>
<groupId>com.my.project</groupId>
<packaging>pom</packaging>
<modules>
<module>../child-1</module>
<module>../child-2</module>
</modules>
...
child-2 project pom.xml
...
<parent>
<artifactId>parent-build</artifactId>
<groupId>com.my.project</groupId>
<version>${version.product}</version>
<relativePath>../parent-build/pom.xml</relativePath>
</parent>
<groupId>com.my.project</groupId>
<artifactId>child-2</artifactId>
<packaging>jar</packaging>
<version>${version.product}</version>
...
<dependencies>
<dependency>
<groupId>com.my.project</groupId>
<artifactId>child-1</artifactId>
<version>${version.product}</version>
</dependency>
...
</dependencies>
...
child-1 project pom.xml
...
<parent>
<artifactId>parent-build</artifactId>
<groupId>com.my.project</groupId>
<version>${version.product}</version>
<relativePath>../parent-build/pom.xml</relativePath>
</parent>
<groupId>com.my.project</groupId>
<artifactId>child-1</artifactId>
<packaging>jar</packaging>
<version>${version.product}</version>
...
I want to build all jars with same version and this version should be specified at single place.
I declared a property in parent-build pom.xml
Now when I do mvn clean install on build-all project , it builds all projects in specified order of modules.
That is fine.
But in some cases , I want to build child-2 project only.
i.e ,lets say I do some changes in child-2 project and want to build only this project.
The problem with this scenario is , it cannot find the property value version.product
Edit:
Below is the error I am getting when building child-2 project
Could not transfer artifact com.my.project:parent-build:pom:${version.product} from/to central (http://repo1.maven.org/maven2): Illegal character in path at index 66: http://repo1.maven.org/maven2/com/my/project/parent-build/${version.product}/parent-build-${version.product}.pom
Please help.
Thanks in advance.
Firstly I would urge against this, but I know you may have a module that is not working properly yet etc. In the past I have used profiles in the parent pom to control the modules list:
<profiles>
<profile>
<id>all</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<modules>
<module>1</module>
<module>2</module>
<module>3</module>
<module>4</module>
</modules>
</profile>
<profile>
<id>profile1</id>
<modules>
<module>1</module>
<module>2</module>
<module>3</module>
</modules>
</profile>
</profiles>
then for :
1) mvn clean install - you will get all modules
2) mvn clean install -Pprofile1 you will only get modules 1,2 & 3.
flatten-maven-plugin
This is what I want.
This plugin will replace your version specified in property, with actual property value in all effective pom.xml.
So later if you build any child separately, it will build successfully.
Cheers
I have many java projects, each with its maven build. They all install fine.
In addition, I have many JUnit tests. These tests may depend on other projects, i.e. a test from Project A may import a class from Project B.
My projects are not structered good (all of the classes are just in src directory and not src/main/java). So when I run mvn clean test I get No sources to compile. If I structure it good by moving all of the main classes to src/main/java and all the tests to src/test/java I keep getting cannot find symbol (with a reference to an import of a class in a different project).
I tried defining <testSourceDirectory>, used many different plugins and profiles and also tried lowering my java configuration level to 1.7, all to no avail.
I even tried defining a TestProject that has a simple POM with many modules (either with only <modules>, or defining <dependencies> or both), but even in this - either I get No sources to compile, cannot find symbol or even package x.y does not exist (or it compiles but trying to include it in other projects does nothing).
Can I run a test that involves classes from other other projects? How?
Maybe you can have and use Test dependencies. I'll explain how i achieve that, hope it's usefull for your project.
I've got a project with many subprojects (named projectA, projectB, etc.) each one with its own Tests. Also one of the projects named 'core' with jar packaging that contains common classes. They look like the following structure:
Main
|- core (jar)
|- projectA (war)
|- projectB (war)
All tests on the diferent projects used common code that finally were placed on 'core' project test clases.
This is how we manage the dependencies on maven 3.1.1.
Only for reference Main is also a project with its own pom.xml
Main (pom.xml):
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.mycompany.myproject</groupId>
<artifactId>MyArtifact</artifactId>
<version>1.0</version>
<packaging>pom</packaging>
<properties>
</properties>
<repositories>
</repositories>
<modules>
<module>core</module>
<module>projectA</module>
<module>projectB</module>
</modules>
...
</project>
On core project pom.xml we put:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<artifactId>core</artifactId>
<packaging>jar</packaging>
<parent>
<groupId>com.mycompany.myproject</groupId>
<artifactId>MyArtifact</artifactId>
<version>1.0</version>
</parent>
<build>
<pluginManagement>
<plugins>
<!-- Common test classes jar creation -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.5</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
...
And on the rest of the projects pom.xml we put the dependency with this jar:
<dependencies>
<!-- We put dependency with core.jar an test-core.jar -->
<dependency>
<groupId>com.mycompany.myproject</groupId>
<artifactId>core</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>com.mycompany.myproject</groupId>
<artifactId>core</artifactId>
<version>1.0</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
Eclipse warns about this maven dependency but it works fin when running Test from the command line with maven.
Hope this helps.