How to prevent Maven project from downloading specific dependencies? - java

I wanted to remove all direct / transitive dependencies using older log4J version, particularly version 1.27.1 from my project.
I have made sure that my project's pom.xml and lib folder does not contain log4J 1.27.1.
However, whenever the project builds into a .war file, there are some other libraries in my project which is still using log4j 1.27.1 resulting in log4j-1.27.1.jar being downloaded into the build for deployment.
Is there any means we can force the Maven project by preventing it from downloading a specific dependency? For example, by adding some
configurations to the pom.xml file?
Edit 1: Understand that one way to do it is to do exclusions in the pom.xml file. But that requires me to explicitly mention all the artifacts . Is there anyway to do it as though I am stating "hey, I don't want to see log4j-1.27.1.jar downloaded in my Maven project at all, be it any artifact is depending on it" ?

Use the enforcer plugin to ban all unsecure log4j versions. Any use will fail your build and tell you where it's being used, allowing you to overwrite the log4j dependency with a secure version or exclude it altogether.
From Gunnar Morling's gist (warning: update the log4j minimum version here to the log4j version with no vulnerabilities reported):
<!-- plug-in configuration to put into your parent POM for avoiding any usages of
outdated log4j2 versions, some of which are subject to the RCE CVE-2021-44228
("Log4Shell"), CVE-2021-45046, and CVE-2021-45105. Make sure to check for the
latest version of log4j2 at
https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-core -->
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<id>ban-bad-log4j-versions</id>
<phase>validate</phase>
<goals>
<goal>enforce</goal>
</goals>
<configuration>
<rules>
<bannedDependencies>
<excludes>
<exclude>org.apache.logging.log4j:log4j-core:(,2.17.0)</exclude>
</excludes>
</bannedDependencies>
</rules>
<fail>true</fail>
</configuration>
</execution>
</executions>
</plugin>

Related

Create a jar artifact with minimal pom / parent pom.xml / flattened / regenerated pom [maven]

I have a multimodule maven project and distribute the resulting jar file to different parties. Part of the jar file is the pom.xml file (in META-INF/maven/.../pom.xml).
The problem with that is, that the parent pom.xml is missing which contains a complete list of the dependencies and the necessary dependency-versions etc.
So I tried several things:
Solution 1
I added the effective pom to the jar file
Problem
the pom file is way too big, with too much information (partly internal, local etc)
Solution 2
I combined two plugins and managed to additionally add the parent pom.xml file to the jar.
Problem
This is way better than S1 however the parent pom again contains a (grand)parent and also tags like <scm> which are internal and could & should not be handed to the outside world
Now I wanted to start to manipulate the parent pom and remove some parts etc. However there must be a better solution and others who have the same problem?
What I need is (e.g) a plugin which creates a clean "releasable" pom.xml file with only the dependencies (and of course artifact, groupid, version) and can then be imported by external parties into their repo without any conflicts. Is that possible?
The only thing remotely related is the eclipse tycho pom generator plugin. It is however eclipse specific...
The flatten-maven-plugin is exactly what I needed! Thanks to khmarbaise
I use the following configuration and the pom is looking beautiful :-)
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.2.2</version>
<configuration>
<pomElements>
<repositories>flatten</repositories>
</pomElements>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>

How to generate separate jar files for application, source, and documentation (for central.sonatype.org)

