I’m trying to compile the Code Example 3 from this article explaining the Swing Application Framework (JSR 296) with Gradle and to run it from the command line with the java command.
My directory layout looks like this:
├── build.gradle
└── src/
└── main/
└── java/
└── demo/
└── BasicSingleFrameApp.java
The build.gradle file defines a dependency to the appframework:
apply plugin: 'java'
repositories {
mavenCentral()
}
dependencies {
compile 'org.jdesktop:appframework:1.0.3'
}
And this is the BasicSingleFrameApp.java file, a copy of the example of the article mentioned above, enhanced by a package declaration:
package demo;
import org.jdesktop.application.*;
import javax.swing.*;
import java.awt.*;
public class BasicSingleFrameApp extends SingleFrameApplication {
JLabel label;
#Override
protected void startup() {
getMainFrame().setTitle("BasicSingleFrameApp");
label = new JLabel("Hello, world!");
label.setFont(new Font("SansSerif", Font.PLAIN, 22));
show(label);
}
public static void main(String[] args) {
Application.launch(BasicSingleFrameApp.class, args);
}
}
Compiling with gradle build works fine and without errors.
But when I then try to run the BasicSingleFrameApp with
$ java -cp build/classes/main/ demo.BasicSingleFrameApp
I get the error message:
Error: Could not find or load main class demo.BasicSingleFrameApp
When I replace the BasicSingleFrameApp class with a simple “Hello, world!” printing class without dependencies, everything works fine.
I’m confused, because in my understanding I correctly set up the classpath and I don’t understand why the main method (which has the right signature) cannot be found.
This is my Java version:
java version "1.8.0_20"
Java(TM) SE Runtime Environment (build 1.8.0_20-b26)
Java HotSpot(TM) 64-Bit Server VM (build 25.20-b23, mixed mode)
which I am running an Mac OS X Yosemite 10.10.2.
OK, so #chuchikaeschtli helped me to get to the core of the problem, namely that Gradle manages dependencies for compiling and reports to do so for runtime, but more manual tasks are required to make these dependencies available during runtime.
What still confuses me is the unintuitive error message I got:
Error: Could not find or load main class demo.BasicSingleFrameApp
I would have expected a problem that has to do with missing dependencies at runtime to report an error like
error: package org.jdesktop.application does not exist
which is the kind of error I get when these dependencies are missing at compile time.
And as Gradle gave me the following report about runtime dependencies (by running gradle dependencies) I thought that Gradle somehow manages these:
compile - Compile classpath for source set 'main'.
\--- org.jdesktop:appframework:1.0.3
\--- org.jdesktop:swing-worker:1.1
…
runtime - Runtime classpath for source set 'main'.
\--- org.jdesktop:appframework:1.0.3
\--- org.jdesktop:swing-worker:1.1
…
In the end, after knowing that it is a problem of missing runtime dependencies I found several ways to provide them, which I will share.
First solution: Linking to the cache
As stated in this Stackoverflow answer Gradle caches dependencies in $HOME/.gradle, but the actual path to them is tricky. The answer describes a small Gradle task that outputs the full path of each dependency in the cache (in this example for the compile configuration, which is what I need):
task showMeCache << {
configurations.compile.each { println it }
}
In my case gradle showMeCache reports:
$HOME/.gradle/caches/modules-2/files-2.1/org.jdesktop/appframework/1.0.3/338045feff6e61df237aafd11b6f3fe1a3b4e60e/appframework-1.0.3.jar
$HOME/.gradle/caches/modules-2/files-2.1/org.jdesktop/swing-worker/1.1/dc9f8d6f7236087924aad28fbec794a087dd1b3d/swing-worker-1.1.jar
These are long and nasty file paths, but I’m able to construct a java command that works in the style #chuchikaeschtli suggested:
java \
-cp build/classes/main/\
:$HOME/.gradle/caches/modules-2/files-2.1/org.jdesktop/appframework/1.0.3/338045feff6e61df237aafd11b6f3fe1a3b4e60e/appframework-1.0.3.jar\
:$HOME/.gradle/caches/modules-2/files-2.1/org.jdesktop/swing-worker/1.1/dc9f8d6f7236087924aad28fbec794a087dd1b3d/swing-worker-1.1.jar \
demo.BasicSingleFrameApp
This works, but of course this does not feel very “right”. But it helps to understand the problem: it was really a matter of missing dependency jars.
Second solution: Syncing dependencies to the build directory
In the section Using the Sync task of the Gradle User Guide it explicitly uses an example that describes a better solution to the problem at hand:
Here is an example which maintains a copy of the project's runtime dependencies in the build/libs directory.
task libs(type: Sync) {
from configurations.runtime
into "$buildDir/libs"
}
After running this task with gradle libs I’m able to construct a much simpler working java command:
java -cp build/classes/main/:build/libs/appframework-1.0.3.jar:build/libs/swing-worker-1.1.jar demo.BasicSingleFrameApp
Remember: these dependencies haven’t been synced into the build directory by default!
Third solution: Using Gradle to run the application
With the Application Plugin Gradle itself provides a convenient solution to running an application. After adding these two lines to my build.gradle
apply plugin:'application'
mainClassName = 'demo.BasicSingleFrameApp'
I was able to succesfully start the application with just gradle run. Simplest solution so far!
Note that the application plugin also delivers a task named installDist that within build/install creates a runnable distribution of the app and all its dependencies, complete with start scripts for UNIX and Windows systems. This has the advantage that Gradle is not needed for execution of the application.
Also, if you like the gradle run task of the Application Plugin but are concerned about whether Gradle is available at the target system, have a look at the Gradle Wrapper.
Fourth solution: Creating a One-JAR, Fat Jar, or Uber Jar
There seem to exist Gradle-only ways and Gradle plugins that help with the creation of these jars that include all of the required dependencies for the application.
In the case of the gradle-one-jar-Plugin the application can then be started with a java command like:
java -jar build/libs/YOUR_APP_NAME-standalone.jar
from what I get is that you are starting the app just using java so you have to tell java where to find the swing application framework jar you downloaded. so the command should look something like
java -cp pathtojdektopjar/jdesktopjar.jar;build/classes/main/ demo.BasicSingleFrameApp
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.
Starting with badass-runtime-example-javafx which works for me, I copied this project and extended it with my own code. I created an image with gradlew runtime. When executing the generated .bat file on Windows, I get "Error: JavaFX runtime components are missing, and are required to run this application".
In build.gradle, I added an additional module javafx.fxml:
javafx {
modules = ['javafx.controls', 'javafx.fxml' ]
}
I also ran the suggestModules task and added the suggested modules to
runtime {
modules = ['java.naming', 'java.xml ...']
}
My question is how I can analyze which modules are missing from the Java runtime. I see that java.exe is executed only with -classpath (which includes the application jar) and the main class parameter.
Is java --list-modules the answer? I see there is a large file "modules" in build\image\lib which looks important to me.
I am looking for an analytical method of discovery to solve the problem.
I had an existing project without Gradle and needed to add com.google.code.gson:gson:+ library to work with JSON objects. To begin with I ran either gradle init or gradle build, I'm not sure. This caused my java classes with a main() not to run as the source path was wrong/changed. I have changed the structure following advice to at least get the classes to compile and run, but I still have this warning in run configurations "Warning: Class 'Main' not found in module 'src'" ;
If I set Use classpath of module to src.main, the warning goes away but when I run Main.main() Gradle seems to execute Gradle tasks, like this - this will run indefinitely;
Here is my project structure;
This is my build.gradle file;
/*
* This file was generated by the Gradle 'init' task.
*
* This generated file contains a sample Java project to get you started.
* For more details take a look at the Java Quickstart chapter in the Gradle
* User Manual available at https://docs.gradle.org/6.3/userguide/tutorial_java_projects.html
*/
plugins {
// Apply the java plugin to add support for Java
id 'java'
// Apply the application plugin to add support for building a CLI application.
id 'application'
// idea plugin? // I added this to original build.gradle file
id 'idea'
}
repositories {
// Use jcenter for resolving dependencies.
// You can declare any Maven/Ivy/file repository here.
jcenter()
mavenCentral()
google()
}
dependencies {
// This dependency is used by the application.
implementation 'com.google.guava:guava:28.2-jre'
// Use JUnit test framework
testImplementation 'junit:junit:4.12'
// For use with JSONUtil class // I added this to original build.gradle file
compile 'com.google.code.gson:gson:+'
}
application {
// Define the main class for the application.
mainClassName = 'java.Main' // changed to 'Main' and I can `gradle run` seems to actually run Main.java
}
I have imported com.google.gson.JsonObject and com.google.gson.JsonParser from com.google.gson:gson:2.8.6 library, with no code inspection warnings, i.e available at compile time. If I run my code with a JsonObject jsonObject = new JsonObject I get the error;
Exception in thread "main" java.lang.NoClassDefFoundError: com/google/gson/JsonParser
at HttpUtils.getAccessToken(HttpUtils.java:80)
at Main.auth(Main.java:75)
at Main.play(Main.java:36)
at Main.main(Main.java:17)
Caused by: java.lang.ClassNotFoundException: com.google.gson.JsonParser
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
... 4 more
Line 80 of HttpUtils.java;
JsonObject jsonResponse = JsonParser.parseString(response.body()).getAsJsonObject(); // todo: status 200 "success" else failed
accessToken = jsonResponse.get("access_token").getAsString();
System.out.println(accessToken);
I understand this means that JVM can't compile a .class for JsonParser? I suppose this means the compiler has no knowledge of the library existing, which makes me suspect that Gradle isn't configured properly with the project, as it has downloaded the library, but not added a path to it?
I have tried gradle cleanIdea and then gradle idea. I have rebuilt the the project. I have "Mark directory as source root" on various directories for testing being careful to revert when it failed to change behaviour.
Edit;
I have added a package com.example in the src.main.Java directory and added the java files.
I edited run configuration for Main.java to
Main class: com.example.Main
Use classpath of module: src.main
I also changed the build.gradle file to;
application {
// Define the main class for the application.
mainClassName = 'com.example.Main'
}
Main runs but I am stuck at this point, which seems to run indefinitely;
Also, I am sure I right clicked on build.gradle and selected import, although I can't recreate this as the option isn't available now.
Edit 2;
I have been able to get the classes Main and Test with main() to run by putting them in the test/java/src package, and using unusual run configuration with warnings. Although on closer inspection, it seems to be running code that is previously compiled somewhere, as any changes I make aren't reflected in output.
Here is my project structure at the moment;
This is my run configuration that actually runs main in the standard output console, rather than a Gradle Task. It's clearly wrong, as Main is not in the com.example package or src.main module. If I set it correctly using module src.test and main class src.Main Gradle runs as screenshot 5.
Edit 3;
I see now that Gradle has took over responsibility to build and run the java files. I didn't know running in the output could be done with another CLI app and I admit it confused me, so please forgive anything above that seems stupid, I'm learning and figuring this out as I go.
I found in InteliJ settings Build, Execution, Deployment > Build Tools > Gradle I can change the Build and run using option between InteliJ IDEA and Gradle. The only issue I'm having with Gradle now I understand what is happening is Gradle doesn't seem to update my .class files when I run my main() with Gradle. Maybe this is for another question though.
mainClassName = 'java.Main' // changed to 'Main' and I can "gradle run" seems to actually run Main.java
This is not correct. Based on screenshot - you have not package named java (also I doubld that this is a valid name for a Java package). Create proper package inside src/main/java directory and specify it in the Main source file and in build.gradle file.
Also make sure you have imported build.gradle file in IDE, see Link a Gradle project to an IntelliJ IDEA project
I'm experimenting with building my application with Java 1.8.0_144 to workaround an issue that apparently started after that. I run this command to build:
gradle jfxNative -Dorg.gradle.java.home="c:\Program Files\Java\jdk1.8.0_144"
and it stops with this error:
Execution failed for task ':jfxNative'.
> Not found MSVC dlls
Where and how do I get these MSVC dlls?
The full output looks like this:
c:\...\>gradle jfxNative -Dorg.gradle.java.home="c:\Program Files\Java\jdk1.8.0_144"
> Task :jfxNative
The jar lib\lombok-1.16.18.jar has a main class lombok.launch.Main that does not match the declared main tech.dashman.dashman.ConfiguratorApp
The jar lib\jna-4.5.0.jar has a main class com.sun.jna.Native that does not match the declared main tech.dashman.dashman.ConfiguratorApp
The jar lib\javassist-3.22.0-CR2.jar has a main class javassist.CtClass that does not match the declared main tech.dashman.dashman.ConfiguratorApp
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':jfxNative'.
> Not found MSVC dlls
* Try:
Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output. Run with --scan to get full insights.
* Get more help at https://help.gradle.org
BUILD FAILED in 8s
5 actionable tasks: 2 executed, 3 up-to-date
My current list of dependencies look like this:
dependencies {
compile "tech.dashman:dashmancommon:1.0.0-SNAPSHOT"
compile "org.projectlombok:lombok:1.16.18"
compile "org.springframework:spring-web:5.0.2.RELEASE"
compile "org.springframework.retry:spring-retry:1.2.2.RELEASE"
compile "com.fasterxml.jackson.core:jackson-databind:2.9.3"
compile "org.kordamp.ikonli:ikonli-javafx:2.1.0"
compile "org.kordamp.ikonli:ikonli-fontawesome5-pack:2.1.1"
compile "net.harawata:appdirs:1.0.1"
compile "io.sentry:sentry:1.6.4"
compile "org.javassist:javassist:3.22.0-CR2"
testCompile "junit:junit:4.12"
}
I tried adding
compile "net.java.dev.jna:jna-platform:4.5.1"
to that list but I'm still getting the same error when trying to build the installer.
Adding it to my buildscript dependencies did not change the error either:
buildscript {
repositories {
mavenCentral()
jcenter()
}
dependencies {
classpath "de.dynamicfiles.projects.gradle.plugins:javafx-gradle-plugin:8.8.2"
classpath "com.github.ben-manes:gradle-versions-plugin:0.17.0"
classpath "de.dynamicfiles.projects.javafx.bundler:custom-file-extension-windows-bundler:1.0.2-SNAPSHOT"
classpath "net.java.dev.jna:jna-platform:4.5.1"
}
}
Maintainer of the javafx-maven-plugin/Author of the javafx-gradle-plugin here!
Even if this is a very old post, and I did not see this one before (sorry), I might have an answer to this issue. And it happens even today.
While debugging an issue on the plugin (https://github.com/javafx-maven-plugin/javafx-maven-plugin/issues/395) I found that sometimes there are 2 files missing inside the JDK itself.
In case someone has installed any non-Oracle JDK, e.g. OpenLogic, the provided files are incomplete.
The JDK provided by OpenLogic (other others) provides a file called ant-javafx.jar, where all the system native files are contained (can be found inside the installed JDK in the lib-folder). That file is missing the required runtime files, that are required by the launcher.
Here a screenshot of what it looks like with OracleJDK:
Here a screenshot of what it looks with OpenLogic JDK:
As you can see, there are files missing in there, which makes that JDK not equivalent to the OracleJDK sadly.
On maven-plugin level I can not do anything here to fix this, but you might be able to fix that by modifying that JAR-file by adding the missing files. You should be able to find them on your local windows installation at C:\Windows\System32\vcruntime140.dll or C:\Windows\System32\msvcp140.dll. If these files are missing, you just can download a official runtime installer of these Visual C++ Redistributable files here:
for 32-bit: https://aka.ms/vs/16/release/vc_redist.x86.exe
for 64-bit: https://aka.ms/vs/16/release/vc_redist.x64.exe
Another alternative would be to install a different OpenJDK, for example ojdkbuild (https://github.com/ojdkbuild/ojdkbuild). Using chocolatey on Windows just makes this a one-liner choco install ojdkbuild8 and does contain these required files.
Running into problems extracting tasks from a build.gradle file to then be applied, back into the app/root build.gradle file. The compiler can resolve MarkupBuilder and JsonSlurper fine but cannot resolve the following: import org.apache.commons.lang.StringEscapeUtils.
I've tried adding it as a dependency within the newly created script and also within the app and project levels.
'org.apache.commons.lang:commons-lang:3.5'
The error is below
Could not compile script '/project/app/newscript.gradle'.
startup failed:
> script '/project/app/newscript.gradle': 18: unable to resolve class org.apache.commons.lang.StringEscapeUtils
# line 18, column 1.
import org.apache.commons.lang.StringEscapeUtils
^
1 error
Am I doing something wrong or is this not possible? Would I need to include the script in a different way than apply script: newscript.gradle or another plugin within the newscript.gradle?
A Gradle script is basically a Groovy file. Which in turn gets compiled into JVM bytecode, similar to Java classes. So when compiling a script with an import, the imported classes must be on the classpath. Some classes like the MarkupBuilder are available by default (included either by Groovy or Gradle).
You have to add something like this to be able to use the classes in your script:
import org.apache.commons.lang.StringEscapeUtils;
buildscript {
repositories {
mavenCentral();
}
dependencies {
classpath 'org.apache.commons.lang:commons-lang:3.5'
}
}
The buildscript closure will add the library on the classpath of the Gradle script and you should be able to use its classes.