Maven classpath order issues - maven-2

Does anyone know of a way to set a specific classpath order in Maven2, rather than the random ordering I appear to experience at the moment?
There are a number of legitimate reasons for wanting to do this:
A vendor has supplied a patch jar, which contains overriding classes for a previously released jar and therefore the patch jar must appear first in the classpath ordering.
Two jar's found on the classpath discovered by traversing pom dependencies contain the same class in the same package with different signitures. For example:
jboss
jbossall-client
4.2.0.GA
org.hibernate
hibernate
3.1
both contain:
org.hibernate.util.ReflectHelper.class, but the jbossall-client version is missing the getFastClass method.
From googling I see that this is perhaps a point of contention between maven enthusiasts and people facing this particular issue, but surely there are legitimate reasons for classpath ordering.
Any advice from anyone that has solved this particular quandary would be much appreciated!
Thanks

As of version 2.0.9 maven uses pom order for classpath, so you can actually manipulate it now. We mostly supress transitive dependencies to external libraries that we also include directly.
From the release notes of maven 2.0.9:
MNG-1412 / MNG-3111 introduced deterministic ordering of dependencies on the classpath. In the past, natural set ordering was used and this lead to odd results. The ordering is now preserved from your pom, with dependencies added by inheritence added last. In builds that had conflicting or duplicate dependencies, this may introduce a change to the output. In short, if you have weird issues with 2.0.9, take a look at the dependencies to see if you have conflicts somewhere.

Maven 2.0.9 adds correct ordering so you absolutely must have that version or higher for the below to work.
Secondly you need the an updated plugin. The Maven guys are working on a fix, its in their jira to fix but this is something I urgently needed. So in the meantime I have fixed this myself and you can pull the Modified plugin source code from github.
Edit: Refer to http://jira.codehaus.org/browse/MECLIPSE-388
There are two ways to install it, either pull my modified code and install it or download the prebuilt jar and just add it.
Building the plugin
Run maven install from the plugin directory you checked out and then add the following in your plugins section of your projects pom:
<build>
</plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-eclipse-plugin</artifactId>
<version>2.8-cpfix</version>
</plugin>
</plugins>
</build>
Download the jar
Alternatively if you don't want to download and compile yourself then you can just get hold of the jar file and install it yourself.
Once you have the file run
mvn install:install-file -Dfile=<path-to-file> -DgroupId=org.apache.maven.plugins \
-DartifactId=maven-eclipse-plugin -Dversion=2.8-cpfix -Dpackaging=jar
Regardless of how you installed it now when you run mvn eclipse:eclipse it will pick up the modified code and order the dependencies based on the order you defined in your pom file, no alphabetical ordering. It will also put the JRE container at the top of the dependencies.
Hopefully the real version of this code will come out soon, but in the meantime this fix has worked for me on my project and I hope it can help some others as well.

Rather a further qualification of the question than an answer:
under "Maven Dependencies" Eclipse does not seem to honour the POM-order.
(it does use the POM-order under "Java Build Path" & in the Classpath)
Is that the expected behaviour?
I'm using Eclipse 2021-09 (which has Maven 3.8.1 embedded) under Windows 10.
Here's the POM:
<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>
<groupId>com.group</groupId>
<artifactId>arty.fact</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Maven Dependency Order</name>
<properties>
<maven.compiler.target>17</maven.compiler.target>
<maven.compiler.source>17</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>wsdl4j</groupId>
<artifactId>wsdl4j</artifactId>
<version>1.6.3</version>
<exclusions><exclusion><groupId>*</groupId><artifactId>*</artifactId></exclusion></exclusions>
</dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.14.1</version>
<exclusions><exclusion><groupId>*</groupId><artifactId>*</artifactId></exclusion></exclusions>
</dependency>
</dependencies>
</project>
The Maven Dependencies looks like this:

If you have problem starting with IntelliJ IDEA, you can change the dependencies order from project structrue.

Related

How to bundle JNLP API with Maven Project