Sonatype has a repository that I want to deploy a jar file to, and they ask for separate files for application, sources, and javadocs:
Example:
example-application-1.4.7.pom
example-application-1.4.7.jar
example-application-1.4.7-sources.jar
example-application-1.4.7-javadoc.jar
In Scala SBT, I have a command called "package" that generates the jar file for the project, but that only generates "example-application-1.4.7.jar".
Question: What should I do to generate the other two jar files?
In Maven, in order to get the additional -sources and -javadoc artifacts, add to your POM file the following:
<build>
<plugins>
<!-- additional plugin configurations, if any.. -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Note the snippet above:
We are invoking the Maven Source Plugin to create an additional jar files for sources
We are invoking the Maven Javadoc Plugin to create an additional jar files for javadoc
Executing
mvn clean package
You will find these two additional jars in the target folder.
The .pom file instead is generated during the install phase, but it is not placed under the target folder. Basically, it is a copy of your pom.xml file, with a different extension and used by Maven during the dependency mediation process to check which transitive dependencies are required by the concerned artifact.
Executing
mvn clean install
Maven will install the artifact in your local cache (in your machine), under path_to_cache/.m2/repository/your_groupId/your_artifactId/your_version/. In this folder, you will also find the .pom file, which normally you don't need to distribute (it is created automatically by Maven).
Further note: you probably don't want to generate these additional jar files at each and every build, so to speed up normal builds and have them only on demand, you could wrap the snippet above in a Maven profile.
You can achieve this by removing the snippet above from your build section and add a further section at the end of your pom:
<profiles>
<profile>
<id>prepare-distribution</id>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-source-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-javadoc-plugin</artifactId>
<version>2.10.3</version>
<executions>
<execution>
<goals>
<goal>jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
</profiles>
So that normal builds would not create these jars anymore, but when executing the following:
mvn clean install -Pprepare-distribution
You would instead get them back. the -P option is actually activating on demand the profile defined with the id prepare-distribution.
With Maven 3 a default profile already comes as part of the super pom which perform exactly the same actions (sources and javadoc artifact), hence no need to add anything to your existing project. Simply run:
mvn clean install -Prelease-profile
Or, to activate it via a property
mvn clean install -DperformRelease=true
However, as also specified in the super pom, this profile may be removed in future releases (although there since first Maven 3 version till version 3.3.9 so far)
NOTE: The release profile will be removed from future versions of the super POM
The main reason behind this warning is most probably to push for the usage of the Maven Release Plugin, which indirectly makes use of this profile via the useReleaseProfile option of the release:perform goal.
As highlighted by comments, if you are not familiar with maven (especially via console) I would definitely recommend to
Go through the official Maven in 5 minutes documentation for a quick but worthy look.
Play with Maven from the command line, is there where Maven gives you its best. IDE integrations are great, but command line is the real turning point.
Then play with the POM customization above, to get familiar with some concepts and behaviors, first directly as part of your default build, then moved to a profile.
Then, and only then, move to the Maven Release Plugin usage. I recommend it as last step because you would already have acquired more confidence and understanding and see it as less magic and more reasonable approach.

How maven jar project works when it is packaged to jar?

I am new to maven. I have created a maven project which will be packaged to JAR. I did clean package then jar is created. When i extracted the same jar, i could not see any dependencies (jars) i added in pom.xml inside the packaged jar. If i give this jar to third party clients how will the code work without any dependent jars ? Please help me how maven manages the jars?
Thanks!
Maven handles dependencies based on how you configure the dependency plugin.
See this reference for a simple example of how to do this.
In this example, the following code configures where your dependencies will end up:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.5.1</version>
<executions>
<execution>
<id>copy-dependencies</id>
<phase>package</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeGroupIds>log4j</includeGroupIds>
<outputDirectory>${project.build.directory}/dependency-jars/</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
Then this code sets up the classpath for your main jar, which will allow anyone running it to find these dependencies
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
<mainClass>com.mkyong.core.App</mainClass>
<classpathPrefix>dependency-jars/</classpathPrefix>
</manifest>
</archive>
</configuration>
</plugin>
Your other option would be to create a single jar, with all dependencies included, by following this example here
You could distribute the jar and the POM file if you want to try and provide your users with the files in that manner, but they'd need to be able to access your Maven repository where those dependencies are kept.
Core maven doesn't handle this. Maven is a build tool, its work is to build an artifact (a jar in your case). Dependencies you define in your module's pom.xml file are needed to get the code compiled. You'll need maven plugins to do so.
Now, you're asking not about the build, but the distribution of your compiled binaries.
If I understand it should be a lot of jars (your and your dependencies). Alternatively you may distribute the code as a jar + dependencies inside.
Example:
A first case:
If your code resides in module A (say, the code is in packages org.a.*) and depends on some thirdparty (say, log4j, whose classes reside in org.apache.log4j) than you can expect that you jar will only contain the classes of module a and you expect that the log4j will be added by the user of your module automatically (The first case).
A second case:
module a.jar will contain both org.a.* and org.apache.log4j.* classes, everything in the same module.
In general the first approach is more "healthy" and in this case you shouldn't do anything in maven. Maybe your distribution tool/documentation should contain this information.
If someone uses the module a in his/her code like a thirdparty (if you develop a framework or something) and if his/her project is mavenized, than the fact you've defined a dependency on log4j will make the maven to download the log4j as well as your a.jar (In maven notation, this is called "transitive dependencies").
If you're interested in the second case (this can be relevant if you define some "client application", like "jndi client for some server" for example) you might want to take a look on Maven shade plugin
Beware this can lead to dependency hell (what if the application that uses your client also makes use of log4j? what if the log4j-s are of different version)/
Bottom line, you probably want the first approach, think twice before you decide the second approach :)
One more tip, if you just want to download all the dependencies of your module "a" you might want to use maven dependency plugin - type the following in the command prompt
mvn dependency:copy-dependencies
and you'll find all the dependencies in target/dependencies folder
Hope this helps and happy mavening
The simplest solution to the problem is to use the maven-assembly-plugin which can create such jar with dependencies like the following:
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>distro-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
<configuration>
<descriptorRefs>
<descriptorRef>jar-with-dependencies</descriptorRef>
</descriptorRefs>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
Afterwards you can distribute the created jar xyz-1.0-jar-with-dependencies which contains the defined dependencies.
If you need more control on how the resulting artifact is created or if some files needed to be overwritten etc. you might take a deeper look into maven-shade-plugin

