Can Maven generate the module declaration? - java

The module declaration defines, among other things, a module's dependencies. If I use Maven as a build tool, this is redundant because the pom.xml already contains these (and more) information. Based on that, couldn't Maven generate the module-info.java for me?

One might expect that most of the dependencies are indeed required modules as well. However, requirements can also point to modules of the JDK/JRE, which are not specified in the pom.xml. So yes, if you only look at the dependencies, probably most of them could be transformed to a required module reference.
But a module-descriptor contains much more information, which are all based on decisions to be made by the developer.
I've written an article about it which describes in detail why it is not possible to fully generate this file.

As far as I know, bnd-maven-plugin can generate module-info.class based on the configured dependencies. If you are working with maven-bundle-plugin, you need to specify the version of bndlib manually, for the latest version of maven-bundle-plugin(5.1.3) is still using the 5.x version of bndlib, and bndlib requires 6.x to support jpms.
Document: https://bnd.bndtools.org/releases/6.1.0/chapters/330-jpms.html

Related

Maven different package name in newer dependency

The project I am working on produces a jar that I deploy on azure so Spark runs the job.
It is using an internal dependency A which uses the dependency org.apache.commons:commons-configuration:1.10 yet when I deploy on azure it uses 2.1.1 version by default.
On azure we have the version 2.1.1 in which the package name(org.apache.commons.configuration2) differs from the 1.10 version ( org.apache.commons.configuration).
So having this line in the dependency A caused an error when using the 2.1.1 version:
Import org.apache.commons.configuration
It needs to be having "2" at the end, a thing I can t add as A is a dependency.
I tried excluding org.apache.commons:commons-configuration from A then using the maven shade plugin to rename the package but the jar file become double the actual size besides the shaded jar produced alone not inside the zip with the workflow and the sh file, a thing my team may not like.
Updating from commons-configuration 1 to 2 is a major change, the new version is not a drop-in replacement. As you have already pointed out the top level package changes and this will most likely brake library A. The correct solution will probably be to update library A to use commons-configuration 2.
You can still try to hack the Maven project setup to see if it works:
Exclude commons-configuration 1 from library A dependency using <exclude> tag.
Add commons-configuration 2 as a direct project dependency with provided scope in module B. The provided scope is needed to avoid packaging the dependency.
If you want to avoid using the maven-shade plugin than an alternative solution might be to:
Exclude commons-configuration 1 in the library A dependency declaration;
Work out which classes and methods from commons-configuration 1 that library A uses (easy enough if you have the source code, otherwise a modern IDE will disassemble it for you);
Write your own versions of these classes and methods in your application that delegate to the commons-configuration2 implementation.
Note that commons-configuration2 is a part of the Apache Spark distribution and it cannot be ignored. It would need to be added to your project with <scope>provided</scope>.
If this is too hard then the maven-shade-plugin is your only viable solution.

Java 9 modules and uber-jar

When developing Java libraries we're currently using the Apache Maven Shade Plugin to hide internal dependencies on other libraries (jars) by renaming their package names.
Is it possible to hide these internal library-dependencies by using the Java 9 module system and not exporting the name of the internally used libraries?
I.e:
Both module A and B include, but does not export, class org.codehaus.jackson.map.ObjectMapper (included using e.g. Maven Shade plugin) with different versions for the class
Module A uses module B
Will each module still use its implementation org.codehaus.jackson.map.ObjectMapper?
I believe it should by so, but I have found no documentation explicitely confirming this, nor any texts / examples recommending this approach for this quite usual versioning issue.
This issue is described as http://openjdk.java.net/projects/jigsaw/spec/issues/#MultiModuleExecutableJARs and there's no support for it yet. In case all dependencies are modules, it would make sense to use jlink to solve this. But as long as there is at least one non-module, there's no solution available yet. This is something that needs to be solved within the JDK/JRE.
It is still a valid case, so I would suggest to ask this question the at jigsaw-dev mailinglist and refer to #MultiModuleExecutableJARs

Why doesn't Gradle or Maven have a dependency version lock file?

