1

I am trying to both run and jlink a project that uses Gradle, Spring, and JavaFX. In my first attempt, I had been able to configure some simple files for my project that allow me to run it, but not jlink it. For my more recent attempt, I used this project as a template to tinker with. The result allows me to jlink but not run. I suspect what I have to do to get it to work is to transfer settings from the jlink task to the run task. I am guessing that the way to do this is to use the application plugin, though I don't know what settings need to be set, or what the syntax is for writing them.

build.gradle

plugins {
    id 'application'
    id 'org.springframework.boot' version '2.2.4.RELEASE'
    id 'io.spring.dependency-management' version '1.0.10.RELEASE'
    id 'org.openjfx.javafxplugin' version '0.0.8'
    id 'org.beryx.jlink' version '2.22.0'
}
group 'group'
sourceCompatibility = '14'
targetCompatibility = '14'
mainClassName = 'mod/app.Main'
jar {
    enabled = true;
    manifest.attributes('Main-Class': 'mod/app.Main')
}

repositories {
    jcenter()
    mavenCentral()
    mavenLocal()
}
javafx {
    version = '14.0.1'
    modules = [ 'javafx.controls']
}
jlink {
    addOptions('--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages')
    launcher {
        name = 'start-app'
        customImage {
            appModules = ['group.merged.module']
        }
        jvmArgs = [
                '--add-reads', 'group.merged.module=mod',
                '-cp', '../app/*'
        ]
    }
    mergedModule {
        additive = true
        uses 'ch.qos.logback.classic.spi.Configurator'
    }
    jpackage {
        imageName = 'start-app'
        skipInstaller = true
        installerName = 'start-app'
        installerType = 'pkg'
    }
    forceMerge('log4j-api')
}

dependencies {
    implementation 'mysql:mysql-connector-java:8.0.16'
    implementation 'com.h2database:h2:1.4.200'
    implementation 'org.jasypt:jasypt:1.9.3'
    implementation 'com.github.javafaker:javafaker:1.0.2'
    implementation ('org.controlsfx:controlsfx:11.0.2'){exclude group: 'org.openjfx'}
    implementation 'javax.servlet:javax.servlet-api:4.0.1'
    implementation 'org.springframework.boot:spring-boot-starter'
}

module-info.java

module mod {
    requires javafx.controls;
    requires org.controlsfx.controls;
    requires spring.boot.autoconfigure;
    requires spring.context;
    requires spring.boot;
    requires spring.beans;
    requires jasypt;
    requires javafaker;
    requires java.sql;
    requires java.naming;

    opens app to spring.core;

    exports app;
}

enter image description here

Both build.gradle and module-info.java have been modified from their original form in the template project and inserted into my project. Both spring.factories, and application.properties remain unmodified from their original form and are inserted into my project. No other files have been moved.

> Task :run FAILED
Error occurred during initialization of boot layer
java.lang.module.ResolutionException: Modules org.yaml.snakeyaml and snakeyaml export package org.yaml.snakeyaml.error to module org.apache.logging.slf4j
4 actionable tasks: 3 executed, 1 up-to-date

* Exception is:
org.gradle.api.tasks.TaskExecutionException: Execution failed for task ':run'.
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.lambda$executeIfValid$1(ExecuteActionsTaskExecuter.java:205)
    at org.gradle.internal.Try$Failure.ifSuccessfulOrElse(Try.java:263)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:203)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.execute(ExecuteActionsTaskExecuter.java:184)
    at org.gradle.api.internal.tasks.execution.CleanupStaleOutputsExecuter.execute(CleanupStaleOutputsExecuter.java:114)
    at org.gradle.api.internal.tasks.execution.FinalizePropertiesTaskExecuter.execute(FinalizePropertiesTaskExecuter.java:46)
    at org.gradle.api.internal.tasks.execution.ResolveTaskExecutionModeExecuter.execute(ResolveTaskExecutionModeExecuter.java:62)
    at org.gradle.api.internal.tasks.execution.SkipTaskWithNoActionsExecuter.execute(SkipTaskWithNoActionsExecuter.java:57)
    at org.gradle.api.internal.tasks.execution.SkipOnlyIfTaskExecuter.execute(SkipOnlyIfTaskExecuter.java:56)
    at org.gradle.api.internal.tasks.execution.CatchExceptionTaskExecuter.execute(CatchExceptionTaskExecuter.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.executeTask(EventFiringTaskExecuter.java:77)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:55)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter$1.call(EventFiringTaskExecuter.java:52)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:416)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$CallableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:406)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.call(DefaultBuildOperationExecutor.java:102)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.call(DelegatingBuildOperationExecutor.java:36)
    at org.gradle.api.internal.tasks.execution.EventFiringTaskExecuter.execute(EventFiringTaskExecuter.java:52)
    at org.gradle.execution.plan.LocalTaskNodeExecutor.execute(LocalTaskNodeExecutor.java:41)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:372)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$InvokeNodeExecutorsAction.execute(DefaultTaskExecutionGraph.java:359)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:352)
    at org.gradle.execution.taskgraph.DefaultTaskExecutionGraph$BuildOperationAwareExecutionAction.execute(DefaultTaskExecutionGraph.java:338)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.lambda$run$0(DefaultPlanExecutor.java:127)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.execute(DefaultPlanExecutor.java:191)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.executeNextNode(DefaultPlanExecutor.java:182)
    at org.gradle.execution.plan.DefaultPlanExecutor$ExecutorWorker.run(DefaultPlanExecutor.java:124)
    at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
    at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
    at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