How to install Grails plugins through Maven pom.xml?

I'm building a Grails project with Maven, which is the required way of building at my company.
On my local machine, I have installed a Grails plugin in the usual way grails install-plugin foo. Of course, when the project is built by Maven on the build server, it knows nothing about this plugin.
I've seen that the following can be useful for the case where the plugin is available in the plugins directory of Grails:
<plugin>
<groupId>org.grails</groupId>
<artifactId>grails-maven-plugin</artifactId>
<version>1.1</version>
<extensions>true</extensions>
<executions>
<execution>
<id>create plugins folder</id>
<phase>validate</phase>
<goals>
<goal>install-plugin</goal>
</goals>
<configuration>
<pluginUrl>${env.GRAILS_HOME}/plugins/grails-hibernate-1.1.zip</pluginUrl>
</configuration>
</execution>
</executions>
</plugin>
but suppose the plugin was not on the machine at all? What would the pluginUrl be in the case where Maven or Grails will need to go to the internet to find the plugin?
Edit:
I've found that the pluginName and pluginVersion tags are useful, and I've added the following execution to the grails-maven-plugin:
<execution>
<id>Hibernate plugin</id>
<phase>validate</phase>
<goals>
<goal>install-plugin</goal>
</goals>
<configuration>
<pluginName>hibernate</pluginName>
<pluginVersion>1.3.7</pluginVersion>
</configuration>
</execution>
This almost works. If I check out my code in a new directory, and delete the contents of my plugins directories, Maven is able to build the project, successfully finding all plugin dependencies.
Yet it still doesn't work on the build server. Any ideas?
Try adding this dependency to your pom.xml
<dependency>
<groupId>org.grails.plugins</groupId>
<artifactId>fields</artifactId>
<version>1.3</version>
<type>zip</type>
<scope>runtime</scope>
</dependency>
when you recompile your App maven will download and install the plugin
You need to use Grails Maven plugin, so the pom will get updated with changes to the plugin list. Have a look at Grails documentation describing Maven integration, especially the section on "mavenizing" the project.

Maven: My project has a dependency that is NOT in any maven repo (system scope). Does it get included in my final jar?

So my maven project depends on a jar that is NOT in any maven repository. Therefore I need to use the system scope to include this local file in my maven classpath. Question is: When I build my final jar to distribute my library, I do need to somehow include that dependency with it. The classes can be extracted and then bundled with my library classes OR the jar can be included somehow inside my library jar. I am not even sure the latter (jar inside jar) is possible in Java.
So how should I approach this problem? Will Maven take my system scope dependency and take care of that for me? What should I do?
Thanks!
No, it will not. These dependencies are only for compilation and transitive dependency resolution. Your library consumers should have the jar too.
However, you could use the Assembly Plugin to repackage the jar's classes into your artifact.
Or, the standard approach: then you will publish your artifact, you will create a public repository for deployment. You also could deploy the jar in it.
UDPATE: adding example for the shade plugin (instead of the Assembly)
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.4</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<artifactSet>
<includes>
<include>legacy-jar.groupId:legacyJar</include>
</includes>
</artifactSet>
</configuration>
</execution>
</executions>
</plugin>
There is a second option. Put the jar into your repository as described here:
http://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html
and to your online repository
http://maven.apache.org/guides/mini/guide-3rd-party-jars-remote.html
give your client access to your repository and use your legacy.jar as normal dependency.
It depends a little bit what kind of library do you have. With this way you don't have problems with version conflicts of your legacy.jar in the environment of your customer.
Could this be what you are looking for, http://maven.40175.n5.nabble.com/How-to-specify-local-dependency-in-maven2-td103415.html.

Categories