How to create an aggregate fat jar from super pom file? - java

I have an aggregate maven module that is a multi module maven project consisting of a super POM. This aggregate module consists of several sub maven projects. In the super POM of the aggregate module, I have included the jar dependencies that are common for the sub-projects. In the POM file of each sub-project, I have included the jars that are only specific to these projects.
The super POM looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<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.testproject</groupId>
<artifactId>testproject</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<modules>
<module>../../Projects/ProjectA</module>
<module>../../Projects/ProjectB</module>
<module>../../Projects/ProjectC</module>
</modules>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.swinglabs.swingx</groupId>
<artifactId>swingx-core</artifactId>
<version>1.6.3</version>
</dependency>
<dependency>
<groupId>org.jdesktop</groupId>
<artifactId>swing-layout</artifactId>
<version>1.0.3</version>
</dependency>
</dependencies>
</project>
An important note is that my aggregate module contains no source code, but just the super POM. Hence there is no main class as well and therefore I cannot use the Maven Assembly Plugin. My main class is in one of the sub-projects.
How can I create a jar file out of the super POM of the aggregate module?
The reason why I would like to get a jar from the POM file is that I would like to continue using ant for the build process.
My expectation is that the final jar from the super POM contains all the dependent jars like junit, swingx-core, swing-layout etc as one fat jar
that I can include in the ant build process.
Is this possible with maven? Am I missing something?
I have tried to change the packaging from pom to jar but I get a compile error on mvn-install saying "packaging with jar is invalid. Aggregator projects require 'pom' as packaging".
mvn-install and mvn-compile work fine when I use packaging as pom.

Related

Spring boot multi module maven projects without repository

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

.getResource("/filename") returns null when maven packaging is pom

I have a Maven project with a submodule, developed in IntelliJ, using Java 11.
Unless the pom.xml file contains <packaging>pom</packaging>, there is a warning that
'packaging' with value 'jar' is invalid. Aggregator projects require 'pom' as packaging.
But when packaging is set to "pom", the resource file that I need can't be loaded; a null value is returned, and an exception is thrown. From the main() method:
URL resource = getClass().getResource("/fx/gui.fxml");
Objects.requireNonNull(resource);
On the other hand, sometimes the submodule is not found, unless I ask for pom packaging. What I do then is: Request pom packaging, start the program and watch it fail, remove the pom packaging statement from pom.xml, start again and the program works.
My resource file is in the standard location src/main/resources/fx/gui.fxml. This location is also given in the pom file:
<build>
<sourceDirectory>${project.basedir}/src/main/java</sourceDirectory>
<testSourceDirectory>${project.basedir}/src/test/java</testSourceDirectory>
<resources>
<resource>
<directory>${project.basedir}/src/main/resources</directory>
</resource>
</resources>
</build>
Please help me understand what's going on. Do I need pom packaging, and how can resources be loaded with it?
Looks like you have your source code in your parent pom.
A parent pom (with sub-modules) must have packaging as pom and cannot have java source code. See this question
You should move your code in a new sub-module.
The project or module which contains the source code, has to have the packaging as jar/war as per your requirement. It can not be packaging as pom. Generally pom packaging is used with parent module when you have multimodule project structure and the submodules would have packaging as jar/war. So in your case, if you have multimodule project structure, your parent packaging would be "pom" and all submodules(contain source code) must have jar/war. Note : Your parent module should not have source code, if it is then move your source code to submodule. Multimodule project structure is basically used where there are common dependencies and artifacts can be used in multiple submodules so that duplication can be removed.
Like below.
parent pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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>org.abc.test</groupId>
<artifactId>testartifact</artifactId>
<version>1.0.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>1.8</java.version>
</properties>
<modules>
<module>rest-services</module>
</modules>
</project>
Submodule 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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.abc.test</groupId>
<artifactId>testartifact</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>rest-services</artifactId>
<name>rest-services</name>
<dependencies>
<dependency>
</dependency>
</dependencies>
</project>

Basic maven question: Does maven transitively install dependencies?

