I'm creating a sample demo application with JavaFX in IntelliJ, but I need to use a library called the JavaFaker library. I'm using Gradle as the build system, but every time I try to add the library, either as the implementation in the build.gradle file, or via IntelliJ project structure options, the module.java file says error: module not found. I've already tried adding it to modules but nothing changes.
module-info.java
module com.example.demo1 {
requires javafx.controls;
requires javafx.fxml;
requires javafaker;
opens com.example.demo1 to javafx.fxml;
exports com.example.demo1;
}
build.gradle
plugins {
id 'java'
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.10'
id 'org.beryx.jlink' version '2.24.1'
}
group 'com.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
ext {
junitVersion = '5.8.2'
javaFakerVersion = '1.0.2'
}
sourceCompatibility = '17'
targetCompatibility = '17'
tasks.withType(JavaCompile) {
options.encoding = 'UTF-8'
}
application {
mainModule = 'com.example.demo1'
mainClass = 'com.example.demo1.HelloApplication'
}
javafx {
version = '17.0.1'
modules = ['javafx.controls', 'javafx.fxml']
}
dependencies {
implementation("com.github.javafaker:javafaker:${javaFakerVersion}")
testImplementation("org.junit.jupiter:junit-jupiter-api:${junitVersion}")
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:${junitVersion}")
}
test {
useJUnitPlatform()
}
jlink {
imageZip = project.file("${buildDir}/distributions/app-${javafx.platform.classifier}.zip") as RegularFile
options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
launcher {
name = 'app'
}
}
jlinkZip {
group = 'distribution'
}
error message
> Task :HelloApplication.main() FAILED
Error occurred during initialization of boot layer
java.lang.module.FindException: Module javafaker not found, required by com.example.demo1
I tried for a while to get this to work with Gradle but was unable to. I don't know Gradle well, but unless you do, I don't advise trying it.
Alternate option: use a static import
I didn't try this, but this is suggested in another answer.
Before you try this, see:
What's the difference between requires and requires static in module declaration
It is IMO, a bit of a hack in this usage case. This makes the module optional at runtime. But, if the module is on the classpath instead of the module path its code can still be used. More information quoted from the linked answer:
A requires static clause expresses a dependency that is optional at
run time. That means at compile time the module system behaves exactly
as described above.
At run time, on the other hand, it mostly ignores requires static
clauses. If it encounters one, it does not resolve it. That means, if
an observable module is only referenced with requires static, it does
not make it into the module graph!
Alternate option: Non-modular project
You can fix this issue by making your project non-modular:
Delete your module-info.java file.
Run your application with JavaFX modules on the module-path.
The org.openjfx.javafxplugin you are already doing will help achieve this by specifying the modules to be used.
To execute the application directly in the IDE rather than through Gradle, you will need to specify the module options to the VM for the IDE execution configuration (information on that is in the getting started documentation at openjfx.io).
For packaging, switch to using the badass-runtime-plugin rather than the badass-jlink-plugin. This will package the application via jpackage rather than jlink (which cannot package non-modular applications or applications with automatic modules).
In the application block of your build file, you no longer need to specify the module for your application as you no longer have one.
While that means that your application is no longer modular, in this case, in my opinion, this is not such a big loss. The dependencies you are using are not well-defined modules, so you can't use jlink to create a package for your application, and you don't have the level of modular encapsulation and definition you would normally receive for fully modular projects.
For more information, see the Getting started instructions at:
https://openjfx.io/openjfx-docs/
Under the sections "Non-Modular with Gradle" for your selected IDE.
Alternate option: Using Maven
It is easy to get this to work with Maven.
Create a new JavaFX project
Choose Maven as your build system instead of Gradle.
Add the javafaker dependency to your pom.xml.
<dependency>
<groupId>com.github.javafaker</groupId>
<artifactId>javafaker</artifactId>
<version>1.0.2</version>
</dependency>
Press the refresh icon in the Maven window to reimport the Maven project into the IDE.
Add the requires clause for the javafaker module into your module-info.java
requires javafaker;
Add the code to use javafaker to your app.
I don't have code to use javafaker, so I could not verify that the last step would work, but, give it a try . . .
Why you can receive this issue when using Gradle, but not Maven
Looking at the Gradle Documentation section "Using libraries that are not modules":
A third case are traditional libraries that provide no module information at all — for example commons-cli:commons-cli:1.4. Gradle puts such libraries on the classpath instead of the module path. The classpath is then treated as one module (the so called unnamed module) by Java.
This is the case with the javafaker dependency that you are using. It has no module-info.java and does not define the property Automatic-Module-Name in its manifest file (which are the other two cases in the section). Both the other cases result in Gradle putting the library on the module path, but the case you have means that it is on the class path.
This is a problem when you want to access the code from a named module that you define, which you have because you created a module-info.java.
Your module can only find code and resources of modules it requires (which need to be on the module path), so you add requires javafaker to the module-info.java, and get the following when you try to run through the IDE:
java.lang.module.FindException: Module javafaker not found, required by com.example.demo1
So you remove the requires javafaker from the module-info.java as advised by the Gradle documentation I linked and you get the following when you try to compile:
Package 'com.github.javafaker' is declared in module 'javafaker', but module 'com.example.demo1' does not read it
So you must place the library in the module-info to use it, but you can't place the library in module-info because Gradle puts in on the classpath -> catch-22.
There are workarounds to this such as providing VM arguments to allow access to the unnamed module (which is the classpath), or maybe modifying the module path handling of the Gradle build and/or IDE somehow (I don't know how), but they are pretty ugly.
On the other hand, for this case, Maven acts differently from Gradle, it places the dependent library on the module path, even if it does not have a module-info.java or Automatic-Module-Name defined. This means that it was (for me) much easier to set up and use.
Incidental advice on module naming
This is not an error, but note: Although module names with numbers in them are now allowed due to a change in the module system specification, it is probably best not to put numbers in module names to prevent the module name and version info being confused.
I've had a similar issue recently. Adding static to the requires statement helped however. Maybe this will fix your issue without having to switch to maven.
So you'd need to add: requires static javafaker;
Related
Please forgive me in advance as I've been using Java since the early 2000s and have been slow to transition new projects toward being compliant with Project Jigsaw and modules (introduced in Java 9.) I'm stuck and hoping someone can help me out. I've tried to create as minimal project as possible to help me focus on the problem. I'm using:
JavaFX - I followed the instructions on https://openjfx.io/openjfx-docs/ using their guidance for Modular Gradle with IntelliJ, though I'm not interested in building an image yet, so I'm leaving jlink out of it. This worked just fine.
Tablesaw for some pandas-like data crunching
JUnit 5.8.2
I have only one class file, HelloFX down the package org.hello.
Executing..
$ .\gradlew run
I get a ResolutionException error from Gradle while trying to run the project:
Error occurred during initialization of boot layer java.lang.module.ResolutionException: Modules shims and RoaringBitmap export package org.roaringbitmap to module listenablefuture
My project tree (all located in a root folder called TestProject):
./gradle
./gradlew
./build.gradle
./.gradle
./gradlew.bat
./settings.gradle
./.idea
./src
./src/test
./src/test/resources
./src/test/java
./src/main
./src/main/resources
./src/main/java
./src/main/java/module-info.java
./src/main/java/org
./src/main/java/org/hello
./src/main/java/org/hello/HelloTS.java
Here are the pertinent files:
settings.gradle
rootProject.name = 'TestProject'
build.gradle
plugins {
id 'application'
id 'java'
id 'idea'
id 'org.openjfx.javafxplugin' version '0.0.12'
id 'org.javamodularity.moduleplugin' version '1.8.10'
}
group 'org.example'
version '1.0-SNAPSHOT'
repositories {
mavenCentral()
}
javafx {
version = "17.0.2"
modules = [ 'javafx.controls', 'javafx.fxml' ]
}
test {
useJUnitPlatform()
}
dependencies {
implementation 'tech.tablesaw:tablesaw-core:0.42.0'
testImplementation 'org.junit.jupiter:junit-jupiter:5.8.2'
}
application {
mainModule = "$moduleName"
mainClassName = "org.hello.HelloFX"
}
module-info.java
module TestProject {
requires javafx.graphics;
requires javafx.controls;
requires tablesaw.core;
exports org.hello;
}
What I've discovered so far:
Eliminate Tablesaw - Comment out requires tablesaw.core; from module-info.java and implementation 'tech.tablesaw:tablesaw-core:0.42.0' from build.gradle and my little JavaFX app works just fine with modules, but then I lose Tablesaw.
Eliminate modules - Remove module-info.java, then comment out the mainModule line in build.gradle. Then, I can run both a sample JavaFX program and a sample Tablesaw program by simply changing mainClassName to the program I want to run. I can even add some Tablesaw code in my sample JavaFX app, and it works. This is my backup plan, since it gives me what I want, albiet without modularization.
So, I'm really stumped here. This post didn't help, nor did any other that tried to address this weird ResolutionException error from Gradle. My guess is that Tablesaw is not module compliant? Or I need some sort of exclusion clause in my dependencies for Tablesaw? I tried to use the java-library plugin and use the api clause in build.gradle for Tablesaw as it seemed like that plugin is for non-modular libraries, but that didn't work.
There must be a way to do this, but admittedly I am about ready to throw in the towel and, yet again, just go back to non-modular development for my latest project. I have been a huge fan of Java since its inception, (even fully certified back in the Sun Microsystems days! That'll date me!) I understand why modularization has been introduced. Makes complete sense! But frankly, I'm finding its implementation to be quite challenging to embrace.
Any help would be greatly appreciated. Thank you kindly!
Tablesaw 0.42.0 isn’t built to support the Java module system.
It has no module-info.
It uses shading for its dependencies
It uses dependencies like RoaringBitmap that have issues if you try to use them with the module system.
I suggest you log an issue with Tablesaw requesting that they modularize the library.
In the meantime, JavaFX should be run from the module path as it is only supported that way, but it will probably be better to run Tablesaw from the class path.
You can put JavaFX on the module path and add the JavaFX modules via command line switches.
Put Tablesaw on the class path, don’t add it as a module.
Don’t define a module-info for your app, create a non-modular app that adds the JavaFX modules via switches. This means that your app code is also on the class path so it can access Tablesaw and it can also access JavaFX modules through virtue of the command line switches.
I don’t use Gradle, so I can’t provide you the exact build script you need for this.
For more info see:
openjfx.Io getting started documentation on non-modular with gradle for your IDE
You will probably be able to package your app using the:
badass runtime plugin.
Problem
How do you create a java library jar that both:
is java module (has module-info)
has a depending legacy (non-module) jar. (like commons-exec)?
The dependency is an implementation detail - should not be exported.
Sources
Having the following build.gradle (using gradle-6.8):
plugins {
id 'java-library'
}
group = 'test'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '15'
repositories {
mavenCentral()
}
java {
modularity.inferModulePath = true
}
dependencies {
implementation 'org.apache.commons:commons-exec:1.3'
}
and the following module-info.java:
module test.module {
requires commons.exec;
}
Errors
I'm getting the following compile error:
module-info.java:2: error: module not found: commons.exec
requires commons.exec;
^
If I don't include requires commons.exec then the error becomes:
error: package org.apache.commons.exec is not visible
import org.apache.commons.exec.CommandLine;
^
(package org.apache.commons.exec is declared in the unnamed module,
but module test.module does not read it)
commons.exec module name?
Running jar --file=commons-exec-1.3.jar --describe-module does output:
No module descriptor found. Derived automatic module.
commons.exec#1.3 automatic
requires java.base mandated
contains org.apache.commons.exec
contains org.apache.commons.exec.environment
contains org.apache.commons.exec.launcher
contains org.apache.commons.exec.util
So commons.exec looks like a valid module name for commons-exec-1.3.jar. Intelij Idea seem to agree and does auto-complete it in module-info.java. Though it fails at build time.
I managed to overcome the same issue using java-module-info plugin.
This plugin allows you to add module information to a Java library
that does not have any. If you do that, you can give it a proper
module name and Gradle can pick it up to put it on the module path
during compilation, testing and execution.
plugins {
id 'java-library'
id("de.jjohannes.extra-java-module-info") version "0.6"
}
Add this section into your build.gradle to add the commons-exec module information
extraJavaModuleInfo {
module("commons-exec-1.3.jar", "org.apache.commons.exec", "1.3") {
exports("org.apache.commons.exec")
}
}
Add requires org.apache.commons.exec; to your module-info.java
EDIT 1:
Gradle 7.0 comes with full support for the Java module system. Users can now build, test, and run Java modules via Gradle. The mere presence of module-info.java will let Gradle infer that your jar is a module and has to be put on the modulepath instead of the traditional classpath.
Using libraries that are not modules
I want to shrink a fat jar which has been created using the Gradle shadow plugin. The reason for this is that I need to keep some classes within a specific package within the finished jar as they are used for dynamic class generation (e.g. Class.forName(...)) and that plugin won't allow me to specify a package to keep when calling minimize() within the shadow jar task). I am therefore using the Proguard Gradle plugin.
buildscript {
dependencies {
classpath 'net.sf.proguard:proguard-gradle:6.2.2'
classpath 'net.sf.proguard:proguard-base:6.2.2'
}
}
plugins {
id 'java-library'
id 'application'
id 'com.github.johnrengelman.shadow' version '5.2.0'
}
sourceCompatibility = 1.11
shadowJar {
zip64 true
}
application {
mainClassName = 'com.example.MyApp'
}
task proguard(type: proguard.gradle.ProGuardTask) {
dependsOn shadowJar
injars shadowJar
outjars "${buildDir}/libs/${project.name}-${project.version}-proguard.jar"
dontobfuscate
dontoptimize
keep 'class com.example.packagetokeep.*'
// next block taken verbatim from Proguard's documentation examples:
// Automatically handle the Java version of this build.
if (System.getProperty('java.version').startsWith('1.')) {
// Before Java 9, the runtime classes were packaged in a single jar file.
libraryjars "${System.getProperty('java.home')}/lib/rt.jar"
} else {
// As of Java 9, the runtime classes are packaged in modular jmod files.
libraryjars "${System.getProperty('java.home')}/jmods/java.base.jmod", jarfilter: '!**.jar', filter: '!module-info.class'
}
}
So, what I am trying to do is create a fat jar with all dependencies in it, then pass to Proguard to minimize. For now, I don't want to optimise or obfuscate.
When I try to run the task, I get a lot of errors of the form can't find referenced class for classes like javax.xml.stream.events.Attribute, java.sql.Timestamp, java.util.logging.Level. I think it's because Proguard is not picking up the Java runtime libraries. Here is the error summary:
Warning: there were 75920 unresolved references to classes or interfaces.
You may need to add missing library jars or update their versions.
If your code works fine without the missing classes, you can suppress
the warnings with '-dontwarn' options.
(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedclass)
Warning: there were 670 unresolved references to program class members.
Your input classes appear to be inconsistent.
You may need to recompile the code.
(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedprogramclassmember)
Warning: there were 4 unresolved references to library class members.
You probably need to update the library versions.
(http://proguard.sourceforge.net/manual/troubleshooting.html#unresolvedlibraryclassmember)
How do I make Proguard work with Gradle and Java 11?
I have a project built with Gradle version 6.4 and JDK 8. I'm trying to use the Gradle plugin for Test Fixtures (java-test-fixtures) but I have some issues with the dependencies.
According to the Gradle page linked above, the project should be structured like this:
core-module
-- src
-- main
-- java
-- test
-- java
-- testFixtures
-- java
While the build.gradle.kts file has the following dependencies section:
dependencies {
api("com.my.external.project:1.0")
// ... more API dependencies
testFixturesCompileOnly(project(":core-module"))
testFixturesApi("junit:junit:4.12")
// ... more test dependencies
}
Now, in IntelliJ (the IDE I'm using) classes in the testFixtures/java source folder see the classes in the main/java source folder. So I can add new Java classes under testFixtures/java that have dependencies on those under main.
However, I won't be able to import the dependencies from the external library com.my.external.project:1.0. The problem is confirmed when I try to run the Gradle task compileTestFixturesJava.
I can duplicate the entry in the dependencies section; e.g. I can add:
testFixturesImplementationOnly("com.my.external.project:1.0")
But that is not really what I expect to do; especially when I have dozens of dependencies.
I could also define the dependencies in an array and run a for-each over them. Still, this is not the cleanest solution.
Is there a clean solution that will allow the testFixtures module to use the dependencies declared in the main module?
Most important concept in the Gradle java-test-fixtures plugin is stated in their documentation:
[this plugin] will automatically create a testFixtures source set, in which you can write your test fixtures. Test fixtures are configured so that:
they can see the main source set classes
test sources can see the test fixtures classes
This plugin will indeed create the following dependencies: main <-- testFixtures , and testFixtures <-- test
In your case, testFixtures module should automatically depend on main sources, and also on main dependencies declared in api scope ( com.my.extenal.project:1.0)
See a similar example in a valid sample project here https://github.com/mricciuti/so-64133013 :
Simpsons class has access to Person class from main module
TestHelpers class has access to main dependencies declared in api configuration
Note that testFixtures will not inherit dependencies from the test module: if you need to use such libraries in this module (eg. JUnit, Mockito, ...) you will need to declare explicit dependency , using testFixturesImplementation or testFixturesApi configuration.
See example in core-module
plugins {
id ("java-library")
id ("java-test-fixtures")
}
dependencies {
// Main dependencies
// will be available in "testFixture" as well thanks to "api" configuration
api("org.apache.commons:commons-lang3:3.9")
api(project(":utils-module"))
// Testfixture dependencies
// ==> mockito lib will be available in testFixture module and also in consumer modules (e.g test)
testFixturesApi("org.mockito:mockito-core:3.5.13")
// Test dependencies
// dependencies specific to the "test" module; not visible from "testFixtures"
testImplementation("org.junit.jupiter:junit-jupiter-api:5.3.1")
testRuntimeOnly ("org.junit.jupiter:junit-jupiter-engine:5.3.1")
}
When adding dependencies to my project I am never sure what prefix I should give them, e.g. "classpath" or "compile".
For example, should my dependencies below be compile time or classpath?
Also, should this be in my applications build.gradle or in the module specific build.gradle?
Current build.gradle (at application level):
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'org.hibernate:hibernate-core:5.0.5.Final'
compile 'mysql:mysql-connector-java:5.1.38'
}
If buildscript itself needs something to run, use classpath.
If your project needs something to run, use compile.
The buildscript{} block is for the build.gradle itself.
For multi-project building, the top-level build file is for the root project, the specific build file is for sub-project (module).
Top-level build file where you can add configuration options common to all sub-projects/modules.
Do not place your application dependencies in top-level build file, they belong in the individual module build.gradle files
I'm going to guess that you're referencing compile and classpath within the dependencies {} block. If that is so, those are dependency Configurations.
A configuration is simply a named set of dependencies.
The compile configuration is created by the Java plugin. The classpath configuration is commonly seen in the buildScript {} block where one needs to declare dependencies for the build.gradle, itself (for plugins, perhaps).
If I understand correctly, you're confusing Project.dependencies script block with the Project.buildscript.dependencies script block (just like I did when I reached this question).
I'll try to answer this with what I found.
I think you should be already familiar with the Project.dependencies script block. In this block, we declare dependencies that are required by our source code. There are several ways to declare a dependency that we need for the project. See Gradle Tutorial: Dependency Types. I'll only mention the part that is the most relevant to this problem:
compile 'org.hibernate:hibernate-core:5.0.5.Final' is a module dependency declaration. The compile configuration (which is now deprecated by the implementation configuration.) is merely a keyword for Implementation only dependencies. It is not a keyword describing which type of dependency it is (by type here I'm following the three types defined in the tutorial, i.e. module, file, and project.)
In Gradle Tutorial: Organizing Build Logic it says:
If your build script needs to use external libraries, you can add them
to the script’s classpath in the build script itself. You do this
using the buildscript() method, passing in a closure which declares
the build script classpath.
This is the same way you declare, for example, the Java compilation
classpath. You can use any of the dependency types described in
Dependency Types, except project dependencies.
Having declared the build script classpath, you can use the classes in
your build script as you would any other classes on the classpath.
I hope things are getting clear to you now.
With classpath "com.android.tools.build:gradle:${Versions.android_gradle_plugin}" we're setting classpath method with com.android.tools.build:gradle:${Versions.android_gradle_plugin} which is a module dependency that is used by the build script itself rather than the source in your project.
On the other hand, with compile 'org.hibernate:hibernate-core:5.0.5.Final' we're declaring a module dependency required for your project with the compile configuration.
tl;dr: The classpath, compile, and implementation are all keywords that can be used against dependencies under different circumstances. The former is used when you want to pass in a dependency to the build script, and the latter is one of the configuration you may want to declare.
Android:
classpath in project build.gradle —— the implementation after classpath is only used by gradle it self, used in build script. So if i add the implementation (such as retrofit) in the project build.gradle classpath 'retrofit...', i can't get retrofit in my code!! Because —— my code can't see it, only the buildscript can see it.
implementation in app build.gradle —— add the implementation your code can use!!