Compile subset of dependencies into my jar file - java

I have a project with multiple Maven dependencies and want to minimize the size of my compiled JAR. Right now it seems that IntelliJ is compiling all of the external dependency source files into my JAR even though I only use a small subset of their functionalities.
I would like to include only the files that are directly used by my module.

You can declare a dependency as optional:
<dependencies>
<dependency>
<groupId>sample.dependency</groupId>
<artifactId>small-dependency</artifactId>
<version>1.0</version> <!-- Will be packaged in JAR -->
</dependency>
<dependency>
<groupId>sample.dependency</groupId>
<artifactId>really-big-dependency</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
</dependencies>
Another approach is to use the provided scope. The difference is provided is used if you know the dependency will be included in the classpath of the application that will run your JAR (e.g. a Web or Java EE container):
<dependencies>
<dependency>
<groupId>sample.dependency</groupId>
<artifactId>small-dependency</artifactId>
<version>1.0</version> <!-- Will be packaged in JAR -->
</dependency>
<dependency>
<groupId>sample.dependency</groupId>
<artifactId>really-big-dependency</artifactId>
<version>1.0</version>
<scope>provided</scope> <!-- Will not be packaged in JAR, needs to be provided in classpath at runtime -->
</dependency>
</dependencies>
Sources:
Maven - Optional Dependencies and Dependency Exclusions
Maven - Dependency Scope

Related

Spring-Boot: How can we remove some dependencies from Effective pom?

I am using spring-boot 2.0.3.RELEASE. When I am clicking on "show Effective POM" option by using IntelliJ IDEA, it loads Effective POM. And there I can see a few dependencies that my client don't want to have at there side.
Is there any way to tell Maven not to include these dependencies? How can we exclude dependencies from effective poms?
Maven provides a way to exclude dependencies with the exclude tag
Here is an example taken from the documentation website https://maven.apache.org/guides/introduction/introduction-to-optional-and-excludes-dependencies.html
<dependencies>
<dependency>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0</version>
<scope>compile</scope>
<exclusions>
<exclusion> <!-- declare the exclusion here -->
<groupId>sample.ProjectB</groupId>
<artifactId>Project-B</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
The idea is to locate parent dependencie from where you are getting deps you don't want and add an exclusion tag.
If they are needed in runtime you can specify the scope to provided
<dependency>
<groupId>sample.ProjectA</groupId>
<artifactId>Project-A</artifactId>
<version>1.0</version>
<scope>provided</scope>
</dependency>
That will tell maven to use the deps to compile but not no include them in the target package, and they will be provided in the production environment by the JVM executing the code.
Hope this helps

Java 8 to Java 9 migration optimal way for mavenised project

I am migrating my project from Java 8 to Java 9. My project is mavenised. Now for migrating to Java 9, I am planning on creating a separate module directory where all the required dependency of a module will go.
For doing this through maven the only way I know is to use the copy plugin of maven to copy all the required dependency in the module directory. So after running maven installs for a module, the dependent jars will be copied in repository folder(which is by default) and also copied to this module directory folder.
So there will be a copy of the jars and also hard coding in pom.xml for copying specific dependency in the module directory.
This approach doesn't seem to be clean, is there any way out were automatically maven can read my module-info.java file and copy the required dependency not in the classpath but in the specified directory
Here is my 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>
<parent>
<groupId>com.aa.bb</groupId>
<artifactId>cc</artifactId>
<version>0.0.1</version>
<relativePath>../../pom.xml</relativePath>
</parent>
<artifactId>dd</artifactId>
<name>dd</name>
<groupId>com.aa.cc</groupId>
<version>1.0.0</version>
<properties>
<maven.compiler.source>10</maven.compiler.source>
<maven.compiler.target>10</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.11</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<release>10</release>
<compilerArgs>
<arg>--module-path</arg>
<arg>./moduledir</arg>
</compilerArgs>
</configuration>
<dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>6.2</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</project>
module-info.java
module com.some_module_name {
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
requires org.apache.commons.codec;
requires spring.beans;
requires spring.context;
}
After adding module-info.java to an existing java 10 module and running maven i am getting issue like "package exist is used in module a and module b".
What i believe is while running maven it is looking for module dependency in the .m2/repository and there are loads of jar there as the m2./repository is common for my multiple modules.
So what i was planning to do is create a separate module directory for each module and place the required jar for that module in it which way it even works.
I assume you're making this effort to make sure Maven "does the right thing" regarding the module and class path, i.e. placing direct dependencies of your module on the former and all other dependencies on the latter. If that's so, there's nothing you need to do - from version 3.7 on, the Maven Compiler Plugin does it for you as soon as you add a module-info.java to your src/main/java directory.
You can verify that by running Maven in debug mode with mvn clean compile -X. When you carefully analyze the output, you will see which JARs end up on which path.

Maven - depend on multi-module aggregator project

