I work on a big legacy project and I've noticed that in the project root pom we explicitly forced certain maven plugin versions.
I've read about the 'maven way' and it seems to me that this is a violation of this way - forcing versions instead of inheriting them from the superpom. Here's an example of what we have in the project pom:
<build>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>...</configuration>
</plugin>
My question is - what are the valid reasons (if any) to force plugin versions like that. I wonder because often times I find code that was written without any clear purpose and I do wonder if this is such a case, and if I should just drop the version from the project root pom.
Afterthought: On this site they say:
When declaring a "normal" version such as 3.8.2 for Junit, internally
this is represented as "allow anything, but prefer 3.8.2." This means
that when a conflict is detected, Maven is allowed to use the conflict
algorithms to choose the best version. If you specify [3.8.2], it
means that only 3.8.2 will be used and nothing else.
So this means, if you force the version to ensure stability, then you should also use [] otherwise maven is free to ignore your forced version.
The best is to define plugins versions only in a corporate pom and of course mainain this corporate pom over the time which means update the plugins versions from time to time.
This means in consequence that in no other project it is needed or better should be prevented to use a different versions of plugins (except there are very good reasons for this bugs in plugins).
Furthermore the excerpt you have given is an example of bad practice cause plugins and/or their configuration should be defined by using pluginManagement instead.
So if a project needs an older version of a maven plugin there should be at least a comment in the pom which describes why it's using not the inherited version. May be with a link to an appropriate JIRA issue...
what are the valid reasons (if any) to force plugin versions like that.
A valid reason is to keep a build repeatable, especially if there are known problems with a later version of the plugin. This ensures that the specific version is used, rather than a later version from an organisational parent pom (or, worse, from the default with no version specified anywhere).
I wonder because often times I find code that was written without any clear purpose and I do wonder if this is such a case
It's very possible, in a large code base, that this is exactly the case. The plugin configuration could have been copied from somewhere else and the version included without a good reason.
and if I should just drop the version from the project root pom.
If there is no reason given, either in a comment or a commit message, and if the same plugin has a version specified in the parent pom and if the build still works perfectly without it, then you should drop that version.
If it doesn't work, you should either fix the build or add a comment explaining exactly why this version is necessary.
As has already been stated, this can be because of corporate reasons - using new versions of plugins can break tests, functionality or even a whole product itself; new versions have to be tested thoroughly, advanced teams even need to discuss them because it can change the product in many ways.
Not forcing versions will create some kind of unstable situation during the next big build - which is always unwanted, developers dont like randomness :).
If your POM only describes a small, private project or maybe even a small community project you may very well let maven do all the version-management but thats pretty much a no-go for professional products which are worth .... say hundreds of thousands or even millions of currency units.
Related
I want to understand the difference between [3.8.2] vs 3.8.2 when mentioned for our dependency's version.
From here ,
When declaring a "normal" version such as 3.8.2 for Junit, internally
this is represented as "allow anything, but prefer 3.8.2." This means
that when a conflict is detected, Maven is allowed to use the conflict
algorithms to choose the best version. If you specify [3.8.2], it
means that only 3.8.2 will be used and nothing else. If somewhere else
there is a dependency that specifies [3.8.1], you would get a build
failure telling you of the conflict. We point this out to make you
aware of the option, but use it sparingly and only when really needed.
The preferred way to resolve this is via dependencyManagement.
But I feel something is wrong here.
if I write <version>3.8.2</version> for our dependency and that version artifact is not present in our maven repo, then it is not picking anything else. Build simply fails.
So, why above they are saying - "allow anything, but prefer 3.8.2."
Also, they say - This means that when a conflict is detected,.... I am not able to understand this. What possible can be a conflict that does not arise for 3.8.2 but arises for [3.8.2] ?
The whole thing works as follows:
Step 1: Maven builds a dependency tree for your project, including your direct dependencies, their dependencies, the dependencies of your dependencies and so on.
Step 2: Now Maven makes a list of all nodes. If it encounters a dependency in just one version (say 3.8.2 or [3.8.2]) it will pick just that version.
Step 3: If Maven finds more than one version, the magic begins.
If all versions are versions without brackets (like 3.8.2), it picks the "nearest" version as mentioned in dependency mediation principle.
If you have some (or all are) version ranges (like [1.0.0,2.0.0]) or fixed versions (like [1.0.0]), then first it finds the intersection of all ranges/concrete version (Note that it does not consider versions without brackets here for finding this intersection).
If this intersection is found null, build fails. If it is not null, then it proceeds further by chooses the "nearest" version/concrete version/version range.
If by nearest definition, we get a version range/concrete version, then maven selects the most recent available version in resultant intersection of version range found.
If by nearest definition, we get a version (not concrete version) , then maven checks if that version is present in resultant intersection of version range found. If yes, this version is selected. If not, then maven selects the most recent available version in resultant intersection of version range (and does not fail the build).
The quote "allow anything, but prefer 3.8.2" is at best misleading. Maven does not try to make up for missing dependencies in the repository, it just "mediates" versions if more than one version is found in the dependency tree.
I am in process of updating my project's jdk from 8 to 11 . So, while running I am facing such kind of warnings
org.aspectj.internal.lang.annotation.ajcDeclareAnnotation scanned from multiple locations: jar:file:///Users/nishtha.garg/.m2/repository/org/aspectj/aspectjrt/1.9.2/aspectjrt-1.9.2.jar!/org/aspectj/internal/lang/annotation/ajcDeclareAnnotation.class, jar:file:///Users/nishtha.garg/.m2/repository/org/aspectj/aspectjweaver/1.9.2/aspectjweaver-1.9.2.jar!/org/aspectj/internal/lang/annotation/ajcDeclareAnnotation.class
javax.mail.search.MessageIDTerm scanned from multiple locations: jar:file:///Users/nishtha.garg/.m2/repository/javax/mail/mail/1.4/mail-1.4.jar!/javax/mail/search/MessageIDTerm.class, jar:file:///Users/nishtha.garg/.m2/repository/javax/mail/javax.mail-api/1.6.2/javax.mail-api-1.6.2.jar!/javax/mail/search/MessageIDTerm.class
org.aspectj.lang.reflect.DeclarePrecedence scanned from multiple locations: jar:file:///private/var/folders/rw/9_fr4s6s01d5vcykl3fqkd7d3j59v0/T/jetty-0.0.0.0-0-attache-api-rest.war-_api-any-13258162480745251787.dir/webapp/WEB-INF/lib/aspectjrt-1.9.2.jar!/org/aspectj/lang/reflect/DeclarePrecedence.class, jar:file:///private/var/folders/rw/9_fr4s6s01d5vcykl3fqkd7d3j59v0/T/jetty-0.0.0.0-0-attache-api-rest.war-_api-any-13258162480745251787.dir/webapp/WEB-INF/lib/aspectjweaver-1.9.2.jar!/org/aspectj/lang/reflect/DeclarePrecedence.class
org.apache.cxf.transport.http.policy.HTTPClientAssertionBuilder$HTTPClientPolicyAssertion scanned from multiple locations: jar:file:///private/var/folders/rw/9_fr4s6s01d5vcykl3fqkd7d3j59v0/T/jetty-0.0.0.0-0-attache-api-rest.war-_api-any-13258162480745251787.dir/webapp/WEB-INF/lib/cxf-rt-transports-http-3.1.12.jar!/org/apache/cxf/transport/http/policy/HTTPClientAssertionBuilder$HTTPClientPolicyAssertion.class, jar:file:///private/var/folders/rw/9_fr4s6s01d5vcykl3fqkd7d3j59v0/T/jetty-0.0.0.0-0-attache-api-rest.war-_api-any-13258162480745251787.dir/webapp/WEB-INF/lib/cxf-rt-transports-http-3.1.4.jar!/org/apache/cxf/transport/http/policy/HTTPClientAssertionBuilder$HTTPClientPolicyAssertion.class
I can understand that this is occurring because 2 jars have same class and I have to exclude some dependencies but I am not getting how to exclude and what should be the criteria.
Let's take one of your conflicts as an example:
org.apache.cxf.transport.http.policy.HTTPClientAssertionBuilder$HTTPClientPolicyAssertion
scanned from multiple locations:
jar:file:///private/var/folders/rw/9_fr4s6s01d5vcykl3fqkd7d3j59v0/T/jetty-0.0.0.0-0-attache-api-rest.war-_api-any-13258162480745251787.dir/webapp/WEB-INF/lib/cxf-rt-transports-http-3.1.12.jar!/org/apache/cxf/transport/http/policy/HTTPClientAssertionBuilder$HTTPClientPolicyAssertion.class,
jar:file:///private/var/folders/rw/9_fr4s6s01d5vcykl3fqkd7d3j59v0/T/jetty-0.0.0.0-0-attache-api-rest.war-_api-any-13258162480745251787.dir/webapp/WEB-INF/lib/cxf-rt-transports-http-3.1.4.jar!/org/apache/cxf/transport/http/policy/HTTPClientAssertionBuilder$HTTPClientPolicyAssertion.class
Digging deeper (bolded) you can see that the class in question is provided by different version of "cxf-rt-transports-http", namely separate jars for versions 3.1.4 and 3.1.12.
You will need to examine each conflict individually and remove the less desirable jar from the classpath.
The best way to do so is by creating an exclusion, such as:
<exclusions>
<exclusion> <!-- declare the exclusion here -->
<groupId>org.apache.cxf</groupId>
<artifactId>cxf-rt-transports-http</artifactId>
</exclusion>
</exclusions>
You declare this on the dependency that depends on the above.
Which isn't always easy to figure out unless you have software that illustrates the dependency graph.
In Eclipse for example, the Maven POM editor can show you dependencies, conflicts, and generate exclusions for you.
Selection criteria:
In most cases you'll probably want the newest version available, so in this case that's likely going to be 3.1.12.
However, you may not want the latest, latest, but a particular stable version that you have extensively tested, yet which doesn't contain any vulnerabilities. So the answer to that isn't straight forward.
If you need a specific version, it may be best to declare an explicit dependency on that artifact in your own pom and exclude it from all others.
Edit:
Another reason why you might need to choose a particular version over another is that you may be using another library that in turn relies on a specific version of a conflicting dependency, but has issues with a newer one.
In most cases version numbering is <major>.<minor>.<bugfix> and in almost all cases bugfix releases are binary compatible with each other, so in the case of cxf-rt-transports-http above you should be fine selecting 3.1.12.
On the other hand, say you had dependencies A depending on C v1.5.0 and B depending on C v2.1.2. This would be more difficult due to possible incompatibility between those major versions. There may be a feature you need in v2, but A is not compatible as an important method that A relies on was removed.
You would then want to check whether a newer version of A exists that will work with C v2.1.X and replace that instead. If there isn't you might either need to downgrade B, or install them side by side. In an extreme case that may involve repackaging the product.
An alternative is to contribute to or fork project A to create a version with updated dependencies.
Maybe it helps to think about the purpose of the warnings:
They're really saying that your code may run, but it is possible that a dependency version is selected in a non-deterministic fashion and that as a result some operations of your code might not to what you expect.
What you want to do is make it deterministic by providing the JVM only one option to choose from in each case. You do that by making a conscious choice as a developer as to which version to use or depend on.
If you have no other reasons, then I'd go for the latest/highest stable version, as that will probably include the most features and bugfixes.
Test thoroughly, as you would, and on the off-chance that you have problems that are not caused by your own code, isolate and report a possible bug, downgrade if you need to until the bug is fixed, and/or contribute a fix yourself.
I have builds like: 1.0.0-9, 1.0.0-10, 1.0.0-11, 1.0.0-12, etc.
I've configured my dependency like:
[1.0.0,)
Which means that it should use the latest version from the existing ones (ofc which starts with 1.0.0), but instead of using the 1.0.0-12 it used the 1.0.0-9. I think that it's because the 9 is grater then the 1.
Firstly I would like to force the maven to use truly my latest builds.
I can also have 1.0.0-LOCAL build (which is a local build on the developer's PC). I would like to force the maven to use the 1.0.0-LOCAL instead any other builds if it's available.
I don't know exactly how I could do these, maybe somehow with the settings.xml configuration file which is located on the build machine and on the developer's PC as well.
If I would have a any other way to do this please let me know.
Have a good day,
Arnold Robert Turdean
Update:
It turned out that the Maven Version Range - downloads all the available versions not just the latest one was the original problem.
Which maven version do you use? From Maven point of view all of those given versions are releases? The question is if it wouldn't be better to use a SNAPSHOT version instead?
Furthermore to check if the ordering of your artifacts is correct or work like you expect this can be checked by using this:
java -jar apache-maven-3.3.9\lib\maven-artifact-3.3.9.jar 1.0.0-12 1.0.0-9
Display parameters as parsed by Maven (in canonical form) and comparison result:
1. 1.0.0-12 == 1-12
1.0.0-12 > 1.0.0-9
2. 1.0.0-9 == 1-9
which shows correctly that 1.0.0-12 is greater than 1.0.0-9. So the question is also if you are using a repository manager etc. ? Do you do only a mvn install ?
What I don't understand is your statement about `1.0.0-LOCAL' ?
Apart from that I would suggest to prevent using of version ranges cause they make you build non reproducible.
In maven, dependency hierarchy is so important. Make sure that you don't have other dependencies which added before, into other pom's.
(In hierarchy, maven cares latest dependency.). Dependency 1.0.0-LOCAL must be the latest one.
I suggest to you, while the local developing time use generic -SNAPSHOT, use numbers when you start packaging .
When using multiple APIs in a single project, the JAR files required for each API are added to the project in addition to other needed libraries such as Apache Commons, logging, etc. that are already used by the project. This sometimes results in a large number of jar files.
When a certain API or library is no longer used, it would be nice to remove the JAR files associated with it. However, there is a risk that another API or library requires it. This would NOT always become apparent during the building of the project. Sometimes, JARs that are missing throw errors only at runtime.
I have the following questions:
What is the best way to deal with this issue? In other words, be able to remove JARs without running the risk of runtime errors later?
I have been told that Maven solves this problem. Does it? Would it work if the external APIs used are not Maven-based? Would I be able to remove JARs without worrying about runtime errors? Do I need to rewrite my entire project to be based on Maven?
How do non-JVM platforms deal with the issue of shared libraries and removing them? Is Java lacking in this area or it is a common issue for all platforms?
Yes I agree Maven could help you in this case. Basically in Maven compile & runtime dependencies for each artifact (jar/war/ear/etc) are declared on pom.xml file. If multiple dependencies depends on same artifacts the latest version is used -- for example:
A-1.0.jar -- depends on --> C-2.0.jar
B-1.0.jar -- depends on --> C-2.1.jar
Only C-2.1.jar is is included in your project.
If a required dependency couldn't be found / taken out, Maven build will automatically fail. So to avoid runtime dependency missing, you can declare a dependency in runtime scope to a particular artifact -- and when you no longer need it you just take it out
There is an old trick I used to use on UNIX many years ago, it might still work for you. First use UNIX "touch" to set the date/time on all your files to the current date/time. Then wait for at least one minute. Then run your application. Then run UNIX "ls -lut" to list all your files, but this time the ones that were not used will have the date/time set in the first step whereas those that were used will have a more recent date/time due to the "u" switch reporting the last used date/time.
I have a Java project, which is still in a very early stage. There are no real releases yet, so I set version as 1.0.0-SNAPSHOT in my pom.xml.
Before each submit, I perform a Sonar analysis directly from Maven using:
mvn sonar:sonar
Sonar has this nice ability to record changes of KPIs over time. However, it only does that for a version. Now, as I don't update my version number, it overrides the last analysis result and replaces it with the current one.
I wonder how to get around that. I don't want to increase version numbers manually each day as this doesn't seem very natural to me.
What other options do I have? Should I use one of the Maven plugins to add a build number to the version string so that I have unique versions? Do you have any other strategy?
If you want increase version number every release, the maven release plugin is recommendation.
But if you want increase the version number everyday even every build, you can use unique version number like timestamp which can get from Maven building phase, after Maven 2.1, it has the built-in ${maven.build.timestamp} you can use, and you can use format
<properties>
<maven.build.timestamp.format>yyyyMMdd-HHmm</maven.build.timestamp.format>
</properties>
for more information you can visit http://maven.apache.org/guides/introduction/introduction-to-the-pom.html
Sonar does preserve the metrics for each snapshot build (based on your configuration). You can then rename the snapshots suitably and use them to get historical information.
You say that you don't to releases yet. As soon as you do, the Maven Release Plugin is a recommendation. It handles the SVN related stuff that comes with a release.
I'm not sure if this helps you with day-to-day work on a SNAPSHOT, though.