Caused by: org.gradle.process.internal.ExecException: Process 'command 'C:\Program Files\Java\jdk 14.0.2.12 Adopt\bin\java.exe'' finished with non-zero exit value 1
    at org.gradle.process.internal.DefaultExecHandle$ExecResultImpl.assertNormalExitValue(DefaultExecHandle.java:417)
    at org.gradle.process.internal.DefaultJavaExecAction.execute(DefaultJavaExecAction.java:40)
    at org.gradle.api.tasks.JavaExec.exec(JavaExec.java:132)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at org.gradle.internal.reflect.JavaMethod.invoke(JavaMethod.java:104)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.doExecute(StandardTaskAction.java:49)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:42)
    at org.gradle.api.internal.project.taskfactory.StandardTaskAction.execute(StandardTaskAction.java:28)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:727)
    at org.gradle.api.internal.AbstractTask$TaskActionWrapper.execute(AbstractTask.java:694)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$3.run(ExecuteActionsTaskExecuter.java:568)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:402)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$RunnableBuildOperationWorker.execute(DefaultBuildOperationExecutor.java:394)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor$1.execute(DefaultBuildOperationExecutor.java:165)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:250)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.execute(DefaultBuildOperationExecutor.java:158)
    at org.gradle.internal.operations.DefaultBuildOperationExecutor.run(DefaultBuildOperationExecutor.java:92)
    at org.gradle.internal.operations.DelegatingBuildOperationExecutor.run(DelegatingBuildOperationExecutor.java:31)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeAction(ExecuteActionsTaskExecuter.java:553)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeActions(ExecuteActionsTaskExecuter.java:536)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.access$300(ExecuteActionsTaskExecuter.java:109)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.executeWithPreviousOutputFiles(ExecuteActionsTaskExecuter.java:276)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter$TaskExecution.execute(ExecuteActionsTaskExecuter.java:265)
    at org.gradle.internal.execution.steps.ExecuteStep.lambda$execute$1(ExecuteStep.java:33)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:33)
    at org.gradle.internal.execution.steps.ExecuteStep.execute(ExecuteStep.java:26)
    at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:67)
    at org.gradle.internal.execution.steps.CleanupOutputsStep.execute(CleanupOutputsStep.java:36)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:49)
    at org.gradle.internal.execution.steps.ResolveInputChangesStep.execute(ResolveInputChangesStep.java:34)
    at org.gradle.internal.execution.steps.CancelExecutionStep.execute(CancelExecutionStep.java:43)
    at org.gradle.internal.execution.steps.TimeoutStep.executeWithoutTimeout(TimeoutStep.java:73)
    at org.gradle.internal.execution.steps.TimeoutStep.execute(TimeoutStep.java:54)
    at org.gradle.internal.execution.steps.CatchExceptionStep.execute(CatchExceptionStep.java:34)
    at org.gradle.internal.execution.steps.CreateOutputsStep.execute(CreateOutputsStep.java:44)
    at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:54)
    at org.gradle.internal.execution.steps.SnapshotOutputsStep.execute(SnapshotOutputsStep.java:38)
    at org.gradle.internal.execution.steps.BroadcastChangingOutputsStep.execute(BroadcastChangingOutputsStep.java:49)
    at org.gradle.internal.execution.steps.CacheStep.executeWithoutCache(CacheStep.java:159)
    at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:72)
    at org.gradle.internal.execution.steps.CacheStep.execute(CacheStep.java:43)
    at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:44)
    at org.gradle.internal.execution.steps.StoreExecutionStateStep.execute(StoreExecutionStateStep.java:33)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:38)
    at org.gradle.internal.execution.steps.RecordOutputsStep.execute(RecordOutputsStep.java:24)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.executeBecause(SkipUpToDateStep.java:92)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.lambda$execute$0(SkipUpToDateStep.java:85)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:55)
    at org.gradle.internal.execution.steps.SkipUpToDateStep.execute(SkipUpToDateStep.java:39)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:76)
    at org.gradle.internal.execution.steps.ResolveChangesStep.execute(ResolveChangesStep.java:37)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:36)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsFinishedStep.execute(MarkSnapshottingInputsFinishedStep.java:26)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:94)
    at org.gradle.internal.execution.steps.ResolveCachingStateStep.execute(ResolveCachingStateStep.java:49)
    at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:79)
    at org.gradle.internal.execution.steps.CaptureStateBeforeExecutionStep.execute(CaptureStateBeforeExecutionStep.java:53)
    at org.gradle.internal.execution.steps.ValidateStep.execute(ValidateStep.java:74)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.lambda$execute$2(SkipEmptyWorkStep.java:78)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:78)
    at org.gradle.internal.execution.steps.SkipEmptyWorkStep.execute(SkipEmptyWorkStep.java:34)
    at org.gradle.internal.execution.steps.legacy.MarkSnapshottingInputsStartedStep.execute(MarkSnapshottingInputsStartedStep.java:39)
    at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:40)
    at org.gradle.internal.execution.steps.LoadExecutionStateStep.execute(LoadExecutionStateStep.java:28)
    at org.gradle.internal.execution.impl.DefaultWorkExecutor.execute(DefaultWorkExecutor.java:33)
    at org.gradle.api.internal.tasks.execution.ExecuteActionsTaskExecuter.executeIfValid(ExecuteActionsTaskExecuter.java:192)
    ... 30 more