I've got a multi-module maven project, with a structure like:
projectA-parent
- module-1
- module-2
And I have another project where I want to bring in all the modules in projectA-parent as runtime dependencies (it's a Spring application, and each module in projectA-parent contains Spring services that I want to be able to autowire).
What I'd like to do is something like
<dependency>
<groupId>projectA-group</groupId>
<artifactId>projectA-parent</artifactId>
<scope>runtime</scope>
</dependency>
so that if I add another module to projectA-parent it is automatically brought in as a runtime dependency (i.e., I don't want to have to add each new module as a dependency in my Spring application as I add them). Is such a thing possible?
You will have to use
<dependencies>
<dependency>
<groupId>projectA-parent-groupId</groupId>
<artifactId>projectA-parent-artifactId</artifactId>
<type>pom</type>
</dependency>
</dependencies>
This will transitively add all dependencies declared in com.my:commons-deps to your current POM.
Using
<dependencyManagement>
<dependencies>
<dependency>
<groupId>...</groupId>
<artifactId>...</artifactId>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
works as a simple 'include' of artifacts versions in your dependency management. Thus, it won't add any dependency in your project.
UPDATE:
Another aprroach would be to use a BOM (Bill of Materials). Check this link for the usage of BOM. It is hidden somewhere at the bottom.
You can create a BOM that lists all your modules as dependencies and then you can include the BOM into your pom.xml like this:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>your_bom_group_id</groupId>
<artifactId>your_bom_artifact_id</artifactId>
<version>you_bom_version</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
I guess I'd just add another module that referred to the other modules in your project, e.g.:
projectA-parent
- module-1
- module-2
- module-deps
with module-deps as a jar or pom that depends on module-1 and module-2.
You'll have to update module-deps as you add more modules, but at least it's only in one place.

Maven project not working with other projects

I have a Java project that uses http common-logging.jar, httpclient-4.4-alpha1.jar, httpcore-4.4-alpha1.jar, httpmime-4.4-alpha1.jar. I converted my Java project to a Maven project and added the above jar files to my pom.xml. This is my XML file:
<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>MyClient</groupId>
<artifactId>MyClient</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.4</version>
</dependency>
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>org.json</groupId>
<artifactId>json</artifactId>
<version>20090211</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src</sourceDirectory>
</build>
</project>
I did Run As > Maven Clean and Run As > Maven Install and it built the project. It generated a jar file in the target folder called MyClient-0.0.1-SNAPSHOT.jar. I added this jar to another project that uses this jar file (to make HTTP calls) and I get the error
Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError: org/apache/http/entity/mime/content/ContentBody
Can someone tell me what is causing this? This seems to like a dependancy issue? Can someone tell me what I am doing wrong?
Maven will not automatically package these dependencies into your jar file. These files are only used to compile the code and build the jar file itself. As the documentation for the jar plugin states:
The resulting 'jar' file contains the compiled java class files as well as the files from src/main/resources.
You have a number of options:
Package your MyClient jar file as a "fat" jar using something like the maven shade plugin
Add the dependencies from the MyClient POM to the POM in the project that uses the jar file.
Use a maven multi-module project with a common parent so that transitive dependencies such as this are resolved between the projects.
Manually copy all of those jar files over when you copy the MyClient jar file

How to avoid duplication of derived dependencies in EAR/WAR with Maven?

To make the example as simple as possible, let's say I have classic Java EE 5 application.
Let's say I use x-lib in EAR module, and x-lib uses commons-io.
Also I use y-lib in WAR module, and y-lib uses commons-io too.
The EAR is set as provided in WAR.
The result I get is:
- app.ear
/lib
x-lib.jar
commons-io.jar
/app.war
/WEB-INF
/lib
y-lib.jar
commons-io.jar
I don't want commons-io to be packaged in app.war/WEB-INF/lib once it is packaged already in app.ear/lib.
In my war's pom.xml I have:
<dependency>
<groupId>my.group</groupId>
<artifactId>app</artifactId>
<type>ejb</artifact>
<scope>provided</scope>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>some.other.group</groupId>
<artifactId>y-lib</artifactId> <!-- This loads commons-io as compile dependency -->
<version>1.2.3</version>
</dependency>
Is there a way to tell maven that I want everything that is provided along with app ejb dependency should be set to provided and not included in WAR?
I do not want to track all those duplicated JARs and set them as provided or exclude explicitly one by one.
EDIT
I am aware of skinny-war solution. However I don't like the drwaback of duplicating dependencies in WAR and EAR. Maybe you know how to overcome this.
From the version 2.7 of the maven-ear-plugin, you can use SkinnyWars.
See here for specs: https://maven.apache.org/plugins/maven-ear-plugin/examples/skinny-wars.html
Basically, you can add the skinnyWars tag to your plugin configuration:
<plugin>
<artifactId>maven-ear-plugin</artifactId>
<version>2.10.1</version>
<configuration>
<defaultLibBundleDir>lib/</defaultLibBundleDir>
<skinnyWars>true</skinnyWars>
</configuration>
</plugin>
That SHOULD be automatic if you add the EJB module as a provided dependency to your war module and let all the dependencies of that EJB come transitively. So don't add the overlapping dependencies to your war pom, just let Maven deal with it.
Is there a way to tell maven that I want everything that is provided
along with app ejb dependency should be set to provided and not
included in WAR?
In this case a think the best option by now is to use wilcards in dependency exclusion.
It is only supported in maven 3.
Below is an example of how to declare the ejbmodule dependency from a war project:
<dependency>
<groupId>ejbModuleGroupId</groupId>
<artifactId>ejbModuleArtifactId</artifactId>
<scope>provided</scope>
<type>ejb</type>
<exclusions>
<exclusion>
<groupId>*</groupId>
<artifactId>*</artifactId>
</exclusion>
</exclusions>
</dependency>
You can use this same strategy when declaring a dependency to a ejb-client.
Depending on your maven 3 version, you'll get a nasty warning, but in newer versions it must be fine.
You need to add an exclusion like this:
<dependency>
<groupId>some.other.group</groupId>
<artifactId>y-lib</artifactId>
<version>1.2.3</version>
<exclusions>
<exclusion>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
</exclusion>
</exclusions>
</dependency>

Categories

Resources