Java 11 с JavaFX построения JAR с Gradle - PullRequest
0 голосов
/ 20 сентября 2019

Мы разрабатываем приложение JavaFX 11 для Adopt JDK 11 и IntelliJ с использованием Gradle.В конце нам нужен файл EXE для Windows (приложение предназначено только для Windows).В первой попытке мы также использовали launch4J в Gradle, из-за следующей проблемы нам также было бы хорошо использовать BAT-файл, который мы могли бы вместо этого перенести в исполняемый файл.

Итак, наша главная цель и вопрос в том, как создать исполняемый файл JAR.

Мы сделали несколько подходов с разными результатами, и мы полностью потеряны.

FAT JAR

Мы сделали отдельное приложение для тестирования:

package de.test;

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.TextArea;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        VBox vBox = new VBox();

        Button button = new Button("Klick");

        TextField textfield = new TextField();
        TextArea area = new TextArea();
        area.setMinHeight(300);

        button.setOnAction(event -> area.setText(area.getText() + " --  Klick Version 1.0.8"));
        vBox.getChildren().addAll(button, textfield,area);

        primaryStage.setScene(new Scene(vBox));
        primaryStage.setTitle("Test");
        primaryStage.show();
    }
}

FAT JAR

И нашей первой попыткой был FAT-JAR с или без файла modules-info.java с использованием плагина javafx из gradle.Вот наш файл gradle

plugins {
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.8'
}

group 'de.test'
version '1.0.8'

sourceCompatibility = 11

javafx {
    modules = ['javafx.controls']
}

mainClassName = 'de.test.Main'

