How to change the working directory using JavaFX with Gradle? - java

The title says all. How do I change the working/runtime directory when using JavaFX with Gradle in Eclipse?
Basically, I have a project that requires log4j and initiates a basic logger which uses the "logs/" directory from the place the jar is run. This directory is being made in the home of the source, but I want it to be made in the "run/" directory. I'm assuming for other files that will be created, they will also have this same issue.
My build.gradle is this:
// Plugins
plugins {
id 'application'
id 'org.openjfx.javafxplugin' version '0.0.7'
}
// Repositories
repositories {
jcenter()
mavenCentral()
}
// Dependencies
dependencies {
implementation 'com.google.code.gson:gson:2.8.5'
implementation 'org.apache.logging.log4j:log4j-core:2.12.0'
}
// JavaFX
javafx {
version = '12'
modules = ['javafx.controls']
}
mainClassName = 'net.protolauncher.backtest2.ProtoLauncher'
I am using Eclipse to run it, but this issue also occurs when just running the run task. I tried changing the Working Directory in the "Gradle Project" run configuration, but it didn't work at all (it just loaded forever).
To give an example, here's the directory of my source code: DirectoryX. Now, I made a folder in here called "run", like so: DirectoryX/run. When I run the program, I want my logs to go into DirectoryX/run/logs and similar files to go into the run directory. However, when running with Gradle my log files are being created in DirectoryX/logs.
This probably made no sense, but if it did, I really appreciate any help I can get.

After hours of searching online to no avail, I finally found a StackOverflow answer that solves the question. Turns out, JavaExec is a complicated thing, and what I was doing was specific to that, NOT JavaFx.

Related

Java 11 + Gradle with modules + JavaFX + Tablesaw yields ResolutionException

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.

Added Gradle to Java project "Exception ... java.lang.NoClassDefFoundError"

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

In Buildship: how can I substitute a Gradle project with a built jar?

