Не удается запустить приложение JavaFX вне IDE при использовании javafxplugin и hibernate - PullRequest
0 голосов
/ 04 января 2019

У меня проблема с распространением моего приложения JavaFX с использованием gradle: installDist. Я свел проблему к базовому примеру.

У меня есть следующие настройки для моего приложения:

  • OpenJDK 11
  • OpenJFX 11
  • Gradle 5,0
  • Hibernate 5.4
  • h2database 1.4.197

build.gradle:

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

group 'de.testing'
version '1.0-SNAPSHOT'



repositories {
    mavenCentral()
}

dependencies {
    compile group: 'org.hibernate', name: 'hibernate-core', version: '5.4.0.Final'
    compile group: 'com.h2database', name: 'h2', version: '1.4.197'
}

mainClassName = 'de.testing.Main'

Код:

package de.testing;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;

public class Main {
    public static void main(String[] args) {
        EntityManagerFactory emf = Persistence.createEntityManagerFactory("thePersistenceUnit");
        EntityManager em = emf.createEntityManager();
    }
}

persistence.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://java.sun.com/xml/ns/persistence
 http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0">

    <persistence-unit name="thePersistenceUnit" transaction-type="RESOURCE_LOCAL">
        <provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
        <properties>
            <property name="connection.driver_class" value="org.h2.Driver"/>
            <property name="hibernate.connection.url" value="jdbc:h2:~/testing"/>
            <property name="hibernate.dialect" value="org.hibernate.dialect.H2Dialect"/>
            <property name="hibernate.hbm2ddl.auto" value="update"/>
            <property name="hibernate.show_sql" value="true" />
        </properties>
    </persistence-unit>
</persistence>

Когда я запускаю это в своей IDE, все в порядке. Однако, когда я использую gradle: installDist для распространения приложения и попытки запуска сценария запуска, я получаю следующее исключение:

.\testing.bat
Jan. 04, 2019 6:53:22 NACHM. org.hibernate.jpa.internal.util.LogHelper logPersistenceUnitInformation
INFO: HHH000204: Processing PersistenceUnitInfo [
        name: thePersistenceUnit
        ...]
Jan. 04, 2019 6:53:22 NACHM. org.hibernate.Version logVersion
INFO: HHH000412: Hibernate Core {[WORKING]}
Jan. 04, 2019 6:53:22 NACHM. org.hibernate.cfg.Environment <clinit>
INFO: HHH000206: hibernate.properties not found
Exception in thread "main" java.lang.NoClassDefFoundError: net/bytebuddy/NamingStrategy$SuffixingRandom$BaseNameResolver
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.cfg.Environment.buildBytecodeProvider(Environment.java:345)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.cfg.Environment.buildBytecodeProvider(Environment.java:337)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.cfg.Environment.<clinit>(Environment.java:230)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.boot.registry.StandardServiceRegistryBuilder.<init>(StandardServiceRegistryBuilder.java:78)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.boot.registry.StandardServiceRegistryBuilder.<init>(StandardServiceRegistryBuilder.java:67)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.<init>(EntityManagerFactoryBuilderImpl.java:202)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.<init>(EntityManagerFactoryBuilderImpl.java:174)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.jpa.boot.spi.Bootstrap.getEntityManagerFactoryBuilder(Bootstrap.java:76)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.jpa.HibernatePersistenceProvider.getEntityManagerFactoryBuilder(HibernatePersistenceProvider.java:171)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.jpa.HibernatePersistenceProvider.getEntityManagerFactoryBuilderOrNull(HibernatePersistenceProvider.java:119)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.jpa.HibernatePersistenceProvider.getEntityManagerFactoryBuilderOrNull(HibernatePersistenceProvider.java:61)
        at org.hibernate.orm.core@5.4.0.Final/org.hibernate.jpa.HibernatePersistenceProvider.createEntityManagerFactory(HibernatePersistenceProvider.java:50)
        at java.persistence@2.2/javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:79)
        at java.persistence@2.2/javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:54)
        at testing@1.0-SNAPSHOT/de.testing.Main.main(Main.java:9)
Caused by: java.lang.ClassNotFoundException: net.bytebuddy.NamingStrategy$SuffixingRandom$BaseNameResolver
        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)
        ... 15 more

Однако, когда я удаляю

id 'org.openjfx.javafxplugin' version '0.0.5'

из моего списка плагинов Gradle и перераспределить приложение, оно работает нормально, используя распределенный стартовый скрипт из gradle: installDist.

1 Ответ

0 голосов
/ 05 января 2019

Проблема может быть легко воспроизведена с кодом, который вы отправили.

Я могу подтвердить, что это связано с плагином javafx-gradle-plugin, который, в свою очередь, использует плагин java9-modularity. Проблема уже была подана здесь некоторое время назад, и, вероятно, она связана.

В основном проблема заключается в том, что с этим плагином все зависимости добавляются в путь модуля, и ни одна из них не добавляется в путь к классам.

Почему работает IDE?

Когда вы «запускаете из IDE», я предполагаю, что вы запускаете задачу run:

./gradle run

И это прекрасно работает. Если вы делаете:

./gradle --info run

напечатает командную строку. Что-то вроде:

