使用自动模块的JavaFX应用程序的分发

8
我已创建了一个JavaFX应用程序。它在我的Intellij IDE中完美运行。现在我想分发这个应用程序 - 也就是说,我想获取一个安装程序,用户可以下载,然后它会为他们安装应用程序。
我找到了一篇非常有趣的文章here。这篇博客文章基本上描述了我想要实现的内容。不过有两个区别:
  1. 我正在使用Maven而不是Gradle
  2. 我有使用iText7和apache.commons.lang3等自动模块的依赖项
使用自动模块使事情变得非常复杂。有一个名为ModiTect的GitHub项目(here)被编写来解决这些问题。但我没有使用ModiTect的经验,甚至我的Maven知识也几乎不存在(意思是:我不太清楚我在pom.xml中做什么)。
我需要的是一个解释(逐步)如何将ModiTect(如果必要,还包括jpackage)集成到我的pom.xml中,以便为使用自动模块的JavaFX应用程序获取安装程序(以及sqlite数据库,虽然这不应该是问题)。有人能提供这个说明或者给我推荐一个教程吗?
我在这个问题的结尾提供了一个MWE。这个MWE是一个TestApp。为了说明问题,请运行应用程序并按“Print PDF”按钮。pdf文件会被创建在资源-> pdf中。
当执行javafx:run时,MWE将会编译和运行。当执行javafx:jlink时,会出现与使用自动模块相关的错误。
我不知道该怎么解决这个问题。ModiTect似乎是一个很有前途的插件。另一种可能的方法可以在this GitHub存储库中找到。但正如我之前所说:我的Maven知识不足以真正掌握这里发生了什么。任何帮助都对我意义重大!
MWE:

Project Structure

pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.company</groupId>
    <artifactId>TestApp</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>15</maven.compiler.source>
        <maven.compiler.target>15</maven.compiler.target>
    </properties>
    <dependencies>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>15.0.1</version>
        </dependency>

        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>15.0.1</version>
        </dependency>

        <dependency>
            <groupId>de.jensd</groupId>
            <artifactId>fontawesomefx-fontawesome</artifactId>
            <version>4.7.0-9.1.2</version>
        </dependency>

        <dependency>
            <groupId>org.xerial</groupId>
            <artifactId>sqlite-jdbc</artifactId>
            <version>3.34.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-lang3</artifactId>
            <version>3.12.0</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>kernel</artifactId>
            <version>7.1.14</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>layout</artifactId>
            <version>7.1.14</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>io</artifactId>
            <version>7.1.14</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-jdk14</artifactId>
            <version>1.7.30</version>
        </dependency>

    </dependencies>
    <build>
        <plugins>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <release>15</release>
                    <source>15</source>
                    <target>15</target>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.5</version>
                <configuration>
                    <mainClass>com.company.TestApp</mainClass>
                </configuration>
            </plugin>

        </plugins>
    </build>
</project>

module-info.java:

module com.company {
    requires javafx.controls;
    requires javafx.fxml;
    requires java.sql;
    requires org.apache.commons.lang3;
    requires kernel;
    requires layout;
    requires io;
    requires sqlite.jdbc;
    requires javafx.graphics;

    opens com.company to javafx.fxml;
    opens com.company.controllers to javafx.fxml;

    exports com.company;
    exports com.company.controllers;
}

TestAppController.java:

package com.company.controllers;


import javafx.event.ActionEvent;

import javafx.fxml.FXML;

import javafx.scene.control.Button;
import javafx.scene.control.TextArea;

import org.apache.commons.lang3.StringUtils;

import com.itextpdf.kernel.pdf.PdfDocument;
import com.itextpdf.kernel.pdf.PdfWriter;

import com.itextpdf.layout.element.Paragraph;
import com.itextpdf.layout.Document;

import java.io.FileNotFoundException;


public class TestAppController {

    @FXML
    private TextArea taText;

    @FXML
    private Button btnPrint;


    public void handleButtonAction(ActionEvent event) {
        if (event.getSource() == btnPrint) {
            setTaText();
            printPdf();
        }
    }

    public void setTaText() {
        taText.setText(StringUtils.leftPad("Random Text left padded by 50", 50));
    }

    public void printPdf() {
        String directoryString = "src/main/resources/com/company/pdf";

        try {
            String filepath = directoryString + "/" + "pdf_1" + ".pdf";
            PdfWriter writer = new PdfWriter(filepath);
            PdfDocument pdf = new PdfDocument(writer);
            Document document = new Document(pdf);
            document.add(new Paragraph(taText.getText()));
            document.close();
        } catch (FileNotFoundException e) {
            System.out.println(e.getMessage());
            return;
        }

    }
}