jar {
    manifest {
        attributes 'Main-Class': mainClassName
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

repositories {
    mavenLocal()
    mavenCentral()
}

После запуска gradle clean jar мы попытались выполнить jar с помощью следующей команды java -jar ApplicationTest-1.0.8.jar результат:

Ошибка: не удалось найти или загрузить основной класс de.test.Main. Причина: java.lang.NoClassDefFoundError: javafx / application / Application


FAT JAR с зависимостями и module-info.java

Затем мы попытались добавить зависимости для JavaFX следующим образом в файл gradle

compile group: 'org.jboss.resteasy', name: 'resteasy-client', version: '4.3.0.Final'
compile "org.openjfx:javafx-graphics:11.0.2:win"
compile "org.openjfx:javafx-base:11.0.2:win"
compile "org.openjfx:javafx-controls:11.0.2:win"
compile "org.openjfx:javafx-fxml:11.0.2:win"
compile "org.openjfx:javafx-graphics:11.0.2:win"

И это информация о модуле.java-дескриптор, который мы добавили в пакет de.test:

module ApplicationTest.main {

    requires javafx.controls;

    exports de.test;

}

При вызове gradle installDist мы использовали эту команду

cd build \ install \ ApplicationTest\ lib java --адд-модули "javafx.controls" --модуль-путь.-jar ApplicationTest-1.0.8.jar

По крайней мере, приложение запустилось в этот момент, но макет был разрушен со следующими информационными сообщениями:

Сен.20, 2019 8:32:55 ВОРМ.com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged INFO: Не удалось загрузить таблицу стилей: com / sun / javafx / scene / control / skin / modena / modena.css 20 сентября 2019 г. 8:32:55 VORM.com.sun.javafx.css.StyleManager loadStylesheetUnPrivileged INFO: не удалось загрузить таблицу стилей: com / sun / javafx / scene / control / skin / modena / modena.css

No layout when using the JAR file

Между прочим: IntelliJ всегда работает при использовании команды gradle clean run

Using IntelliJ the layout is fine


JLink с module-info.java

Следующим подходом было использование jlink и следующих модулей-info.java

module ApplicationTest.main {
    requires javafx.controls;
    requires javafx.graphics;

    exports de.test;

}

Нам нужно было немного расширить файл gradle, так какследующие

plugins {
    id 'java'
    id 'application'
    id 'org.openjfx.javafxplugin' version '0.0.8'
    id 'org.beryx.jlink' version '2.10.4'
}

group 'de.test'
version '1.0.8'

sourceCompatibility = 11

mainClassName = 'de.test.Main'

javafx {
    version = 11
    modules = [ 'javafx.controls']
}

jlink {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
}


repositories {
    mavenLocal()
    mavenCentral()
}

При использовании gradle jlink и выполнении файла cd build \ image \ bin \ ApplicationTest.bat приложение запускается WITH макет.

В этот момент мы где-то счастливы.Теперь нам нужно было добавить некоторые зависимости для нашего приложения.

Использование следующих зависимостей прекрасно работало

dependencies {
    compile "commons-io:commons-io:2.6"
    compile "org.apache.logging.log4j:log4j-api:2.11.2"
    compile "dom4j:dom4j:1.6.1"
    compile "commons-lang:commons-lang:2.6"
    compile "axis:axis:1.4"
    compile "jaxen:jaxen:1.1.6"
    compile "net.java.dev.jna:platform:3.5.2"
    compile "org.apache.poi:poi:4.1.0"
    compile "org.apache.poi:poi-scratchpad:4.1.0"
    compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.4"
}

Но как только я добавляю другие зависимости, я получаю сообщения об ошибках другого типа.Я пробовал каждую зависимость в одиночку, чтобы изолировать сообщение об ошибке.

org.jboss.resteasy - resteasy-client - 3.7.0.Final

Cannot derive uses clause from service loader invocation in: javax/ws/rs/client/FactoryFinder.find().
Cannot derive uses clause from service loader invocation in: javax/ws/rs/ext/FactoryFinder.find().
Cannot derive uses clause from service loader invocation in: javax/ws/rs/sse/FactoryFinder.find().
Cannot derive uses clause from service loader invocation in: javax/xml/bind/ServiceLoaderUtil.firstByServiceLoader().

ApplicationTest\build\jlinkbase\tmpjars\de.test.merged.module\module-info.java:154: error: the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
    provides javax.ws.rs.ext.Providers with org.jboss.resteasy.plugins.interceptors.CacheControlFeature,
                                                                                   ^
ApplicationTest\build\jlinkbase\tmpjars\de.test.merged.module\module-info.java:155: error: the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
                org.jboss.resteasy.plugins.interceptors.encoding.ClientContentEncodingAnnotationFeature,
                                                                ^
ApplicationTest\build\jlinkbase\tmpjars\de.test.merged.module\module-info.java:156: error: the service implementation type must be a subtype of the service interface type, or have a public static no-args method named "provider" returning the service implementation
                org.jboss.resteasy.plugins.interceptors.encoding.MessageSanitizerContainerResponseFilter,
                                                                ^
...     
                                            ^
25 errors

Зависимость: org.apache.logging.log4j: log4j-core: 2.11.2

package org.apache.logging.log4j.spi is not visible
(package org.apache.logging.log4j.spi is declared in module org.apache.logging.log4j, but module de.test.merged.module does not read it)
package org.apache.logging.log4j.message is not visible
(package org.apache.logging.log4j.message is declared in module org.apache.logging.log4j, but module de.test.merged.module does not read it)

Зависимость: org.jboss.resteasy: resteasy-multipart-provider: 3.7.0.Final

module not found: java.activation

Зависимость: org.jboss.resteasy: resteasy-jackson2-provider: 3.7.0.Final

package javax.ws.rs.ext does not exist
package javax.ws.rs.ext does not exist
package javax.ws.rs.ext does not exist

Заключение

Мы сейчас никуда не денемся.Возможно, это как-то связано с modules-info.java, но инструкции и документация по этой теме довольно сложны и часто очень подробны.Мы просто хотели приложение, которое можно было запускать за пределами IntelliJ.

Мы открыты для всех решений, пока они работают.Нам также не нужно современное решение, но что-то, из чего мы можем каким-то образом получить EXE-файл (также с помощью внешнего инструмента).Наше приложение не обязательно должно быть модульным, но мы не получаем и не решаем его.

РЕДАКТИРОВАТЬ 1) FAT Jar со вторым основным классом

Как и в этой статье мы добавили второй основной Java-класс без расширения Application и поместили его в файл Gradle

package de.test;

public class Main {
    public static void main(String[] args) {
        MainFX.main(args);
    }

}

, а другой класс выглядит так:

package de.test;

import ...;

public class MainFX extends Application {

    public static void main(String[] args) {
        launch();
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
    ...
    }
}

ифайл Gradle выглядит следующим образом:

plugins {
    id 'java'
    id 'application'
}

group 'de.test'
version '1.0.8'

sourceCompatibility = 11

mainClassName = 'de.test.MainFX'

jar {
    manifest {
        attributes 'Main-Class': mainClassName
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

repositories {
    mavenLocal()
    mavenCentral()
}

dependencies {
    compile "commons-io:commons-io:2.6"
    compile "org.apache.logging.log4j:log4j-api:2.11.2"
    compile "dom4j:dom4j:1.6.1"
    compile "commons-lang:commons-lang:2.6"
    compile "axis:axis:1.4"
    compile "jaxen:jaxen:1.1.6"
    compile "net.java.dev.jna:platform:3.5.2"
    compile "org.apache.poi:poi:4.1.0"
    compile "org.apache.poi:poi-scratchpad:4.1.0"
    compile "com.fasterxml.jackson.datatype:jackson-datatype-jsr310:2.9.4"

    compile "org.jboss.resteasy:resteasy-multipart-provider:3.7.0.Final"
    compile "org.jboss.resteasy:resteasy-jackson2-provider:3.7.0.Final"
    compile "org.apache.logging.log4j:log4j-core:2.11.2"
    compile "org.jboss.resteasy:resteasy-client:3.7.0.Final"
    compile "org.openjfx:javafx-graphics:11.0.2:win"
    compile "org.openjfx:javafx-base:11.0.2:win"
    compile "org.openjfx:javafx-controls:11.0.2:win"
    compile "org.openjfx:javafx-fxml:11.0.2:win"
    compile "org.openjfx:javafx-graphics:11.0.2:win"
}

Результат действительно странный java -jar ApplicationTest.jar возвращает

Ошибка: не удалось найти или загрузитьmain class de.test.MainFX Причина: java.lang.ClassNotFoundException: de.test.MainFX

Мы дважды проверили файл jar и файлы классов находятся в правильном месте (de \ test \Main.class и de \ test \ MainFX.class)

РЕДАКТИРОВАТЬ 2) FAT Jar со вторым основным классом

На основании этой статьи мы снова добавили плагин javafx,удалил зависимости javafx и определил Launcher-Main внутри манифеста и обычного FXMLПочта как mainClassName:

...
mainClassName = 'de.test.MainFX'

javafx {
    version = 11
    modules = [ 'javafx.controls', 'javafx.fxml', 'javafx.web']
}

jar {
    manifest {
        attributes 'Main-Class': 'de.test.MainLauncher'
    }
    from {
        configurations.compile.collect { it.isDirectory() ? it : zipTree(it) }
    }
}

Результат

> Exception in thread "main" java.lang.NoClassDefFoundError:
> javafx/application/Application
>         at java.base/java.lang.ClassLoader.defineClass1(Native Method)
>         at java.base/java.lang.ClassLoader.defineClass(ClassLoader.java:1016)
>         at java.base/java.security.SecureClassLoader.defineClass(SecureClassLoader.java:174)
>         at java.base/jdk.internal.loader.BuiltinClassLoader.defineClass(BuiltinClassLoader.java:802)
>         at java.base/jdk.internal.loader.BuiltinClassLoader.findClassOnClassPathOrNull(BuiltinClassLoader.java:700)
>         at java.base/jdk.internal.loader.BuiltinClassLoader.loadClassOrNull(BuiltinClassLoader.java:623)
>         at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
>         at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
>         at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
>         at de.test.MainLauncher.main(MainLauncher.java:7) Caused by: java.lang.ClassNotFoundException: javafx.application.Application
>         at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
>         at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
>         at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
>         ... 10 more

Итак, мы добавили зависимости для javafx снова со следующим результатом:

Graphics Device initialization failed for :  d3d, sw
Error initializing QuantumRenderer: no suitable pipeline found
java.lang.RuntimeException: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at com.sun.javafx.tk.quantum.QuantumRenderer.getInstance(QuantumRenderer.java:280)
        at com.sun.javafx.tk.quantum.QuantumToolkit.init(QuantumToolkit.java:222)
        at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:260)
        at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
        at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
        at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
        at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
        at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: Error initializing QuantumRenderer: no suitable pipeline found
        at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.init(QuantumRenderer.java:94)
        at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:124)
        ... 1 more