/Users/<user>/Downloads/jdk-11.0.1.jdk/Contents/Home/bin/java \
     --add-modules javafx.controls,javafx.fxml \
     --module-path  /path/to/your-project/build/classes/java/main: \
         /path/to/your-project/build/resources/main: \
         /Users/<user>/.gradle/caches/modules-2/files-2.1/org.hibernate/hibernate-core/5.4.0.Final/70...2b/hibernate-core-5.4.0.Final.jar: \
         ...
         /Users/<user>/.gradle/caches/modules-2/files-2.1/com.sun.xml.fastinfoset/FastInfoset/1.2.15/bb..e8/FastInfoset-1.2.15.jar \
     -Dfile.encoding=UTF-8 -Duser.variant \
     -cp /path/to/your-project/build/classes/java/main: \
         /path/to/your-project/build/resources/main: \
         /Users/<user>/.gradle/caches/modules-2/files-2.1/org.hibernate/hibernate-core/5.4.0.Final/70...2b/hibernate-core-5.4.0.Final.jar: \
         ...
         /Users/<user>/.gradle/caches/modules-2/files-2.1/com.sun.xml.fastinfoset/FastInfoset/1.2.15/bb..e8/FastInfoset-1.2.15.jar \
     de.testing.Main

Запуск с build/install/your-project/lib

Таким образом, вы можете попытаться запустить вручную из папки lib, полученной из задачи installDist, со всеми банками. С терминала:

./gradlew installDist
cd build/install/your-project/lib
/Users/<user>/Downloads/jdk-11.0.1.jdk/Contents/Home/bin/java \
     --add-modules javafx.controls,javafx.fxml \
     --module-path . \
     -cp '*' \
     de.testing.Main

Это должно сработать.

Проблема

Так почему запуск сценария не удался?

Если вы редактируете скрипт:

cd build/install/your-project/bin
nano your-project

Вы можете проверить, что строка:

...
esac

CLASSPATH=

# Determine the Java command to use to start the JVM.
if [ -n "$JAVA_HOME" ] ; then
...

содержит пустой путь к классам. И если вы видите окончательные варианты:

# Collect all arguments for the java command, following the shell quoting and substitution rules
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $YOUR-PROJECT_OPTS -classpath "\"$CLASSPATH\"" de.testing.Main "$APP_ARGS"

это означает, что окончательная командная строка будет выглядеть примерно так:

/Users/<user>/Downloads/jdk-11.0.1.jdk/Contents/Home/bin/java \
     --add-modules javafx.controls,javafx.fxml \
     --module-path . \
     -classpath \        // <--- Nothing is added here!
     de.testing.Main

Это на самом деле начинается, потому что все jar-файлы были добавлены в путь к модулю. Но происходит сбой с опубликованным исключением, поскольку путь к классу пуст.

Возможные исправления

Ручное исправление

Одним из возможных исправлений является редактирование скрипта после запуска задачи installDist и добавления пути к классам, например:

CLASSPATH="../lib/*"

Сохраните скрипт и запустите:

cd build/install/your-project/bin
./your-project

Это должно сработать.

Изменить installDist Задание

Очевидно, что вы не будете делать это вручную, поэтому вот исправление для задачи startScripts, которое вы можете добавить в свой файл build.gradle:

tasks.startScripts { doLast { def scriptFile = file "$ {outputDir} / $ {applicationName}" scriptFile.text = scriptFile.text.replace ('CLASSPATH =', 'CLASSPATH = \' $ APP_HOME / lib / * \ '') } }

Удалить плагин

Другое возможное решение - удалить плагин и использовать старый подход:

dependencies {
    compile group: 'org.hibernate', name: 'hibernate-core', version: '5.4.0.Final'
    compile group: 'com.h2database', name: 'h2', version: '1.4.197'

    compile "org.openjfx:javafx-base:11.0.1:mac"
    compile "org.openjfx:javafx-controls:11.0.1:mac"
    compile "org.openjfx:javafx-graphics:11.0.1:mac"
    compile "org.openjfx:javafx-fxml:11.0.1:mac"
}

compileJava {
    doFirst {
        options.compilerArgs = [
                '--module-path', classpath.asPath,
                '--add-modules', 'javafx.controls,javafx.fxml'
        ]
    }
}

run {
    doFirst {
        jvmArgs = [
                '--module-path', classpath.asPath,
                '--add-modules', 'javafx.controls,javafx.fxml'
        ]
    }
}

После запуска задачи installDist вы можете убедиться, что скрипт содержит полный путь к классу:

CLASSPATH=$APP_HOME/lib/your-project.jar:$APP_HOME/lib/hibernate-core-5.4.0.Final.jar:...:$APP_HOME/lib/FastInfoset-1.2.15.jar

Shadow Jar

Другое решение, сохраняющее плагин, но создающее теневую банку:

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

где Launcher - класс, который не расширяется Application:

public class Launcher {

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

Теперь вы можете запустить задачу jar, и она создаст теневую флягу со всеми зависимостями, включая зависимости JavaFX. Тогда вы можете запустить:

./gradlew jar
cd build/libs
java -jar your-project.jar

Подать вопрос

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

...