Maven Shade - add local JAR file [duplicate] - java

I already found an answer here on Stack Overflow how to include a 3rd party JAR in a project without installing it to a "local repository":
Can I add jars to maven 2 build classpath without installing them?
But, when I use the Maven Shade Plugin to create a JAR that includes all the dependencies of the project as well, the 3rd party JAR is not included automatically.
How can I make the Maven Shade Plugin add such a 3rd party JAR in to the shaded JAR?
As per the answer gotten, I made it work. What I did was, added this snippet to the beginning of my pom.xml:
<repositories>
<repository>
<id>repo</id>
<url>file://${basedir}/repo</url>
</repository>
</repositories>
Then added a dependency for my project, also to pom.xml:
<dependencies>
<dependency>
<groupId>dummy</groupId>
<artifactId>dummy</artifactId>
<version>0.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
And then ran a command line to add a package to 'repo':
mvn org.apache.maven.plugins:maven-install-plugin:2.3.1:install-file
-Dfile=<my-jar>.jar -DgroupId=dummy -DartifactId=dummy
-Dversion=0.0.0 -Dpackaging=jar -DlocalRepositoryPath=`pwd`/repo/
(Not sure if the repo path needs to be a full path, but didn't want to take chances.)
The contents of the repo subdirectory is now:
repo/dummy/dummy/0.0.0/dummy-0.0.0.jar
repo/dummy/dummy/0.0.0/dummy-0.0.0.pom
repo/dummy/dummy/maven-metadata-local.xml
Now I can check this in to version control, and have no local or remote dependencies.

But, when I use the Maven Shade Plugin to create a JAR that includes all the dependencies of the project as well, the 3rd party JAR is not included automatically.
Yes, because the system scoped dependencies are assumed to be always present (this is exactly what the system scope is about) so they won't be included. People actually don't understand what system scope dependencies are, they just keep abusing them (yes, this is abuse), and then get side effects and wonder why (as Brian pointed out in his answer).
I already wrote many, many, really many times about this here on SO and in 99% of the cases, system scoped dependencies should be avoided. And I'll repeat what the Dependency Scopes mini guide says one more time:
system: This dependency is required in some phase of your project's lifecycle, but is system-specific. Use of this scope is discouraged: This is considered an "advanced" kind of feature and should only be used when you truly understand all the ramifications of its use, which can be extremely hard if not actually impossible to quantify. This scope by definition renders your build non-portable. It may be necessary in certain edge cases. The system scope includes the <systemPath> element which points to the physical location of this dependency on the local machine. It is thus used to refer to some artifact expected to be present on the given local machine an not in a repository; and whose path may vary machine-to-machine. The systemPath element can refer to environment variables in its path: ${JAVA_HOME} for instance.
So, instead of using the system scope, either:
Add your libraries to your local repository via install:install-file. This is a quick and dirty way to get things working, it might be an option if you're alone but it makes your build non portable.
Install and run an "enterprise repository" like Nexus, Archiva, or Artifactory and add your libraries via deploy:deploy-file. This is the ideal scenario.
Setup a file based repository as described in this previous answer and put your libraries in there. This is the best compromise if you don't have a corporate repository but need to work as a team and don't want to sacrifice portability.
Please, stop using the system scope.

The Maven addjars plugin solves this problem - see
http://code.google.com/p/addjars-maven-plugin/wiki/UsagePage

Used <resources> to include my lib with all jars. i.e:
<build>
<resources>
<resource>
<directory>${project.basedir}</directory>
<includes>
<include>lib/*.jar</include>
</includes>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.3</version>
<configuration>
<createDependencyReducedPom>false</createDependencyReducedPom>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>

If you only need a quick and dirty solution, you can add the content of the extracted jar file to your src/main/resource directory.

Related

Dynamically package JAR to specific directory for any given Java project using Maven and IntelliJ IDEA

I'm making Minecraft server (Spigot) plugins using IntelliJ IDEA and Maven. I want a button which works on all my Spigot projects (not necessarily automatically determining these projects, though that would also be useful) which packages my plugin directly to my testing server's 'plugins' folder then starts the server.
There are two methods I've thought of to accomplish this, but neither of them work due to limitations with IntelliJ and Maven.
My first idea was to write a batch file which takes the path to the packaged JAR as a parameter, copies the JAR from that path to the 'plugins' folder then starts the testing server.
move "%*" "C:\path\to\my\spigotTestingServer\plugins"
call "C:\path\to\my\spigotTestingServer\startServer.bat"
Then, in the 'Script parameters' for my run configuration, I would reference Maven properties (${project.build.directory}\${project.artifactId}-${project.version}) to obtain this path. However, IntelliJ doesn't seem to allow you to reference Maven properties in any run configuration settings.
My second idea was to modify the package directory directly inside my POM using Maven plugins. However, this means I'd still have to copy this code between projects and it would pollute my Git commits with a path only effective with my filesystem.
Now, I found out Maven has 'build profiles' which could potentially be a solution to this, so I wrote this 'settings.xml'
<settings xmlns="http://maven.apache.org/SETTINGS/1.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.0.0 https://maven.apache.org/xsd/settings-1.0.0.xsd">
<profiles>
<profile>
<id>spigotTestingServer</id>
<build>
<finalName>${project.name}</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.3.1</version>
<configuration>
<outputDirectory>C:/path/to/my/spigotTestingServer/plugins/</outputDirectory>
</configuration>
</plugin>
</plugins>
</build>
</profile>
</profiles>
</settings>
I could then use this for all my Spigot projects by adding '-P spigotTestingServer' to the run configuration.
However, Maven doesn't allow profiles defined in settings.xml to include anything under 'build' including build plugins, which is exactly what I need to modify the package directory.
So, at this point, I'm stuck. Is there any way to get around the issues I've faced so far or are there any alternative solutions to packaging my JAR directly to my Spigot testing server's 'plugins' folder?

Installing local jar to automated pipeline and sonarqube: how to do it with no command and no systempath?

I am developing a project which needs a client personal jar, and it needs to be deployed on a pipeline of tools which are out of our control (sadly). One of the tools in this pipeline is sonarqube.
To build and deploy we have to use maven.
I put the jar into a folder of the project, and tried various way to actually make it work.
The first (working) way was to have it as a system with a systemPath to the folder of the project. It compiled, worked and everything, but sonarqube apparently hates systemPath and made us take it away.
After a tiny bit of searching, we added to our pom a maven-install-plugin, bounded an install-file to validate phase and configured to generate the dependency. This seems to work on local if I first run mvn validate and THEN mvn clean package. Otherwise, it tries to look for the jar on the main repository and fails. If I comment the tag and leave only the plugin active, I noticed it executes the plugin, installs the jar to local repository, but build fails due to not resolving packages and classes inside the jar. If I now put the tag in, everything works, because it now find the jar in the repository.
While this solution works, it doesnt suite me because the repository will be emptied every once in a while, and to restart everything I would need to commits, one knownligly failing, just to install the jar.
I tried addind a tag instead, pointing to a project dir where I would store the necessary jar, and that works just fine on my PC, but utterly fails on the pipeline, looking at main repository only (I guess it is some configuration on the pipeline, but cant really tell, being outside my control)
Was actually able to do it with maven install plugin,
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-install-plugin</artifactId>
<configuration>
<groupId>com.exmaple.stuff</groupId>
<artifactId>ClientJar</artifactId>
<version>1.0</version>
<packaging>jar</packaging>
<file>${project.basedir}/src/main/resources/ClientJar-1.0.jar</file>
<generatePom>true</generatePom>
</configuration>
<executions>
<execution>
<id>install-client-jar</id>
<phase>validate</phase>
<goals>
<goal>install-file</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
And having dependency
<!-- Client jar -->
<dependency>
<groupId>example</groupId>
<artifactId>ClientJar</artifactId>
<version>1.0</version>
</dependency>
If you notice, the tricky part here was actually that groupId is different between the dependency and the plugin declaration. I do not know if this difference is due to the configuration of their artifactory server, but it seems to work locally too.
Also it is needed to explicitly run mvn validate in the pipeline

Why is Maven including multiple versions of the same dependency?

I have a Maven java web app (.WAR) project that includes several libraries, including the Wicket libraries (but I don't think the problem is wicket itself, but rather with maven).
Here's the problem: even tho I only include Wicket 6.20.0, the resulting .WAR contains two copies of the Wicket libraries: 6.20.0 and 6.18.0, as you can see in this screenshot:
Thinking of some conflicting imports I printed the dependency tree using the:
mvn dependency:tree
commnad... but there is no mention of Wicket 6.18.0 in the dependency tree! I also double-checked using Eclipse's "dependency hierarchy" view and I can confirm there's no trace of that import.
I even did a search for string "6.18.0" across the entire workspace with Eclipse, but it's nowhere to be found!
How can I find out what is causing the inclusion of that duplicate version of the library?
Maven doesn't work in this way.
The resolution of more than one dependency with the same artifactId and groupId but with a different version will result to a single dependency (the version used is no determinist).
The presence of two artifacts with the same artifactId and groupId but with two distinct versions in a same lib folder of the WAR is probably related to one of these :
you don't execute mvn clean package but only mvn package.
your use a bugged version of the Maven war plugin. Try to update it to check that.
you have a Maven plugin that copies Wicket jars 6.18.0 in the WEB-INF/lib folder of the target folder during the build of the component.
the maven WAR project you are building has as dependency an artifact of type WAR. In this case, the dependencies of the WAR dependency are so overlaid in the WAR project that you are building.
An interesting Maven issue about duplicated JAR because of WAR dependencies :
JARs with different versions can be in WEB-INF/lib with war as dependencies
Your answer and your comment indicate that actually you have a WAR dependency in your build.
Unfortunately, there is not really a good and long term effective solution to bypass this limitation.
As said in my comment, using the packagingExcludes property of the maven war plugin is a valid workaround for the actual issue :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<!-- ... -->
<packagingExcludes>WEB-INF/lib/wicket-*-6.18.0.jar</packagingExcludes>
</configuration>
</plugin>
But beware, using that will do your build less robust through the time.
The day where you update the version of the WAR dependency and that in its new version, it pulls again a different version of wicket, you have still a risk to have duplicate jars with two distinct versions in your built WAR.
Using the overlay feature by specifying the overlay element of the maven-war-plugin is generally better as it focuses on the overlay applied for the war dependency. It fixes the problem early.
As a result, you could define to exclude any wicket JARs from the WAR dependency :
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<version>2.4</version>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<overlays>
<overlay>
<groupId>com.whatever.youlike</groupId>
<artifactId>myArtifact</artifactId>
<excludes>
<exclude>WEB-INF/lib/wicket-*.jar</exclude>
</excludes>
</overlay>
</overlays>
</configuration>
</plugin>
This way is better but this is still a workaround.
The day where the dependency WAR is updated and that it pulls new dependencies (other than Wicket) that are declared in your actual build but with different versions, you may finish with the same kind of issue.
I think that declaring a dependency on a WAR artifact should be done only as we don't have choice.
As poms and projects refactoring are possible, introducing a common JAR dependency which the two WARs depend on and that contains only common sources and resources for the two WARs makes really things simpler.
Well, I figured it out while poking around.
I had a dependency of type "war" in the project:
<dependency>
<groupId>com.whatever.youlike</groupId>
<artifactId>myArtifact</artifactId>
<version>1.0.7-SNAPSHOT</version>
<type>war</type>
</dependency>
Apparently (I wasn't aware of this, my fault here) these type of dependencies will include themselves in the classpath by copying all libs to the main WAR /libs folder, but these will NOT show app in the dependency tree / dependency hierarchy.
I solved by configuring an explicit exclusion in the WAR plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-war-plugin</artifactId>
<version>2.4</version>
<configuration>
<!-- ... -->
<packagingExcludes>WEB-INF/lib/wicket-*-6.18.0.jar</packagingExcludes>
</configuration>
</plugin>
Use clean install and the double dependency will probably be gone.
Because other libs can use same libs but different version or you tried different version and didn't make mvn clean
The command mvn dependency:tree is telling you the correct information - what you are looking at here is an eclipse / build issue.
Clear out all the target and build areas in your project. If need be, check it out from source control to a new folder.
Alternatively you can build your project in IntelliJ IDEA, and see if you get the correct dependencies (most likely you will).

Smarter Native Dependency Handling with Maven

I'm currently in the midst of converting a large multi-module project (~100 sub-modules) to use Maven. Currently we use Ant + Ivy.
So far no major issues have cropped up and I'm comfortable that Maven is still a good fit. However, I wonder if there is a better way to handle native dependencies.
So far I have come to the following conclusions.
It's best to install each native dependency into the maven repo either as a standalone library or an archived package containing multiple dependencies.
Rather than get lost in declaring each and every dependency with the Maven dependency plugin, I opted to give each a classifier (e.g. natives-win32) and use the following in the parent POM:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-dependency-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<id>copy</id>
<phase>compile</phase>
<goals>
<goal>copy-dependencies</goal>
</goals>
<configuration>
<includeScope>runtime</includeScope>
<includeClassifiers>natives-win32</includeClassifiers>
<outputDirectory>${project.build.directory}/natives</outputDirectory>
</configuration>
</execution>
</executions>
</plugin>
So far this seems to be a simple all-round solution that doesn't require too much messing about to add new native dependencies. It also offers me a simple all-round solution for managing natives. The only thing I must do is ensure that my /natives/ directory is defined on java.library.path.
One thing that bothers me (a little) about this approach is that all my native dependencies get copied around to each sub-module that expresses a transitive dependency on them, whilst my happy jar libraries are added to the classpath referenced to where they sit in my local repository (no copy required).
Is there no way to be smarter about this and have my natives referenced from withing their repository location (assuming I don't have them archived, i.e. dll). That would save a bunch of unnecessary copying about.
Are there any other potential gotchas' that I should be concerned about with the above approach?
Your snippet shows a goal attached to a build phase, not a dependency. Is the 'copy dependencies' goal in a super pom and inherited by all modules? There's no way to move it only to the modules which are going to be run/packaged as an app?
It could be, that I didn't got it. But why don't you deploy all your native libs into the repository at first. If the native libs are stable and change seldom, That could be done in a seperate reactor.
And afterwards you reference those native dependencies simply via GAV as any other dependency. Also the problem af unnecessary copying is solved by that.
I ended up using the maven natives plugin and dealing with the fact that I have redundant copies of the native libraries around the place. The reason for this was primarily due to the simplicity that the plugin offers and the fact that it also has a related eclipse plugin that sets up natives in developers eclipse environment without intervention.

Release different configurations with Maven

I'm currently migrating our build process to Maven from Ant. Our application is deployed to many different customers, each with a unique set of dependencies and and configuration. I can implement different profiles to model these and build the required wars from them. However this is a process that happens at compile time.
Each release is tagged under SVN as well as uploaded to our internal Nexus repository. I want to be able to take a defined release and reconstruct it based a profile. Is there a way to do something like this? Is there something other than profiles I should be using?
"declare several execution for the war plugin to produce several artifacts (and install/deploy them)" This sounds like this might be the way forward. How would I go about doing this?
This goes a bit against a Maven golden rule (the one main artifact per module rule) but can be done. The One artifact with multiple configurations in Maven blog post describes one way to implement this approach:
I decided to put all the environment
specific configuration in a special
source tree, with the following
structure:
+-src/
+-env/
+-dev/
+-test/
+-prod/
Then I configured the maven-war-plugin
to have three different executions
(the default plus two extra), one for
each environment, producing three
different war files: beer-1.0-dev.war,
beer-1.0-test.war and
beer-1.0-prod.war. Each of these
configurations used the standard
output files from the project and then
copied the content from the
corresponding src/env/ directory on
to the output files, enabling an
override file to be placed in the
corresponding src/env/ directory. It
also supported copying a full tree
structure into the output directory.
Thus if you for instance wanted to
replace the web.xml in test you
simply created the following
directory:
src/env/test/WEB-INF/
and placed your test specific
web.xml in this directory and if you
wanted to override a db.property
file placed in the classpath root
directory for the test environment you
created the following directory:
src/env/test/WEB-INF/classes
and placed your test specific
db.property file in this directory.
I kept the src/main directory
configured for development
environment. The reason for this was
to be able to use the
maven-jetty-plugin without any extra
configuration. Configuration
Below you find the maven-war-plugin
configuration that I used to
accomplish this:
<plugin>
<artifactId>maven-war-plugin</artifactId>
<configuration>
<classifier>prod</classifier>
<webappDirectory>${project.build.directory}/${project.build.finalName}-prod</webappDirectory>
<webResources>
<resource>
<directory>src/env/prod</directory>
</resource>
</webResources>
</configuration>
<executions>
<execution>
<id>package-test</id>
<phase>package</phase>
<configuration>
<classifier>test</classifier>
<webappDirectory>${project.build.directory}/${project.build.finalName}-test</webappDirectory>
<webResources>
<resource>
<directory>src/env/test</directory>
</resource>
</webResources>
</configuration>
<goals>
<goal>war</goal>
</goals>
</execution>
<execution>
<id>package-dev</id>
<phase>package</phase>
<configuration>
<classifier>dev</classifier>
<webappDirectory>${project.build.directory}/${project.build.finalName}-dev</webappDirectory>
<webResources>
<resource>
<directory>src/env/dev</directory>
</resource>
</webResources>
</configuration>
<goals>
<goal>war</goal>
</goals>
</execution>
</executions>
</plugin>
(...) I can define each customer project with profiles but I don't know if there's a way to release them to a repository.
You have several options:
use profiles and run the build several times (create artifacts with a classifier and install/deploy them)
declare several execution for the war plugin to produce several artifacts (and install/deploy them)
use different modules (and maybe war overlays to merge a common part with a specific one)
Or at least a way in Maven to automatically build an artifact with a specified profile from say an SVN tag.
Well, this is doable. But without more details about a particular problem, it's hard to be more precise.
I would take a look at your architecture and see if there is a way to split up your project into multiple projects. One would be the main code base. The other projects would depend on the JAR file produced by the main project and add in their own configuration, dependencies, etc to produce your final artifact.
This would let you version customer specific code independently of each other as well as keeping common code in one place and separate from customer specific stuff.
Have you taken a look at the Maven Assembly plugin?
This plugin allows you to customize how your distribution is assembled - i.e. what format (.tar.gz, .zip, etc), directory structure, etc. I think you should be able to bind several instances of the plugin to the package phase to assemble multiple variations of your output (i.e. the packaging for customer 1, customer2, etc, separately).
The deploy plugin should then automatically handle deploying each of your assembled packages in the target directory to the repository.
I ended up doing something slightly different. We're not storing the releases in our internal repository. Instead we're building using Hudson and a multi-configuration project (one configuration/profile for each customer). This way when a release is made the Hudson job is run to build different wars for all customers. They are then stored on the Hudson server instead of Nexus. Builds for specific versions and customers can also be built at any time from the releases in Nexus. – samblake Mar 16 '11 at 12:32

Categories