When I compile my Spring Boot application in Java 9, it fails after a couple of messages such as this one:
package com.fasterxml.jackson.annotation is not visible
(package com.fasterxml.jackson.annotation is declared in the unnamed module, but module com.fasterxml.jackson.annotation does not read it)
Can someone tell me what is going on here? As I understand it, any pre-Java 9 code not in a Java-9 module will be part of the unnamed module where anything is exposed.
I'm using this as an annotation like this in my module:
#JsonIgnore
public Week getNextWeek()
{
Calendar instance = this.getFirstDay();
instance.set(Calendar.WEEK_OF_YEAR, this.week + 1);
return new Week(instance);
}
So if this is the case with the com.fasterxml.jackson.annotation package, why is the error referring to a module with that name, and why is it a problem that it does not read it?
Quoting from the JigSaw Spec:
The unnamed module exports all of its packages. This enables flexible migration, as we shall see below. It does not, however, mean that code in a named module can access types in the unnamed module. A named module cannot, in fact, even declare a dependence upon the unnamed module.
What you're looking for are Automatic Modules. In automatic modules, a jar can be placed on the module path and will automatically derive the module name from the jar itself. In case you're using Maven, that should be the artifactId.
As such, if you are using jackson-annotations in maven as following:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.0.0</version>
</dependency>
You'd still require to define it inside your module-info.java:
module example {
requires jackson.annotations;
}
After that, you're free to use the annotations within your module.
Related
While moving a project to Gradle, I stopped using my custom build of org.json which had a module-info.java fitted to it to comply to the module system. Now, I am using it via Maven normally, and as org.json is not a module by default, it gets put into the unnamed module.
My module-info looks like this:
open module mymodule {
requires java.desktop;
requires java.logging;
}
I am getting the error:
SomeSourceFile.java: error: package org.json is not visible
import org.json.*;
^
(package org.json is declared in the unnamed module, but module mymodule does not read it)
This is logical, except I don't know why my module doesn't read the unnamed module (the purpose of the unnamed module is full backwards compatibility with non-modular software so all packages are exported etc.), and how I could make my module read the unnamed module. As you can see, I have already tried making my module open to no avail.
Probably updating the Maven Compiler Plugin (to 3.8.1, for instance) will do the trick.
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
</plugin>
</plugins>
</build>
The other thing will be to require the JSON module. From their pom.xml, I can see that they declare the Automatic-Module-Name as org.json (https://github.com/stleary/JSON-java/blob/master/pom.xml#L186)
So your module-info.java will become like this:
open module mymodule {
requires java.desktop;
requires java.logging;
requires org.json;
}
I am migrating from Java 8 to Java 10, and I am running my test which now fails because of package protected classes. The build is run under maven 3.5.4 + Oracle JDK 10.0.2:
maven-compiler-plugin 3.7.0 + asm 6.2
maven-surefire-plugin 2.22.0 + asm 6.2 + junit 5.2.0
asm 6.2 is required for both compiler/surefire because of a bug in the version of ASM used by those plugins.
mockito-core 2.20.0 (but was using 2.20.0 with Java 8 before).
Eclipse Photon R
The project can be found here ide-bugs.zip (it is located at Eclipse forum because I've made this Topic on Eclipse for another problem, this time with Eclipse having local error with module).
The test is very simple: we try to mock different class, with different access level - all of which were working in Java 8.
package protected class
public class but not exported, not opened
public class not exported but opened to Mockito
public class not exported but opened to all
package protected class not exported but opened to Mockito
package protected class not exported but opened to all
In Java 8, case 1, 5 and 6 are the same (access to package protected). Case 2, 3 and 4 are the same (access to public).
The test fails because Mockito is unable to either:
class org.mockito.codegen.NotExportedOpenToMockitoProtected$MockitoMock$117073031 cannot access its superclass nodatafound.mjpmsuc.withopens.NotExportedOpenToMockitoProtected
class org.mockito.codegen.NotExportedNotOpenedPublic$MockitoMock$365628885 (in unnamed module #0x3f07b12c) cannot access class nodatafound.mjpmsuc.internal.NotExportedNotOpenedPublic (in module nodatafound.mockito_jpms_usecase) because module nodatafound.mockito_jpms_usecase does not export nodatafound.mjpmsuc.internal to unnamed module #0x3f07b12c
Mockito effectively have a Automatic-Module-Name but is seen as the unamed module because all jar found in the class path for a big "unnamed module".
While I'm fine with migrating from package-protected to non exported package, I fail to understand how I can address the problem keeping my interface/class not visible to other modules ?
[edit] updated the version of plugin/dependency one month after, no result.
I found part of answer to my problem here: https://blog.codefx.org/java/java-module-system-tutorial/#Open-Packages-And-Modules
Mockito is using reflection to access classes from module or class path.
Mockito is in the "unnamed module" because Maven adds it into the class path rather than the module path. This explains why the opens package to org.mockito never works: there is no org.mockito module.
Maven Surefire does not care to contribute to the "opens" of the module in order to allow Mockito to access it.
Mockito is (no longer?) able to mock non-private & non-final classes class. By any means package protected class are private. The error is rather explicit: Mockito create a class extending the package protected class, which now fails (it was working before, but this was probably because Mockito created the class in the same package than the one being mocked).
Nevertheless, this give a problematic configuration in the pom.xml of each module:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>
--add-opens nodatafound.mockito_jpms_usecase/nodatafound.mjpmsuc=ALL-UNNAMED
</configuration>
</plugin>
We need to explicitly add opens to an unnamed module. This should not be done in the module-info.java because it expose the module to all other modules or jars which is against encapsulation.
This is problematic because:
You need to specify it in the pom.xml for each package.
It add additional burdens to the surefire configuration which I prefer simple.
You don't have validation from the IDE; Eclipse will validate module-info.java marking invalid package.
m2e does not pass to Eclipse JUnit plugin the necessary <argLine /> making the test fail in Eclipse.
The maven approach (which is the same in Eclipse, and perhaps Gradle as far as I know) does not permits an additional module-info for the tests; eg: lets test dependency be modular (this could be probably be done using a dedicated test module per source module like Eclipse does for plugin' tests).
I've tried to "upgrade" a project using Hibernate to Java 9, but I am having problems getting the module to function properly.
The relevant part of my module-info.java looks like this:
module test {
...
requires java.base;
requires hibernate.core;
requires javax.transaction;
requires java.sql;
}
and the relevant dependencies in my POM are
org.jboss.spec.javax.transaction:jboss-transaction-api_1.2_spec, 2.0.0.Alpha1
org.hibernate:hibernate-core, 5.2.12.Final
javax.transaction:javax.transaction-api, 1.2
The problem is, if I run the program, I get a NoClassDefFoundError for javax.transaction.SystemException. I looked into this, and quite obviously, my module is missing a requires on javax.transaction.
So I add a module dependency on javax.transaction-api. I then go on and attempt to run the program again - now I'm missing java.sql.SQLException.
Here is what I am having a problem with: if I add a dependency on the module java.sql, which contains this class, I end up with a conflict:
module reads package javax.transaction.xa from both java.sql and javax.transaction.api
java.sql and javax.transaction.api contain different packages, and have one in common (javax.transaction.xa), but I require all of them.
How do I deal with this? Is there something simple I am missing?
Use version 1.3 instead of 1.2 of javax.transaction-api, in this version javax.transaction.xa has been removed.
Maven dependency:
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.3</version>
</dependency>
I have a multi-module maven project with three modules core, utils and test-utils
Core has the following dependencies definition
<dependency>
<groupId>my.project</groupId>
<artifactId>utils</artifactId>
</dependency>
<dependency>
<groupId>my.project</groupId>
<artifactId>test-utils</artifactId>
<scope>test</scope>
</dependency>
I have added Java 9 module-info.java definitions for all three modules and core's looks like this:
module my.project.core {
requires my.project.utils;
}
However I cannot figure out how to get core's test classes to be able to see the test-utils classes during test execution. When maven-surefire-plugin attempts the test run I get class not found.
If I add a requires my.project.testutils; to core's module-info.java:
module my.project.core {
requires my.project.utils;
requires my.project.testutils; //test dependency
}
Then at compile time I get an error that the my.project.testutils module can't be found (presumably because it's only brought in as a test dependency).
How does one work with test dependencies in a Java 9 modular world? For obvious reason's I don't want my main code to pull in test dependencies. Am I missing something?
With maven and java9, if your my.project.testutils is a test scope dependency, you don't need to explicitly include(requires) it in the module descriptor.
The test dependencies are taken care via the classpath itself. So you can simply remove the testutils and it would be patched by maven while executing tests.
module my.project.core {
requires my.project.utils;
}
Refer to the slide 30 pertaining to maven-compiler-plugin.
I would also suggest you take a look at Where should I put unit tests when migrating a Java 8 project to Jigsaw and this comment by Robert confirming on the implementation that maven follows.
Edit: Created a sample project drawing an analogy that the main module is same as your core, the dependency on guava is same as your utils and the junit dependency is same as your testutils.
We are working on Mavenizing our java project and we would like to setup a clean separation between interfaces and implementations for each module.
In order to do so, we want to split each module into two sub-modules one for interfaces and data objects used by them and another for implementations.
For example:
+commons
+commons-api
+commons-impl
The POMs of the modules will be configured such that no module depends on the impl sub-modules. This way no code from one module will be able to "see" implementation details of another module.
What we are having trouble with, is where to put our spring XMLs.
In our project we automatically import spring XML files using wildcard import like
<import resource="classpath*:**/*-beans.xml"/>
This way the location of Spring XMLs doesn't really matter at runtime, as all the modules get loaded into the same class loader and, the strict one way dependency rules in the POMs don't apply.
However, during development we want the IDE - we use Intellij IDEA - to recognize implementation classes referenced from the spring XMLs.
We also want IDEA to recognize beans defined in other modules.
If we put the spring XMLs in API sub-modules - they won't "see" the implementation classes in the impl sub-modules.
If we put them in the impl sub-modules, their beans won't be "seen" from other modules.
It is probably possible to configure the IDEA project to recognize spring XMLs from modules on which there is no dependency, but we prefer for our POMs to hold all the project structure information and not rely on IDEA project files.
We considered creating a third sub-module just to hold Spring XMLs (and perhaps hibernate xmls as well). For example:
+commons
+commons-api
+commons-impl
+commons-config
The external modules will depend on both commons-api and commons-config and commons-config will depend on both commons-api and commons-impl, with the dependency on commons-impl marked as "provided" (to prevent transitive resolution).
This however seems like a complex and awkward solution and we feel that there must be a better - simpler way to achieve interface/impl separation with Maven and Spring.
What you need is a runtime dependency scope:
runtime - This scope indicates that the dependency is not required for compilation, but is for execution. It is in the runtime and test classpaths, but not the compile classpath.
(https://maven.apache.org/guides/introduction/introduction-to-dependency-mechanism.html)
Define a runtime dependency from one impl module to another impl module where you use the impl classes in the *-beans.xml config. Intellij will correctly recognize this in spring configuration files, but won't auto complete them in code (but it will do that in test code).
Also if anyone used the classes in the code, compilation through maven would fail, because the runtime dependency is not on a compile class path.
You can achieve decoupling of api and impl like this:
+ commons (pom)
+ pom.xml <--- serves as a parent aggregator (see below)
+ commons-api (jar) <--- contains models, interfaces and abstract classes only
+ commons-impl (jar) <--- depends on commons-api
+ commons-config (jar) <--- depends on commons-impl only (no need to depend on commons-api as it is brought in transitively)
+ external-project (war or jar) <--- has commons-config as a dependency
Parent aggregator pom (specify build order):
<modules>
<module>commons-api</module>
<module>commons-impl</module>
<module>commons-config</module>
</modules>
The config module can be omitted if it only contains spring application context configuration. The app configuration xml should be in the classpath and folder structure of the module that contains the artifact that you are deploying. So if you are building a war artifact, the app context should be in there.
The only configuration that should be in your commons module would be in a test package of your impl module.
In short you want Idea to override maven dependency graph but avoid keeping this configuration in idea project files?
One option is to group implementation dependencies in a maven profile. This profile would not be enabled by default but you should be able to mark it as active under idea.
Two ideas come to mind:
You will have one (or more) modules where all the modules (api+impl) are dependencies, you could place your spring configuration files there.
Place the spring configuration files in the api modules and declare a dependency on the impl module with scope provided this way the implementations will be known, while there is no dependency of the api for the deployment.
commons-impl at runtime scope in external modules
commons (pom dependencyManagement) =>
+commons-api (compile)
+commons-impl (compile)
+commons-config (compile)
commons-impl (pom dependencies) =>
+commons-api (compile)
+commons-config (compile)
external modules (pom dependencies) =>
+commons-impl (runtime)
+commons-api (compile)
+commons-config (compile)
keep modules number as little as possible;
This speeds up project build time and simplifies its layout.
keep modules structure as plain as possible: single root + all sub modules in the same folder, e. g.:
pom.xml
commons-api/
commons-runtime/
module-a-api/
module-a-runtime/
...
This simplifies navigation across the project, when modules number is really high (>50)
provide runtime-scoped dependencies to the runtime modules only when they are required;
This keeps your architecture clear. Use mocks instead of explicit dependency to another runtime module.
keep your api spring contexts in api modules, define your public beans as abstract bean + interface;
keep your implementation contexts in runtime modules, override api beans with your implementations via spring profiles (use <beans profile="default").
Result: simple, transparent layout and design; full ide support; no explicit dependencies on runtime module internals.