I've recently been introduced to the concept of a dependency version lock file when reading about package managers like NPM, Yarn, Paket, Cargo, etc. My understanding is that it is a file that lists all direct and transitive dependencies along with their exact version number so subsequent builds are guaranteed to use an equivalent set of dependencies. This seems to be a desirable feature since many package managers have or are adopting the concept.
My questions are then:
Why doesn't Maven or Gradle use a lock file? Or if they do, why haven't I seen it?
What are the pros and cons of allowing version ranges in a package manager's dependency resolution strategy vs only allowing exact versions?
Maven does not have a way of to achieve what you are asking for. Even if you set specific versions for your direct dependencies, which you should, your transitive dependencies can easily be unintentionally changed due to a seemingly unrelated change. For example, adding a dependency on a new library can give you an older version of an existing transitive dependency.
What you need is to have a dependencyManagement section that lists all your direct and transitive dependencies. You will still not be able to detect if a transitive dependency is removed or added which is a feature that, for example, NPM provides. The problem with missing that is that all your dependencies are no longer in the dependencyManagement section. To detect those changes you could use something like dependency-lock-maven-plugin which I have written. Using it will also make it less important to have everything in a dependencyManagement section since changes in transitive dependencies will be detected.
I would also recommend having https://maven.apache.org/enforcer/enforcer-rules/requireUpperBoundDeps.html in your build since Maven chooses the versions of the transitive dependencies that are closes in the tree and not, as you would expect, the highest version.
I have seen many runtime problems caused by developers accidentally changing transitive dependencies.
TL;DR: You do need something like a lock file in Maven, but it is not there due to historical ideological reasons.
I would not recommend using version ranges since they make your build not reproducible.
Neither does it behave as you would believe when it comes to transitive dependencies.
Dependency locking was a feature that achieved some maturity by Gradle 5.0:
https://docs.gradle.org/current/userguide/dependency_locking.html
Gradle's implementation was inspired by the Nebula plugin: https://github.com/nebula-plugins/gradle-dependency-lock-plugin
Version ranges do work well, when used as input to whatever updates your locking mechanism. So, for Gradle, you can actually just target specific dependencies that will look to resolve version ranges you've specified for:
gradle classes --update-locks org.apache.commons:commons-lang3,org.slf4j:slf4j-api
Or, you can just say "go update all my deps":
gradle dependencies --write-locks
Specifying resolution strategies is also worth reviewing, if you're looking into automation: https://docs.gradle.org/current/userguide/dependency_resolution.html
Both Maven, SBT and Gradle have what you're describing. It's called "using released (or fixed) versions". A released version looks like 1.2.3, as compared to a version range [1.2.3,), or a snapshot (1.2.3-SNAPSHOT).
If all your dependencies are using released versions, you will achieve what you're describing.
Version ranges are a valid form of versions as well, depending on your use case, but I would normally advise against them, unless they're used for parent POM-s, or just during active development. Version ranges can come handy when you'd like to not have to keep updating the fixed version of a third-party, or parent POM, if you're certain that the respective artifact can in no way break things for you (and, trust me, this does happen a lot with version ranges). Fixed versions should be used when you'd like to guarantee that the code will build and work against what you originally devised and tested it.
There is no need to have a feature such as "lock file", or anything like this, if your pom.xml strictly defines the versions of your dependencies.
If you read the documentation regarding dependency management, you will see that this is indeed so:
Maven
Gradle
SBT

why javax.annotation of different organization are different

I'm using guava and jetty in my java project, while i found an odd problem.
In guava,it depend javax.annotation has so many class while in jetty the javax.annotation has less than in guava. What's the difference between them.
What's more,the javax.annotation in jetty is signed,while in guava is not signed. When the jvm load the class from the package javax.annotation in guava.jar and jetty.jar,it throw the SecurityException cause one package is signed and the others is not signed.
So,what's the real difference of the javax.annotation package between the guava.jar and the jetty.jar.
The package in guava:
no sign and has lot of class
The package in jetty:
signed and has little class
There are different libraries that provide types in the javax.annotations package. The one Guava is using is a JAR from the (now abandoned) JSR-305 proposal. In Maven this is com.google.code.findbugs:jsr305:1.3.9.
The other JAR appears to be org.eclipse.jetty.orbit:javax.annotation:1.1.0.v201108011116. It's not clear where that JAR comes from, and it appears to have been uploaded in 2011 and then never updated since.
The conflict between these two JARs appears to be a well-known one.
It appears that the "dependency" on this JAR was added in Guava release 13, but the release notes for that version say:
Made findbugs a provided dependency to avert dep conflicts when using findbugs 2.0. The side-effect of this change is that projects which relied upon Guava to grant access to the JSR-305 annotations "for free" will break unless they provide their own direct dependency on that jar (or an equivalent). Projects should always have been directly depending on JSR-305 (per maven best-practice), but this change makes that should into a must.
Because of this, from what I understand, Guava doesn't actually have any dependency on the com.google.code.findbugs:jsr305:1.3.9 JAR. So you should be free to exclude that dependency and provide your own javax.annotations JAR that you use instead. Alternatively, if you prefer the com.google.code.findbugs:jsr305 JAR, you should exclude the org.eclipse.jetty.orbit:javax.annotation dependency from your build. You can refer to the Maven documentation for how to exclude the unwanted dependency.

How do I tell maven that an artifact has been renamed?

Consider the following scenario.
I have a Java 1.8 project that depends on a java 8 artifact called foo-bar and a java 5 artifact called baz-qux. baz-qux also depends on foo-bar. But it uses the special slimmed down java 5 build called foo-bar-java5. Thus by including baz-qux into my project, I transitively bring in foo-bar-java5. In the end I have an undesirable state where I now have foo-bar and foo-bar-java5. Apparently there's no way to do a global exclude. So I can't just exclude foo-bar-java5. Instead, I must clutter up my cluttered pom and exclude it everywhere it will be pulled in transitively.
With that said, is there any way I can specify that foo-bar provides foo-bar-java5? Or is the only option to truly exclude foo-bar-java5 everywhere?
Here is an answer from another question that may work for you:
https://stackoverflow.com/a/9623517/2879838
Basically, explicitly list the foo-bar-java5 as a dependency in your project and list it as provided. This will tell maven not to put that jar into the archive during the build. This should be a lot less messier than excluding it everywhere.

Categories