I have a project where I need the JNLP API. I did not find an artifact for that on Maven Central, so I added an external Repository which offers that to my pom. That repository went offline this weekend. This is the second time something like this happened to me.
I know this is pretty much what Maven is not about, but really I just want that tiny jnlp-api-1.5.0.jar file to be
In my SCM (I don't want to roll my own Maven repository for just one dependency).
In the compile scope when the project builds.
Which knobs do I have to turn to accomplish this?
As of JDK 7.0, the JNLP API is being provided by the javaws.jar file in your JRE's lib directory, i.e., ${java.home}/lib/javaws.jar. It is possible to use the maven dependency scope system.
<project>
...
<dependencies>
<dependency>
<groupId>javax.jnlp</groupId>
<artifactId>jnlp-api</artifactId>
<version>7.0</version>
<scope>system</scope>
<systemPath>${java.home}/lib/javaws.jar</systemPath>
</dependency>
</dependencies>
...
</project>
You can put the JAR in your local repository using the install-file goal of the maven-install-plugin and reference it as you normally would in your POM. The command would be:
mvn install:install-file -Dfile=/path/to/jnlp-api-1.5.0.jar -DgroupId=<group-id> -DartifactId=<artifact-id> -Dversion=1.5.0 -Dpackaging=<packaging>
Place this command in a script and check it into your SCM. That way, you (and anyone else working on this project) can install it easily to the local repo.

Maven Plugin error

Im a new user in maven. I have few doubts.
I have some BAT files that has to executed via maven
While executing my default commands, I need to echo the status like "Started first task"
When the task gets failed user should get error message popup etc.,
Automatic repository update through maven
Maven calling ant to build *.war
Copying the new created build file(*.war) to local folder with date/time
Auto upload/deploy from build machine to server
Testing particular URLs/products to test the site flow
Sending mail regarding deployment status
Below is my example pom.xml, which trying only (a)
<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.example.maven</groupId>
<artifactId>test</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>test</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.1.1</version>
<executions>
<execution>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<!--echo>Starting CVS Update</echo-->
<configuration>
<tasks>
<exec>
<directory>D:\</directory>
<executable>"D:\test.bat"</executable>
<!--failonerror="true" -->
<!-- optional -->
<!--workingDirectory>"cd C:\repo\projects\mcsandbox"</workingDirectory-->
</exec>
</tasks>
</configuration>
</plugin>
</plugins>
</build>
</project>
I guess this is not the expected answer but I don't think that you're on the right path and I don't see the point of using Maven the way you'd like to use it. From your description, my impression is that you aren't really adopting Maven but rather trying to insert something existing into it and, believe me, this isn't the easiest way to get started with Maven:
This is going to add an extra layer of complexity.
This requires a deeper understanding of the way maven works than "just" adopting it.
You're going to fight against the tool (and there are good chances that the tool will win).
This will generate more troubles than benefits.
I don't really know where to start so I'll just try to quickly cover your points first:
I have some BAT files that has to executed via maven
This is possible but this is not really the Maven way (depending on the logic, you should move it to Maven or keep it outside maven), and I don't think this is going to be very robust (might be wrong though). On top of that, this already requires some knowledge of the build lifecycle (where to hook things) and of plugin configuration. And from what I've seen (I do not mean to be rude), you don't have it.
While executing my default commands, I need to echo the status like "Started first task"
This is not how things work with Maven. Maven isn't procedural like Ant, Maven has conventions, Maven is declarative, Maven has a lifecycle.
When the task gets failed user should get error message popup etc.,
User? Popup? Maven is not really targeted at end users, Maven is a developer tool and what you get in case of an error is a message in a console.
Automatic repository update through maven
Do you mean a CVS update? This is not impossible but is usually not Maven's job (it's more a Continuous Integration engine task).
Maven calling ant to build *.war
This is possible using the Maven Antrun Plugin but that's not the spirit and may go against some maven rules (like one artifact per project). Also, Maven will not magically be aware of things built by Ant. Why don't you just stick with Ant?
Copying the new created build file(*.war) to local folder with date/time
Possible. But that's not how things work by default.
Auto upload/deploy from build machine to server
Maven has plugins (e.g. Cargo) that can help at doing this but you have to tell him to.
Testing particular URLs/products to test the site flow
Maven itself won't do that. But it can run (integration) tests doing that.
Sending mail regarding deployment status
That's not maven's responsibility (more a Continuous Integration engine task).
My global feeling is that you are thinking too much Ant oriented and maybe expecting too much from Maven. Maven is different, it doesn't really work the way you described, it doesn't work like Ant. My recommendation would be to either fully migrate to Maven and adopt its philosophy or to stick with your current Ant solution (and maybe consider using the Maven Ant Tasks or Ivy to leverage things such as dependency management, artifact deployment).
See also
How to convert from Ant to Maven (...)
Differences between Ant and Maven
Maven: The Definitive Guide