TestApp.java:

package com.company;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;


public class TestApp extends Application {

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


    public void start(Stage primaryStage) throws Exception {
        Parent root = FXMLLoader.load(getClass().getResource("testApp.fxml"));
        Scene scene = new Scene(root);

        primaryStage.setScene(scene);
        primaryStage.setTitle("Test");

        primaryStage.show();
    }
}

testApp.fxml:

<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.TextArea?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.text.Font?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.company.controllers.TestAppController">
   <top>
      <AnchorPane prefHeight="60.0" prefWidth="600.0" style="-fx-background-color: #337DFF;" BorderPane.alignment="CENTER" />
   </top>
   <center>
      <AnchorPane prefHeight="200.0" prefWidth="200.0" BorderPane.alignment="CENTER">
         <children>
            <Button fx:id="btnPrint" layoutX="240.0" layoutY="155.0" mnemonicParsing="false" onAction="#handleButtonAction" prefHeight="25.0" prefWidth="120.0" style="-fx-background-color: #337DFF;" text="Print PDF" textFill="WHITE">
               <font>
                  <Font name="System Bold" size="15.0" />
               </font>
            </Button>
            <TextArea fx:id="taText" layoutX="125.0" layoutY="44.0" prefHeight="82.0" prefWidth="350.0" />
         </children>
      </AnchorPane>
   </center>
</BorderPane>

看看这里有没有适用的内容。 - SedJ601
请查看此链接以获取最小化可重现示例(MWE)——不行,所有细节必须在这里包含(外部链接会失效,使问题和潜在答案对未来读者无用)。 - kleopatra
1
我只是在引用你问题中的一句话 ;) 重申一下:不要引用外部资源(你的完整项目?),写一个 [mcve] 并将其示例发布在这里。 - kleopatra
好的入门..现在注意__M__ :) 也就是说,将其简化为仅包含问题和自动模块(不需要任何花哨的UI,一个尝试从后端获取一些元数据的简单页面就足够了) - kleopatra
好的,仍然不是最小化的,但我认为这是一个很好的例子。pom.xml包含了我的示例不需要的依赖项,但我的实际应用程序需要 - 这就是为什么我将它们留在内部的原因。 - Luk
显示剩余4条评论
1个回答

4

您可以使用Moditect插件代替JavaFX Maven插件来创建缺失的module-info文件并自动处理模块依赖性,然后使用Moditect构建镜像。

为您提供的pom可能类似于:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.company</groupId>
<artifactId>TestApp</artifactId>
<version>1.0-SNAPSHOT</version>

<properties>
    <appName>TestApp</appName>
    <launcherName>testapp</launcherName>
    <moduleName>com.company</moduleName>
    <mainClass>com.company.LoginApp</mainClass>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>15</maven.compiler.source>
    <maven.compiler.target>15</maven.compiler.target>
    <version.java>15</version.java>
</properties>

<dependencies>
    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-controls</artifactId>
        <version>15.0.1</version>
    </dependency>

    <dependency>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-fxml</artifactId>
        <version>15.0.1</version>
    </dependency>

    <dependency>
        <groupId>de.jensd</groupId>
        <artifactId>fontawesomefx-fontawesome</artifactId>
        <version>4.7.0-9.1.2</version>
    </dependency>

    <dependency>
        <groupId>org.xerial</groupId>
        <artifactId>sqlite-jdbc</artifactId>
        <version>3.34.0</version>
    </dependency>

    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-lang3</artifactId>
        <version>3.12.0</version>
    </dependency>

    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>kernel</artifactId>
        <version>7.1.14</version>
    </dependency>

    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>layout</artifactId>
        <version>7.1.14</version>
    </dependency>

    <dependency>
        <groupId>com.itextpdf</groupId>
        <artifactId>io</artifactId>
        <version>7.1.14</version>
    </dependency>

    <dependency>
        <groupId>org.slf4j</groupId>
        <artifactId>slf4j-api</artifactId>
        <version>1.7.30</version>
    </dependency>