I would like to be a able to get Eclipse to ignore one Gradle project, and instead use a pre-built version of it.
Background
I have a project "parser" written in Scala, and a dozen others written in Java. The weakest link in my tool-set is Scala IDE. I use this plugin to edit & compile Scala code, but unfortunately it breaks the Java (JDT) tooling quite badly in mixed-language projects*.
Specifically: Call-hierarchy is missing results, searches crash and so on. Also Scala IDE appears to have lost funding and the issues sound fairly fundamental, so I'm not holding my breath for these issues to be fixed.
With Maven (m2e) I had a workaround I was quite happy with:
Build as a .jar put into my local .m2 repository:
cd parser; mvn install
In Eclipse, close the "parser" project
"Like magic", m2e simply picked up the most recent 'installed' .jar and used it in place of the closed project.
An awesome answer would be how to get Gradle to do that!
However all I wish for is any solution that meets these...
Requirements
That I can open Project parser when necessary (which is seldom),
to edit and build changes via the Gradle command-line.
I will close it when done.
Other projects use the built .jar from my local .m2 repo.
(It's fine if they always do so.)
The change must not affect others who don't use Eclipse
(ideally) the change can be used by other Eclipse users
Approaches
A similar question had this good answer by #lance-java with a number of general suggestions. I think I can rule out these ideas:
composite build support / multiple repos. Other team members wouldn't think it makes sense to build this project separately, as it is quite closely integrated with the others.
dependency substitution rules - doesn't appear to meet requirement 3.
Something along the lines of lance-java's idea #4 sounds viable. Paraphrasing...
"use the eclipse plugin [in conjunction with] Buildship, e.g. using the whenMerged hook to tweak the generated .classpath [of all the Java projects]."
UPDATE: [18 Apr]: I had hit a brick wall in this approach. Buildship was not putting the built .jar onto the runtime classpath. (UPDATE 2: Now resolved - see my answer.)
Questions
The main question: How can I structure a solution to this, that will actually work & avoid any major pitfalls?
Note that the project itself has a few dependencies, specifically:
dependencies {
compile 'org.scala-lang:scala-library:2.12.4'
compileOnly 'com.google.code.findbugs:jsr305:1.3.9'
antlr 'org.antlr:antlr4:4.5.3'
}
So a sub-question may be: How to pull these in into the other projects without duplicating the definition? (If that doesn't work automatically.)
So the solution was a bit involved. After adding 'maven-publish' to create the library, I then implemented the following to force Eclipse to use the prebuilt library:
subprojects {
// Additional configuration to manipulate the Eclipse classpaths
configurations {
parserSubstitution
}
dependencies {
parserSubstitution module("com.example:parser:${project.version}")
}
apply plugin: 'eclipse'
eclipse {
classpath {
plusConfigurations += [ configurations.pseLangSubstitution ]
file {
whenMerged { cp ->
// Get Gradle to add the depedency upon
// parser-xxx.jar via 'plusConfigurations' above.
// Then this here if we have a dependency on Project(':parser')
// - If so, remove it (completing the project -> jar substitution).
// - If not, remove the .jar dependency: it wasn't needed.
def usesParser = entries.removeAll {
it instanceof ProjectDependency && it.path.startsWith('/parser')
}
def parserJar =
cp.entries.find { it instanceof Library && it.path.contains('parser-') }
if (usesParser) {
// This trick stops Buildship deleting it from the runtime classpath
parserJar ?. entryAttributes ?. remove("gradle_used_by_scope")
} else {
cp.entries.remove { parserJar }
}
}
}
}
So there are 2 parts to this:
Using 'plusConfigurations' felt a bit round-about. I ended up doing this because I could not see how to construct class Library classpath entries directly. However it could well be that this is required to implement the 'transient dependencies' correctly anyway. (See the end of the question.)
The trick to stop Buildship removing the .jar from the runtime classpath (thus deviating from a Gradle command-line launch) was provided to me by a Gradle developer in this discussion.
Usage
The solution works just as I hoped. Every time some code in this library is modified, I execute the following task of mine on the command line (which also does some other code & resource generation steps, in addition to building the parser jar):
./gradlew generateEclipse
Then in Eclipse I press keyboard shortcuts for "Gradle -> Refresh Gradle Projects", Build.
And harmony is restored. :-)
Navigating to the (prebuilt) source of parser works.
If I need to edit the source, I can open the parser project and edit it. Scala-IDE still does a good job for this.
When I'm done I execute the command, close the project and my Java tools are happy.
In parser project
You shoud use the maven-publish plugin with the publishToMavenLocal task
apply plugin: 'maven-publish'
group = 'your.company'
version = '1.0.0'
publishing {
publications {
mavenJava(MavenPublication) {
from components.java
pom.withXml {
def root = asNode()
root.appendNode('name', 'Your parser project name')
root.appendNode('description', 'Your parser project description')
}
}
}
}
Everytime you make a modification, just change the version number if necessary and go with gradle publishToMavenLocal
In other java project using parser
Just use parser as a regular dependency :
repositories {
mavenLocal()
...
}
compile 'your.company:parser:1.0.0'
If my understanding of your situation is good, it should do the trick.

How to create a debian distro for a java project using gradle

I see someone has asked this question before. I would have loved to have seen the answer, but it was removed. At the risk of getting down-voted like that post..., I really need help with this, as I've spent a few days on it already and I'm thoroughly at a loss...
I have a java project that's fairly mature. We're preparing to go from alpha phase into a beta release. As a part of that, we want to release installable packages with a proper app with an icon, etc. Creating a (dmg) package for distribution on Mac was extremely easy using the macAppBundle gradle plugin and it works beautifully. I'm now attempting to address distribution on Linux. Ideally, the setupbuilder plugin would be the way to go, but there's a bug that's preventing me from creating a .deb or .rpm package. I submitted the bug to the developer and am currently trying to work around the issue by following this blog post.
I am running an Ubuntu 16.04.3 vm in VirtualBox on my Mac and I can successfully create a working executable by running gradle debianPrepareappname. But when I try to run gradle debian to create the .deb file, the build always fails (currently with this error:).
Process 'command 'debuild'' finished with non-zero exit value 255
When I run debuild manually, I see the following:
debuild: fatal error at line 679:
found debian/changelog in directory
/home/username/appname/build/debian/appname
but there's no debian/rules there! Are you in the source code tree?
No rules file is getting created by gradle. I know that the rules file is basically a makefile... and I'm not very familiar with makefiles in general, let alone creating .deb distros. I know makefiles do compilations and copy files to places in the system, but I don't know what needs to be done to create a .deb file or where things need to go. I mean, the necessary components are there and they work:
appname/build/debian/appname/debian/{bin,lib}
The bin has the working executable and the lib has all the necessary jar files. I just don't know what I need to do in the gradle build script to create the .deb file. Here's what I've got in the gradle build file (I've omitted the macAppBundle and setupbuilder stuff that's just vestigial in there right now, just to keep it simple):
apply plugin: 'java'
apply plugin: 'eclipse'
apply plugin: 'application'
defaultTasks "clean", "fatJar", "eclipse"
version = getVersionName()
sourceCompatibility = 1.7
targetCompatibility = 1.7
repositories {
mavenCentral()
}
dependencies {
compile 'com.miglayout:miglayout-swing:5.0'
compile 'com.googlecode.plist:dd-plist:1.3'
compile 'org.freehep:freehep-graphicsio:2.4'
compile 'org.freehep:freehep-graphicsio-pdf:2.4'
compile 'org.freehep:freehep-graphicsio-ps:2.4'
compile 'org.freehep:freehep-graphicsio-svg:2.4'
compile 'org.freehep:freehep-graphics2d:2.4'
compile 'org.swinglabs.swingx:swingx-autocomplete:1.6.5-1'
}
sourceSets {
main {
java {
srcDir 'src/main/java/'
}
}
}
task fatJar(type: Jar) {
manifest {
attributes 'Main-Class':'com.placeholder.appname'
}
baseName = project.name + '-all'
from {configurations.compile.collect {it.isDirectory() ? it : zipTree(it)}}
with jar
}
def getVersionName() {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
}
String applicationVersionFull = getVersionName()
task debianClean(type: Delete) {
delete 'build/debian'
}
tasks.addRule("Pattern: debianPrepare<distribution>") { String taskName ->
if (taskName.startsWith("debianPrepare")) {
task(taskName, dependsOn: [installDist, debianClean]){
String debianDistribution = (taskName - "debianPrepare").toLowerCase()
String debianApplicationVersionFull = getVersionName()
doLast {
copy {
from rootProject.files("build/install/appname")
into rootProject.file("build/debian/appname")
}
copy {
from rootProject.files("gradle/debian/debian")
into rootProject.file("build/debian/appname/debian")
}
}
}
}
}
task debian { // depends on debianPrepare*
doLast {
exec {
workingDir rootProject.file("build/debian/appname")
commandLine "debuild -i -us -uc -b".split()
}
}
}
Everything I've read says this is supposed to be really easy with gradle. The macAppBundle was definitely very easy - it was like 5 lines of code. I barely had to read anything to figure it out and it creates a dmg that has an executable with an icon and everything. I just copied & edited the example in the macAppBundle readme. setupbuilder looked similarly easy, if not for the bug I encountered. Is there a similar example out there for building .deb packages for java projects that doesn't use setupbuilder? I've tried a couple other plugins with no success. I've been googling and I can't find anything straightforward other than the blog post I mentioned. I eventually would like to apply an icon to the executable and other niceties, but first thing is to just get it to build. So why does the rules file not get created? That seems like a good place to start.
I think what you're missing is a "debian" directory with all the related files already present. If you look at syncany's repo https://github.com/syncany/syncany/tree/74c737d871d21dff5283edaac8c187a42c020b20/gradle/debian/debian on github from the blog post you mentioned, you'll see he has 8 files.
At the end of the day, debuild is just bundling a set of files up into an installer. They all have to be there to begin with. His scripts don't create any of these files, just modify some such as the changelog.

How can you override an existing Gradle plugin from a local repository?

I am building an application using Gradle, JDK 8, Java FX, and Test FX. I need to be on JDK 8 for our application to work on all platforms with our tech stack. My problem is that I am unable to get code coverage into our build. I found this link...
https://github.com/jacoco/jacoco/issues/74
...and using the Jacoco preview build at the top, I was able to replace my intellij JARs and successfully run my tests and get the coverage. However, I am having trouble putting this into my build.gradle. From what I can tell, I need to add a local repository in my build script...
...
apply plugin: "jacoco"
...
buildscript {
repositories {
// Local Repo
// MVN Repo(s)
}
dependencies {
// Classpaths
}
}
jacoco {
toolVersion = "0.6.4.201311160552" // I need this specific version, which isn't on a MVN repo
}
...I tried to add my local repo several ways including...
flatDir(dirs: "lib")
flatDir dirs: "${projectDir}/lib"
maven { url uri("lib") }
one or two other ways I forget
...my lib folder contains the exact contents, unchanged, from the preview build zip's lib folder in the link above. It doesn't seem like gradle is having a problem locating the local repo, but it is having trouble finding the JAR. I assume there is something wrong with the way I am naming it or the way that it is "packaged". I have tried modifying the JAR names but I keep getting the error...
What went wrong:
A problem occurred configuring root project 'myProject'.
Could not resolve all dependencies for configuration ':classpath'.
Could not find :org.jacoco.agent:.
Required by:
:myProject:unspecified
...any ideas why my JAR is not being found? Thanks!
"Answering" my own question, despite the fact that I still haven't quite figured it out. Anyways, here are two links I found that seem to solve my problem...
http://forums.gradle.org/gradle/topics/jacocotestreport_is_skipping
...following some of these instructions allow my tests to run, but I am still not able to run "gradle jacocoTestReport" without it failing.
UPDATE
OKAY! I figured it out, the link above did help me figure it out. My problem was with the asm-all JAR, since there were several, I did not know which one to use. In order to get jacoco working with Java 1.8, you do not need to specify the toolVersion property. All you need to do is add the following to your dependencies block (not the buildscript block, the code block)...
jacocoAgent files(
"$projectDir/lib/org.jacoco.agent-0.6.4.201311160552.jar")
jacocoAnt files(
"$projectDir/lib/org.jacoco.ant-0.6.4.201311160552.jar",
"$projectDir/lib/org.jacoco.core-0.6.4.201311160552.jar",
"$projectDir/lib/org.jacoco.report-0.6.4.201311160552.jar",
"$projectDir/lib/asm-all-5.0_BETA.jar")
...where the asm-all-5.0_BETA.jar is taken from the org.ow2.asm group found at...
http://mvnrepository.com/artifact/org.ow2.asm/asm-all/5.0_BETA
...hope this helps!
for reference, latest jacoco libs are changed so i'm sharing the following snippet:
dependencies{
jacocoAgent files("$rootProject.projectDir/lib/org.jacoco.agent-0.8.3.201904130250.jar")
jacocoAnt files("$rootProject.projectDir/lib/org.jacoco.ant-0.8.3.201904130250.jar",
"$rootProject.projectDir/lib/org.jacoco.core-0.8.3.201904130250.jar",
"$rootProject.projectDir/lib/org.jacoco.report-0.8.3.201904130250.jar",
"$rootProject.projectDir/lib/asm-7.0.jar",
"$rootProject.projectDir/lib/asm-tree-7.0.jar",
"$rootProject.projectDir/lib/asm-commons-7.0.jar"
)
}

Categories

Resources