Activate profile in depended-on POM?

My company writes companion products for project management software that uses that software's Java API. They release new API versions with new releases of their products, and also point releases for bug fixes etc. We need to support clients using various versions of their software (and by extension, their API). In order to do this without unnecessary code duplication, we have defined profiles in our products that include the necessary dependencies for each API version.
I have a war project built using this technique with the "api70" profile activated, and another project that depends on that war project with a type of pom, in order to pull in the war's dependencies. The problem is that when building this second project, the profile-specific dependencies are not being included, even though I'm defining -Papi70 on the maven command line when building the depending project.
Is there any way to get this to work?
In the war project:
<!-- API 7.0 profile. -->
<profile>
<id>api70</id>
<dependencies>
<dependency>
<groupId>com.bigcompany</groupId>
<artifactId>integrationlibrary</artifactId>
<version>7.0-a</version>
</dependency>
</dependencies>
<properties>
<apiversion>api70</apiversion>
</properties>
</profile>
In the depending project:
<!-- Depend on war as type=pom for dependency mediation. -->
<dependency>
<groupId>com.mycompany</groupId>
<artifactId>warproject</artifactId>
<version>${warVersion}</version>
<type>pom</type>
</dependency>
Command line used for building depending project:
mvn -P api70 clean package
The resulting build does not include integrationlibrary or any of its transitive dependencies.
I think that your problem doesn't apply to profiles at all. It's about how transitive dependencies work for war packaging. By design they doesn't work :) War archive contain its dependencies in WEB-INF/lib folder or if it is packaged in ear it can share libraries with ear libs. More of the problem you can read on this wiki article. It's about Skinny Wars but topic relates also your problem.
For you interesting is also this JIRA issue.
Quick but not elegant solution is to change packaging form war to pom (or create duplicate pom with pom packaging).
Why don't you create an api70-deps pom project and let your war and dependant project both pull that in, profile activated or otherwise?
This approach works wonders for me... my poms become so much more tidier.

Maven: Including jar not found in public repository