Exception in thread "main" java.lang.RuntimeException: No toolkit found
        at com.sun.javafx.tk.Toolkit.getToolkit(Toolkit.java:272)
        at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:267)
        at com.sun.javafx.application.PlatformImpl.startup(PlatformImpl.java:158)
        at com.sun.javafx.application.LauncherImpl.startToolkit(LauncherImpl.java:658)
        at com.sun.javafx.application.LauncherImpl.launchApplication1(LauncherImpl.java:678)
        at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$2(LauncherImpl.java:195)
        at java.base/java.lang.Thread.run(Thread.java:834)

1 Ответ

0 голосов
/ 20 сентября 2019

Согласно моему (болезненному) опыту до сих пор, единственный способ получить большой проект JavaFX 11+ с множеством работающих внешних зависимостей, которыми вы не можете управлять, - это сохранить все в classpath (также зависимости JavaFX)и используйте эту обертку для основного класса, как вы делали в последнем примере.(Ваш текущий файл Gradle неверен. См. Мой комментарий выше.) Попытка возиться с модульной системой просто приведет вас в никуда и просто потратит ваше время.

В настоящее время я делаю следующее, чтобы создать exe(или приложение на Mac).

  1. Используйте общую настройку, как описано выше.
  2. Через Maven / Gradle собирать все зависимости в папке lib.
  3. С помощью jlink создайте специализированное время выполнения для вашей программы.
  4. Используйте версию EA jpackage для создания exe / app.

Как только вы выяснили все опции, которые вам нужны, этоПодход работает как очарование.

Создать собственное время выполнения с помощью jlink просто.

JAVA_HOME=<the java version you want to use>

$JAVA_HOME/bin/jlink --no-header-files --no-man-pages --compress=2 --strip-debug \
--add-modules <a list of the required or just all modules> \
--output java-runtime

Вы можете использовать инструмент jdeps, чтобы найти нужные вам модули.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...