New at Maven: Using the shade plugin and 3rd party jars - maven-2

This should be pretty simple, but I can't the around to it. I need to create an uberjar using 3rd party jars. I've already followed these instructions: Including a non-Mavenized dependency so it works with maven-shade-plugin and added them to the local repository. But now what? Every Maven tutorial/example is kinda shady (pun intended) and I just don't know how to edit the .pom file properly in order to make it work.
Besides, I'm confused about the shade "plugin" overall. I mean, I followed the basic Maven tutorials and it went all fine and dandy. But when I look into the shade plugin page, I can't find it to download, except for the source code. I mean, isn't it a plugin? Shouldn't I download the binaries and install it into Maven somehow?
Sorry about the extreme noobish question but, needless to say, I know squat about Maven.

To create your shaded (uber) jar, you just need to declare the shade plugin in your pom.xml.
With regards to installation of the shade plugin, simply declaring it in the plugins section of your pom.xml is all you need do. Maven plugins are not installed manually, but are automatically downloaded by Maven (if not already downloaded; just like dependencies), stored in your local repository, and used whenever a project needs them.
As to using it, much like other plugins, declare it in your pom.xml by adding a <plugin> element with your configuration needs. This plugin does nothing automatically (some do, some don't) - you have to specify which "goal" to execute (think "method of a class"), and in which "phase" (think "step" of the build process). Unless you have strange needs, specify the "shade" goal in the "package" phase (see below).
For more configuration possibilities, see the shade usage page, and their examples (especially selecting contents for uber jar). Here is a simple example which, when you run mvn package, replaces your original jar in the target/ directory with the uber jar. It only includes the runtime dependencies, not the ones used at test time (notice the <scope> element of the junit dependency, which is not included in the uber jar).
<project>
<groupId>com.sample</groupId>
<artifactId>test</artifactId>
<version>1.0</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>1.5</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.sample</groupId>
<artifactId>test-core</artifactId>
<version>1.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

If you have already downloaded the binaries and installed them into your local Maven repository all that remains is to declare them as dependencies in the POM. If the shade plugin is also declared in the POM a simple "mvn install" should generate a standalone JAR in the target directory.

Related

Maven plugin dependencies are ignored

I created this profile for deploying artifacts on the server via SCP. I know Ant's scp task is optional, therefore I've added the dependencies.
<profiles>
<profile>
<id>remote-deploy</id>
<build>
<plugins>
<plugin>
<artifactId>maven-antrun-plugin</artifactId>
<executions>
<execution>
<id>scp</id>
<phase>install</phase>
<configuration>
<tasks>
<scp .../>
<sshexec .../>
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-jsch</artifactId>
<version>1.7.1</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.42</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</build>
</profile>
</profiles>
However, when I run the profile, I end up with
An Ant BuildException has occured:
Problem: failed to create task or type
scp Cause: the class
org.apache.tools.ant.taskdefs.optional.ssh.Scp
was not found.
This looks like one of Ant's optional components. Action: Check
that the appropriate optional JAR
exists in
-ANT_HOME\lib
-the IDE Ant configuration dialogs
Do not panic, this is a common
problem. The commonest cause is a
missing JAR.
This is not a bug; it is a
configuration problem
Is it possible maven wasn't able to download those dependencies or it just ignores them?
The problem was Maven (2.2.1) didn't download the dependencies. I've found out after I upgraded Maven to version 3. For some reason,the new version downloaded the plugin dependencies and it miraculously started to work.
I have a suspicion the problem was in old version Maven's settings - pluginRepository wasn't configured.
It is likely that maven has downloaded the jars but it is not in ant's classpath. If the objective is to deploy the artifacts using maven, you should probably use Maven Deploy Plugin. What you are doing seems to be a roundabout way.
There is a maven-antrun-plugin bug entry, that could explain why this is happening in Maven-2, they also describe workarounds
In multi-module builds, if there are multiple poms configuring the maven-antrun-plugin, the first(?) seems to win, so that the ones later in the build chain reuse the antrun config from earlier poms, thus missing out on stuff that is different ...
In my problem case, I opted to use Maven-3, where the issue seems to be fixed, instead of workarounds with Maven-2. This had the additional advantage of the build to also speed up - now taking 6min instead of the 10min before.
However, if Maven-3 is not possible for you, I'd try the workarounds...

How do I get Maven to fail when conflicting versions of the same artifact are referenced?

I'd like my Maven build to fail if the same artifact is referenced with different versions in my dependency tree. This would seem like a fairly trivial option, but I can't work out how to do it. Any clues?
The maven-enforcer-plugin has a dependencyConvergence rule which does what you want. Here's an example from the documentation.
This will cause a build to fail:
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.6.0</version>
</dependency>
</dependencies>
With this being logged during compilation:
[ERROR]
Dependency convergence error for org.slf4j:slf4j-api:1.6.1 paths to dependency are:
+-org.myorg:my-project:1.0.0-SNAPSHOT
+-org.slf4j:slf4j-jdk14:1.6.1
+-org.slf4j:slf4j-api:1.6.1
and
+-org.myorg:my-project:1.0.0-SNAPSHOT
+-org.slf4j:slf4j-nop:1.6.0
+-org.slf4j:slf4j-api:1.6.0
The Maven Dependency Plugin will include a new dependency:analyze-duplicate in its version 2.2 and this is IMHO the closest thing you can get without specific development (it won't fail the build but will list duplicate dependencies).
This goal has been added for MDEP-206 (Add new Mojo to find duplicate dependency declared), which is fixed, but the version 2.2 has yet to be released so if you want to use this feature, you'll have to build a SNAPSHOT from the source tree:
https://svn.apache.org/repos/asf/maven/plugins/trunk/maven-dependency-plugin/
If you really want to fail the build in case of duplicate, either write your own mojo (extend the above one) or submit a RFE of the Maven Dependency Plugin.
As a side note, Maven 3 does Throw [a] Validation Error if pom contains a dependency with two different versions out of the box. To be totally accurate, this will be reported as warning in Maven 3.0 to not break existing Maven 2.x builds (see MNG-4005) and will be increased to error in 3.1 (see MNG-4622).
You can have the build fail on dependency analysis warnings using the dependency plugin.
See
http://maven.apache.org/plugins/maven-dependency-plugin/examples/failing-the-build-on-dependency-analysis-warnings.html
Add the following plugin:
Source: http://maven.apache.org/enforcer/enforcer-rules/dependencyConvergence.html
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-enforcer-plugin</artifactId>
<executions>
<execution>
<configuration>
<rules>
<dependencyConvergence/>
</rules>
</configuration>
<goals>
<goal>enforce</goal>
</goals>
</execution>
</executions>
</plugin>

Simple Mavenization of existing Ant build files

If you have an existing ant file, what is the best way to convert the project to Maven. I've checked out things like fAnt, but if I'm going to mess with this stuff, I might as well go full-bore for Maven. I expected something to exist that can just start the pom.xml for me based on the existing build.xml, but I haven't found anything yet. Suggestions?
I don't know any good automated way to do such a migration because things may just be too different so I would do it manually, step by step, and keep the existing ant build in parallel of the future new one until the whole migration is done (from both technical and human points of view).
First, refactor the existing Ant build to align it on Maven conventions:
Make things modular: if your existing build is a big monolithic build producing several artifacts from a single source tree, break it down into separate modules, one for each artifact.
Update directory structure: Maven comes with a standard directory layout and, while it is possible to customize this layout (i.e. to configure plugins for another layout), this is not really recommended and is more a source of troubles than benefits. So I'd move existing app sources, configuration files, tests, etc to match Maven's layout (e.g. src/main/java for application sources, etc).
Then, start to create the Maven build:
Create POMs for each module: Create a POM, declare external libraries as Maven dependencies (maybe add them to a corporate repository, using an enterprise repository is a good practice in an enterprise context anyway), add dependencies between modules.
Finalize the multi-modules build: Add parent(s) POM(s) and inheritance/aggregating relationships. Test that there is no regression with the created artifacts.
You could do this work in a separate VCS branch if you don't want to change anything until the work is done and create scripts to move things. And when ready, merge the Maven specific stuff and apply the scripts.
You could run the Ant script from Maven with the maven-antrun-plugin. Your pom.xml would look something like this:
<project>
...
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-antrun-plugin</artifactId>
<dependencies>
<dependency>
<groupId>org.apache.ant</groupId>
<artifactId>ant-nodeps</artifactId>
<version>${ant-nodeps.version}</version>
</dependency>
</dependencies>
<executions>
<execution>
<id>init</id>
<phase>compile</phase>
<configuration>
<tasks>
<!-- Ant code goes here -->
</tasks>
</configuration>
<goals>
<goal>run</goal>
</goals>
</execution>
</executions>
</plugin>
</build>
</project>
That way you can start to move your dependencies into Maven, and reference them in the Ant script like so
${com.foo.bar:my-lib:jar}
Then just start slowly moving pieces of your Ant into pure Maven stuff.

How to run jetty:run-war using a war defined by maven coordinates?

Background: I'm setting up a functional tests module in a maven project. We use the maven-jetty-plugin for testing.
I've got the jetty plugin set up as described here (to play nicely with the Failsafe plugin), but what I'd like to do is deploy the war artifact from our main web module using jetty (which has just been installed into the local maven repo by the time the functional test module is running).
The jetty plugin's run-war goal has a <webApp> element which takes a string path to a war to deploy. I'd much rather specify the war to deploy using the maven coordinates defined by our web module. Is there any way to do this?
Possible workarounds:
Section 4.13 of "Better Builds with Maven" describes using cargo to deploy a war specified using maven coordinates, but that's serious overkill given that we're using jetty.
More reasonable IMO is using dependency:copy to copy the just-built-and-installed war artifact to a fixed path in the functional tests module's target directory, which I can then provide in the jetty plugin's <webApp> configuration element.
The jetty plugin's run-war goal has a element which takes a string path to a war to deploy. I'd much rather specify the war to deploy using the maven coordinates defined by our web module. Is there any way to do this?
This is not really the maven jetty plugin is supposed to be used, the plugin deploys the war of the current module, what you want to do is not supported by default.
Section 4.13 of "Better Builds with Maven" describes using cargo to deploy a war specified using maven coordinates,
Yes, Cargo can do this in a clean way.
but that's serious overkill given that we're using jetty.
I don't agree. First, the jetty plugin doesn't support what you want to do out of the box (so it may not be the right tool). Second, serious overkill is highly exaggerated, a misconception actually, especially given that cargo requires very little configuration (zero?) for an embedded Jetty.
More reasonable IMO is using dependency:copy to copy the just-built-and-installed war artifact to a fixed path in the functional tests module's target directory
No offense but your whole question sounds a bit like: I have a hammer, it was fine for a nail, can I use it for a screw given that getting a screw driver seems a serious overkill? To answer this question (which is somehow what you are saying), you can use dependency:copy and get the whole thing working with the maven jetty plugin, but this is a hack (and since you're actually not asking any question, I guess you wanted an opinion on this). Of course the final decision belongs to you :)
Just in case, here is how I would implement this with Cargo:
<dependencies>
<dependency>
<groupId>war group id</groupId>
<artifactId>war artifact id</artifactId>
<type>war</type>
<version>war version</version>
</dependency>
...
</dependencies>
...
<build>
<plugins>
<plugin>
<groupId>org.codehaus.cargo</groupId>
<artifactId>cargo-maven2-plugin</artifactId>
<configuration>
<!-- Container configuration -->
<container>
<containerId>jetty6x</containerId>
<type>embedded</type>
</container>
<!-- Configuration to use with the container or the deployer -->
<configuration>
<deployables>
<deployable>
<groupId>war group id</groupId>
<artifactId>war artifact id</artifactId>
<type>war</type>
<properties>
<context>war context</context>
</properties>
</deployable>
</deployables>
</configuration>
<!-- Don't wait, execute the tests after the container is started -->
<wait>false</wait>
</configuration>
<executions>
<execution>
<id>start-container</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
</execution>
<execution>
<id>stop-container</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
...
</plugins>
...
</build>
And I don't think that this can be objectively qualified as a "serious overkill".

Sharing Test code in Maven

How can you depend on test code from another module in Maven?
Example, I have 2 modules:
Base
Main
I would like a test case in Main to extend a base test class in Base. Is this possible?
Update: Found an acceptable answer, which involves creating a test jar.
I recommend using type instead of classifier (see also: classifier). It tells Maven a bit more explicitly what you are doing (and I've found that m2eclipse and q4e both like it better).
<dependency>
<groupId>com.myco.app</groupId>
<artifactId>foo</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
Thanks for the base module suggestion. However, I'd rather not create a new module for just this purpose.
Found an acceptable answer in the Surefire Maven documentation and a blog. See also "How to create a jar containing test classes".
This creates jar file of code from src/test/java using the jar plugin so that modules with tests can share code.
<project>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<version>2.4</version>
<executions>
<execution>
<goals>
<goal>test-jar</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
In order to use the attached test JAR that was created above you simply specify a dependency on the main artifact with a specified classifier of tests:
<project>
...
<dependencies>
<dependency>
<groupId>com.myco.app</groupId>
<artifactId>foo</artifactId>
<version>1.0-SNAPSHOT</version>
<type>test-jar</type>
<scope>test</scope>
</dependency>
</dependencies>
...
</project>
We solved this by making a maven project with test code as the src/main/java and adding the following dependency to projects:
<dependency>
<groupId>foo</groupId>
<artifactId>test-base</artifactId>
<version>1</version>
<scope>test</scope>
</dependency>
Worked for me for 1 project, but I didn't for another after doing exactly the same steps.
So I debugged:
After mvn clean install I checked /target directory: .jar was there so thats good
Ran mvn dependency:tree on a project which should use those test classes. Noticed that generated jar file with test classes is marked as dependency, so thats good.
Conclusion could be only one - I restarted my Intellj. At first class import was still not visible, but after a minute it started to see it!
Note: I only restarted Intellj, no caches removal etc
Yep ... just include the Base module as a dependency in Main. If you're only inheriting test code, then you can use the scope tag to make sure Maven doesn't include the code in your artifact when deployed. Something like this should work:
<dependency>
<groupId>BaseGroup</groupId>
<artifactId>Base</artifactId>
<version>0.1.0-SNAPSHOT</version>
<scope>test</scope>
</dependency>