Jack J
  • 1,514
  • 3
  • 20
  • 28
  • Isn't there more to the stack trace? Best guess is that when it runs it's still using the standard JRE, instead of the one you created with jlink. – James_D Sep 28 '20 at 15:51
  • I added more of the stack trace. I wasn't sure how much more of it would be helpful. – Jack J Sep 28 '20 at 15:56
  • Well, somewhere it should show you the stack trace caused by the actual application (not the one thrown by gradle). – James_D Sep 28 '20 at 15:58
  • Even setting a breakpoint at public static void main(), this breakpoint is never reached. – Jack J Sep 28 '20 at 16:02
  • „*…I added more of the stack trace…*“ – @JackJ — Your most recent edit of the error message tells you everything you need to know: „*`…java.lang.module.ResolutionException: Modules org.yaml.snakeyaml and snakeyaml export package org.yaml.snakeyaml.error to module org.apache.logging.slf4j…`*“ — I solved that same error around last year this time in another JavaFX project. Although that one wasn't a JLink image, the fix did involve *`gradle run`*. If you push an [*MRE*](https://stackoverflow.com/help/minimal-reproducible-example) up to a repo, I could take a look at it at some point. TIA. – deduper Oct 03 '20 at 19:44
  • I figured out that there is a split package problem with Javafaker and its yaml dependency. I would post that as an answer, except for a few factors. 1) I don't feel this question is very well structured to find that out, and I think this question needs to be rewritten. 2) I don't know enough about split packages to give a good answer. I did solve this, but I did it by removing Javafaker and replacing it with large resource files. A better solution would have probably been a few lines of Gradle code. – Jack J Oct 03 '20 at 20:26
  • I would also like to notify the Javafaker developer because I very much like what it tries to do, but because I don't know enough about split packages, I don't know that I would be of much help. – Jack J Oct 03 '20 at 20:28
  • The reason I believe it is a split library issue is that I made a program where I listed every dependency of my dependencies. I found two shared dependencies. One of these was over JUnit and the other was over yaml. The first was a conflict between Javafaker and Jasypt. The other was a conflict between Javafaker and Spring. – Jack J Oct 03 '20 at 20:35
  • 1
    „*I figured out that there is a split package problem*“ Bingo! Exactly what I figured out for the other project. „*1) I don't feel this question is very well structured*“ — Nah! It's structured perfectly fine IMO. — „*2) I don't know enough about split packages*“ — There's not a lot to know to be honest. [*This is what it is*](https://nirmalsasidharan.wordpress.com/2010/09/02/split-packages-an-osgi-nightmare/). [*This is why it's a problem*](http://mail.openjdk.java.net/pipermail/jigsaw-dev/2016-November/009964.html). Just say in your answer what I quoted from the 1st sentence of your comment. – deduper Oct 03 '20 at 21:54

1 Answers1

1

I found that Javafaker has a split package issue regarding yaml. Removing Javafaker from my program solved this issue, although it would have been better to solve this with some code in Gradle.

Jack J
  • 1,514
  • 3
  • 20
  • 28
  • „*…Removing Javafaker from my program solved this issue…*“ — A perfectly appropriate solution. [*The head Java Language Architect*](https://openjdk.java.net/projects/jigsaw/spec/sotms/#bridges-to-the-class-path) approves of that approach: „*…If two or more JAR files on the class path contain types in the same package then at most one of them can be used as an automatic module…In such situations it often turns out that only one of the JAR files is actually needed* … ***the others can be discarded***“. I wish all my split package problems could be solved that easily. Count your blessings :) – deduper Oct 04 '20 at 02:40