If I was to use a 3rd party library that was not in the maven public repository, what is the best way to include it as dependency for my project so that when someone else checks out my code it will still be able to build?
i.e.
My Application "A" depends on jar "B" which does not exist in the public repository. I, however, wish to add "B" as a dependency to "A" such that when a person on the other side of the world could check out the code and still be able to build "A"
You can install the project yourself.
Or you can use the system scope like the following:
<dependency>
<groupId>org.group.project</groupId>
<artifactId>Project</artifactId>
<version>1.0.0</version>
<scope>system</scope>
<systemPath>${basedir}/lib/project-1.0.0.jar</systemPath>
</dependency>
systemPath requires the absolute path of the project. To make it easier, if the jar file is within the repository/project, you can use ${basedir} property, which is bound to the root of the project.
If you have a parent project with a module that is in this situation (requires a dependency not in a repository) you can setup your parent project to use the exec-maven-plugin plugin to auto-install your dependent file. For example, I had to do this with the authorize.net jar file since it is not publicly available.
Parent POM:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>exec-maven-plugin</artifactId>
<version>1.2.1</version>
<inherited>false</inherited>
<executions>
<execution>
<id>install-anet</id>
<phase>validate</phase>
<goals>
<goal>exec</goal>
</goals>
</execution>
</executions>
<configuration>
<executable>mvn</executable>
<arguments>
<argument>install:install-file</argument>
<argument>-Dfile=service/lib/anet-java-sdk-1.4.6.jar</argument>
<argument>-DgroupId=net.authorize</argument>
<argument>-DartifactId=anet-java-sdk</argument>
<argument>-Dversion=1.4.6</argument>
<argument>-Dpackaging=jar</argument>
</arguments>
</configuration>
</plugin>
</plugins>
</build>
In the above example, the location of the jar is in the lib folder of the "service" module.
By the time the service module enters the validate phase, the jar will be available in the local repository. Simply reference it in the way you set up the groupid, artifact, etc in the parent pom. For example:
<dependency>
<groupId>net.authorize</groupId>
<artifactId>anet-java-sdk</artifactId>
<version>1.4.6</version>
</dependency>
Using system scope may work but it is not recommended even in the Maven specification.
it is not portable.
from Maven book:
system- The system scope is similar to provided except that you
have to provide an
explicit path to the JAR on the local file system. This is intended to allow compilation
against native objects that may be part of the system libraries. The artifact is assumed
to always be available and is not looked up in a repository. If you declare the scope to
be system, you must also provide the systemPath element. Note that this scope is not
recommended (you should always try to reference dependencies in a public or custom Maven
repository).
The best approach is to install to your local repository or to your enterprise repository to be accessible to all your peers.
this is very easy if you are using a repository manager such as Nexus.
This solution worked for me;
1. Created a local-maven-repo in my project's root directory and copied all my jars in the
2. Executed the following command to generate the necessary pom files and metadata etc for each and every jar that I needed to use;
mvn deploy:deploy-file -DgroupId=<somegroupid> -DartifactId=<someartifact> -Dversion=1.0.0 -Durl=file:./local-maven-repo/ -DrepositoryId=local-maven-repo -DupdateReleaseInfo=true -Dfile=<path to jar file>
This generated a new jar file with a pom file inside the local-maven-repo and I was able to include into my project as a dependency like this;
<dependency>
<groupId>somegroupid</groupId>
<artifactId>someartifact</artifactId>
<version>1.0.0</version>
</dependency>
Then mvn package ensured that my project dependencies are resolved and packaged with my war file.
If you are using groovy/grail tool suite (GGTS) then you can directly import that third party dependency (but be sure you have that third party dependency in your local repository) using below steps :
Go to the Project Explorer and right click on project.
Click on import option.
Expend the maven option and select Install or deploy an
artifact to a maven repository and click next.
Brows and select that third party dependency using Artifact File
option and enter the detail of Group Id, Artifact Id and Version
using POM.xml file and click on finish
Wait some moment and possibly error would have gone for that problem.
Generally speaking, you should first put the 3rd party jar into your local repository. After that you can use it by adding the dependency into pom.xml.
For example.
1.put the jar into your local repository first:
mvn install:install-file -Dfile=<path-to-file>
Note: this command requires maven-install-plugin version 2.5 or later. If not, You can refer to Here
2.use the jar by adding the dependency into you project's pom.xml.
just add this into the pom.xml of your project:
<dependency>
<groupId>${the groupId in the jar's pom.xml}</groupId>
<artifactId>${the artifactId in the jar's pom.xml}</artifactId>
<version>${the version in the jar's pom.xml}</version>
</dependency>
3.you can then package or deploy your project by running mvn package or mvn deploy
The 3rd party jar will also be included in the package.

How can maven be used in a continuous integration situation to install versioned artifacts in the repository?

We are in the process of converting our main build process from ant to maven. We use TeamCity for our Continuous Integration server (CI).
We'd like to use the CI server to kick off (nightly) builds whose version contain a build number, as in 1.0.0.build#. These builds would be installed in our local maven repository to be used by other projects. So the CI server would manage the versions, maven would build the project, and the maven repository would make the builds accessible to other projects.
I intended to initiate the build from the CI server using the following command:
mvn -Dversion=1.0.0.25 install
The project's pom would have a bogus version number, and the -D flag would override it, as in:
<version>0.0.0.0</version>
The problem with this method is that the maven install plugin only uses the version in the pom file, not the version passed in on the command line. This is noted in this maven issue.
So since this issue has existed since 08/2006 and has not been fixed, I assume that this is somehow not 'the maven way'. So my question is, how can maven be used in a continuous integration situation to install versioned artifacts in the repository?
Sounds like you want to build SNAPSHOT versions with unique versions.
So, in your POM declare the version as:
<version>#.#.#-SNAPSHOT</version>
Then, in the distributionManagement section of your POM, enable unique versions for the snapshotRepository via (see Maven's POM reference on this):
<snapshotRepository>
<uniqueVersion>true</uniqueVersion>
<id>your-snapshot-repo-id</id>
<name>Your Snapshots</name>
<url>http://your-snapshot-repo-url/maven</url>
</snapshotRepository>
FYI, note that Maven conventions recommend versions be declared as major.minor.revision. So, 1.0.25 instead of 1.0.0.25. If you're able to use this versioning scheme, things will work more smoothly in a Maven world.
Matthew's answer provides a solution where the artifacts get uploaded into the local and remote repository having the desired version number, i.e. the paths inside the repository are contain the correct version numbers, however, Maven installs and deploys always the source POM file that would still contain the ${ciVersion} in the version element.
If you have a multi-module with a common parent like this:
<project xmlns="..." xmlns:xsi="..." xsi:schemaLocation="...">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>myParent</artifactId>
<groupId>com.stackoverflow</groupId>
<version>${ciVersion}</version>
</parent>
<artifactId>myChild</artifactId>
...
</project>
you won't be able to reference a dedicated version of the myChild module, as the dependency resolution will exist with an error that it cannot find the myParent module with version ${ciVersion}.
However, you could use the resolve-pom-maven-plugin that uploads a POM into the local and remote repository where all variables inside the POM get substituted by their actual values. In order to do this, you have to add the following snippet into your (parent) POM:
...
<build>
<plugins>
<plugin>
<groupId>com.sap.prd.mobile.ios.maven.plugins</groupId>
<artifactId>resolve-pom-maven-plugin</artifactId>
<version>1.0</version>
<executions>
<execution>
<id>resolve-pom-props</id>
<goals>
<goal>resolve-pom-props</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
...
Shek's answer is probably 'the maven way', so I'll accept it as the correct answer. However, we are not ready to change our conventions, so here is the workaround that we are using.
By using a level of indirection you can pass a version number in to the pom at build time and have the install and deploy plugins use them. For example:
<project xmlns="..." xmlns:xsi="..." xsi:schemaLocation="...">
<modelVersion>4.0.0</modelVersion>
<groupId>com.stackoverflow</groupId>
<artifactId>stackoverflow</artifactId>
<version>${ciVersion}</version>
<packaging>jar</packaging>
<name>StackOverflow</name>
<properties>
<ciVersion>0.0.0.0</ciVersion>
</properties>
...
</project>
We cannot override ${project.version} directly. So instead, we add a second property called 'ciVersion' and give it a default value of '0.0.0.0' in the properties section. Now the CI server can specify a version number by overriding the ciVersion property on the command line. As in:
mvn -DciVersion=1.0.0.25 install
The install and deploy plugins will use the value of the ciVersion property that was passed in whenever ${project.version} is referenced, as expected, and the default value will be used when no version is provided on the command line. This allows us to switch to maven with minimal impact on our process. In addition, this workaround is unobtrusive, allowing for an easy switch to the SNAPSHOT functionality when desired.