I've looked here https://blog.packagecloud.io/eng/2017/03/09/how-does-a-maven-repository-work/ and that does seem to be the case.
However, I tried to experiment with mvn install and I'm not sure if it's worked as expected. Here's what I did
(1) I created a lib.
(2) Ran mvn install from the command line
(3) Copied the path of my newly created jar
(4) Opened a new maven project, stuck the path into my pom.xml
I'm able to reuse my library methods, BUT: one of my library methods returns a TransportClient which is part of the elasticsearch api. Using intellij inside my new project, it seems like I don't have elasticsearch even though I'm referencing the jar.
Is this expected? I was expecting it to have transitively installed elasticsearch when it referenced my jar.
I'd love a pointer or two in the right direction, I'm completely new to this. :)
My pom.xml for the lib that uses elasticsearch as dependency.
<?xml version="1.0" encoding="UTF-8"?>
<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>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<groupId>estutorial</groupId>
<artifactId>estutorial</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.4.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>transport</artifactId>
<version>6.4.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.11.1</version>
</dependency>
</dependencies>
</project>
My pom.xml for the new maven project that tries to reference the lib for the above pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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>
<properties>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
</properties>
<groupId>sth</groupId>
<artifactId>sth</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<!-- https://mvnrepository.com/artifact/org.projectlombok/lombok -->
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.4</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>estutorial</groupId>
<artifactId>estutorial</artifactId>
<version>1.0</version>
<scope>system</scope>
<systemPath>/home/dell/.m2/repository/estutorial/estutorial/1.0-SNAPSHOT/estutorial-1.0-SNAPSHOT.jar
</systemPath>
</dependency>
</dependencies>
</project>
So, if I understand your steps, your dependency declaration in your referencing application uses a direct classpath to the jar file in your local repository? If so, this is unusual. You shouldn't need to know direct file locations for any of your dependencies of a Maven project. What you should be doing.
In the referenced project (that which requires the Elasticsearch library), it's pom.xml file would defined the elasticsearch dependency itself. This should follow maven standards for dependency declaration (groupId, artifactId and artifactVerion). If you don't have the elasticsearch artifact, maven will attempt to find it and store it in your local repository. You shouldn't have to have any path in your pom.xml file.
When you install the referenced project, it will install into your local repostory both the JAR file and the pom.xml.
In the referencing project, you should define the dependency to your referenced artifact in it's pom file. Same format: groupId, artifactId and artifactVersion. You shouldn't need to provide a specific path. What maven will do is find your referenced jar, but also use the installed POM.xml file for the referenced jar to find the transitive dependencies and include them in your classpath.
From what you've described, your dependency declarations aren't correct. If you can provide your POM file more details can be provided. Otherwise, review the maven intro to dependencies.
No. mvn install is a nearly useless command. It stuffs a jar file into your local repository, for subsequent use by other maven builds. You use the term 'path'. If you run mvn install:install-file to put a jar into your local repo under some coordinates, you can reference those coordinates from another pom; but it will generally lead to future problems as compare to deploying the jar into a proper repository manager.

Creating a object for a class which is in jar

I have two projects. Project B is maven dependency in project A.I have to use a pojo class of project B in project A
Note:- I don't want to add the jar in build properties because in production it will be a issue
Its simple, you can packaging the project A as a jar and add this dependency as a provided scope in project B.
For example:
pom.xml (Project A):
<project>
...
<groupId>com.myproject.easy</groupId>
<artifactId>commom-lib</artifactId>
<version>0.1</version>
<modelVersion>4.0.0</modelVersion>
<packaging>jar</packaging>
</project>
pom.xml (Project B - Needs to use some Pojo Class from Project A):
<project>
...
<dependencies>
<dependency>
<groupId>com.myproject.easy</groupId>
<artifactId>commom-lib</artifactId>
<version>0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
</project>
It won't throw exception on your application server, cause provided scope means the jar used as a dependency in project B going to be on classpath during runtime.

Maven - Transitive dependencies are not resolved for artifact deployed on Artifactory

I have two projects - A and B, where A is dependent on B. I package B as jar and deploy it on a maven server (artifactory), and then include that jar as a normal dependency on project A in pom file. jar file of B shows up in the Maven Dependencies of project A, but dependencies of project B are not shown in dependency hierarchy. It is causing class not found exception for dependencies of B.
However, my project A and B and in same eclipse workspace. When I open project B, project A starts referencing the project B from the workspace instead of remote repository and everything works well.
This question - Maven. Transitive dependencies was closest to my problem, but dependencies of my project B are NOT optional.
Whats going wrong here?
POM for project B
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.myapp</groupId>
<artifactId>utils</artifactId>
<version>1.0.0-RELEASE</version>
<packaging>jar</packaging>
<name>utils</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<!-- Following doesn't get added to project A -->
<dependencies>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
</dependencies>
</project>
POM for project A
<?xml version="1.0" encoding="UTF-8"?>
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.myapp</groupId>
<artifactId>core-app</artifactId>
<version>1.0.0-RELEASE</version>
<packaging>jar</packaging>
<name>core-app</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>com.myapp</groupId>
<artifactId>utils</artifactId>
<version>1.0.0-RELEASE</version>
</dependency>
</dependencies>
</project>
I am using maven quickstart archetype. Project structure of my projects is (which I package as jar):
project-name
src/main/java
src/test/java
pom.xml
To successfully resolve transitive dependencies, project B's jar and pom.xml must be accessible in the Maven repository. When deploying artifacts to a remote repository, be sure both the jar and pom.xml are deployed and available for download.
With the requisite files deployed to the remote repository, use the command line to build project A. Specify a build Maven target to trigger the downloading of all dependencies into the local Maven repository. Something like mvn compile or mvn package will trigger the downloads and successfully build project A.
Once, project B's jar and pom.xml are in the local Maven repository, update the Maven projects in Eclipse and they will rebuild and resolve the dependencies correctly.

Categories