</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.8.1</version>
            <configuration>
                <release>${version.java}</release>
            </configuration>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-dependency-plugin</artifactId>
            <version>3.1.2</version>
            <executions>
                <execution>
                    <id>copy-dependencies</id>
                    <phase>package</phase>
                    <goals>
                        <goal>copy-dependencies</goal>
                    </goals>
                    <configuration>
                        <outputDirectory>${project.build.directory}/modules</outputDirectory>
                        <overWriteReleases>false</overWriteReleases>
                        <overWriteSnapshots>false</overWriteSnapshots>
                        <overWriteIfNewer>true</overWriteIfNewer>
                    </configuration>
                </execution>
            </executions>
        </plugin>
        <plugin>
            <groupId>org.moditect</groupId>
            <artifactId>moditect-maven-plugin</artifactId>
            <version>1.0.0.RC1</version>
            <executions>
                <execution>
                    <id>add-module-info-to-dependencies</id>
                    <phase>package</phase>
                    <configuration>
                        <outputDirectory>${project.build.directory}/modules</outputDirectory>
                        <overwriteExistingFiles>true</overwriteExistingFiles>
                        <modules>
                            <module>
                                <artifact>
                                    <groupId>com.itextpdf</groupId>
                                    <artifactId>kernel</artifactId>
                                </artifact>
                                <moduleInfo>
                                    <name>kernel</name>
                                </moduleInfo>
                            </module>
                            <module>
                                <artifact>
                                    <groupId>com.itextpdf</groupId>
                                    <artifactId>layout</artifactId>
                                </artifact>
                                <moduleInfo>
                                    <name>layout</name>
                                </moduleInfo>
                            </module>
                            <module>
                                <artifact>
                                    <groupId>com.itextpdf</groupId>
                                    <artifactId>io</artifactId>
                                </artifact>
                                <moduleInfo>
                                    <name>io</name>
                                </moduleInfo>
                            </module>
                            <module>
                                <artifact>
                                    <groupId>org.xerial</groupId>
                                    <artifactId>sqlite-jdbc</artifactId>
                                </artifact>
                                <moduleInfo>
                                    <name>sqlite.jdbc</name>
                                </moduleInfo>
                            </module>
                            <module>
                                <artifact>
                                    <groupId>org.apache.commons</groupId>
                                    <artifactId>commons-lang3</artifactId>
                                </artifact>
                                <moduleInfo>
                                    <name>org.apache.commons.lang3</name>
                                </moduleInfo>
                            </module>
                            <module>
                                <artifact>
                                    <groupId>org.slf4j</groupId>
                                    <artifactId>slf4j-api</artifactId>
                                </artifact>
                                <moduleInfo>
                                    <name>org.slf4j</name>
                                </moduleInfo>
                            </module>
                        </modules>
                        <module>
                            <mainClass>${mainClass}</mainClass>
                            <moduleInfoFile>${project.build.sourceDirectory}/module-info.java</moduleInfoFile>
                        </module>
                        <jdepsExtraArgs>
                            <args>--multi-release</args> <args>15</args>
                            <args>--ignore-missing-deps</args>
                        </jdepsExtraArgs>
                    </configuration>
                    <goals>
                        <goal>add-module-info</goal>
                    </goals>
                </execution>
                <execution>
                    <id>create-runtime-image</id>
                    <phase>package</phase>
                    <goals>
                        <goal>create-runtime-image</goal>
                    </goals>
                    <configuration>
                        <modulePath>
                            <path>${project.build.directory}/modules</path>
                        </modulePath>
                        <modules>
                            <module>${moduleName}</module>
                        </modules>
                        <launcher>
                            <name>${launcherName}</name>
                            <module>${moduleName}</module>
                        </launcher>
                        <compression>2</compression>
                        <stripDebug>true</stripDebug>
                        <outputDirectory>${project.build.directory}/jlink-image</outputDirectory>
                        <ignoreSigningInformation>true</ignoreSigningInformation>
                    </configuration>
                </execution>

            </executions>
        </plugin>
        <plugin>
            <groupId>com.github.akman</groupId>
            <artifactId>jpackage-maven-plugin</artifactId>
            <version>0.1.2</version>
            <executions>
                <execution>
                    <phase>package</phase>
                    <goals>
                        <goal>jpackage</goal>
                    </goals>
                    <configuration>
                        <name>${appName}</name>
                        <type>IMAGE</type>
                        <runtimeimage>${project.build.directory}/jlink-image</runtimeimage>
                        <module>${moduleName}/${mainClass}</module>
                    </configuration>
                </execution>
            </executions>
        </plugin>

    </plugins>
</build>

</project>

我的第一次测试似乎很成功,但还有一些细节需要改进(例如,我不喜欢使用--ignore-missing-deps参数)。

也许这可以帮助你更好地前进。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接