log4j Problem mit jlink

Hallo,

ich kämpfe gerade mit log4j und jlink. Wenn ich das Projekt ausführe wird, werden Meldungen über log4j in den Logfile geschrieben. Nachdem ich ein Image erzeugt habe, bekomme ich beim Starten die Fehlermeldung "ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console...".
Das Image habe ich mit Maven erzeugt.
XML:
<?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>net.schusser</groupId>
    <artifactId>testbed</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <!-- Application Properties -->
        <link.name>${project.artifactId}</link.name>
        <launcher>${project.artifactId}</launcher>
        <appName>${project.artifactId}</appName>
        <main.class>net.schusser.testbed.App</main.class>
        <main.module>Testbed</main.module>
        <java.version>17</java.version>
        <required.maven.version>3.6.3</required.maven.version>

        <!-- Dependency versions -->
        <lombok.version>1.18.24</lombok.version>
        <jetbrains.annotations.version>23.0.0</jetbrains.annotations.version>
        <junit.version>5.9.1</junit.version>

        <javafx.version>19</javafx.version>
        <controsfx-version>11.1.2</controsfx-version>
        <log4j.version>2.19.0</log4j.version>
       

        <!-- Plugin dependencies -->
        <maven.clean.plugin>3.2.0</maven.clean.plugin>
        <maven.compiler.plugin>3.10.1</maven.compiler.plugin>
        <maven.dependency.plugin>3.3.0</maven.dependency.plugin>
        <maven.deploy.plugin>3.0.0-M2</maven.deploy.plugin>
        <maven.enforcer.plugin>3.1.0</maven.enforcer.plugin>
        <maven.install.plugin>3.0.0-M1</maven.install.plugin>
        <maven.jar.plugin>3.2.2</maven.jar.plugin>
        <maven.resources.plugin>3.2.0</maven.resources.plugin>
        <maven.site.plugin>4.0.0-M1</maven.site.plugin>
        <maven.surfire.plugin>3.0.0-M6</maven.surfire.plugin>
        <moditect.maven.plugin>1.0.0.RC2</moditect.maven.plugin>
        <jpackage.maven.plugin>0.1.3</jpackage.maven.plugin>
        <maven.pmd.version>3.16.0</maven.pmd.version>
        <pmd.version>6.49.0</pmd.version>
        <codehaus.version.plugin>2.11.0</codehaus.version.plugin>
        <spotbugs.maven.plugin>4.7.2.0</spotbugs.maven.plugin>
        <spotbugs.version>4.7.2</spotbugs.version>

        <!-- other properties -->
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>

    </properties>

    <dependencies>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-controls</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-graphics</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-web</artifactId>
            <version>${javafx.version}</version>
        </dependency>
        <dependency>
            <groupId>org.openjfx</groupId>
            <artifactId>javafx-fxml</artifactId>
            <version>${javafx.version}</version>
        </dependency>

        <dependency>
            <groupId>org.controlsfx</groupId>
            <artifactId>controlsfx</artifactId>
            <version>${controsfx-version}</version>
        </dependency>

       
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>${log4j.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>${log4j.version}</version>
        </dependency>

       
       
        <!-- ************************************************************* -->
       
       
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
            <scope>provided</scope>
        </dependency>

        <!-- Dependency used for @NotNull / @Nullable -->
        <dependency>
            <groupId>org.jetbrains</groupId>
            <artifactId>annotations</artifactId>
            <version>${jetbrains.annotations.version}</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.version}</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-enforcer-plugin</artifactId>
                <version>${maven.enforcer.plugin}</version>
                <executions>
                    <execution>
                        <id>enforce-versions</id>
                        <goals>
                            <goal>enforce</goal>
                        </goals>
                        <configuration>
                            <rules>
                                <requireMavenVersion>
                                    <version>${required.maven.version}</version>
                                </requireMavenVersion>
                            </rules>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>${maven.compiler.plugin}</version>
                <configuration>
                    <release>${java.version}</release>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-clean-plugin</artifactId>
                <version>${maven.clean.plugin}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <version>${maven.deploy.plugin}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-install-plugin</artifactId>
                <version>${maven.install.plugin}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>${maven.jar.plugin}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-resources-plugin</artifactId>
                <version>${maven.resources.plugin}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-site-plugin</artifactId>
                <version>${maven.site.plugin}</version>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${maven.surfire.plugin}</version>
                <configuration>
                    <testFailureIgnore>true</testFailureIgnore>
                </configuration>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-dependency-plugin</artifactId>
                <version>${maven.dependency.plugin}</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.openjfx</groupId>
                <artifactId>javafx-maven-plugin</artifactId>
                <version>0.0.6</version>
                <executions>
                    <execution>
                        <!-- Default configuration for running -->
                        <!-- Usage: mvn clean javafx:run -->
                        <id>default-cli</id>
                        <configuration>
                            <mainClass>${main.module}/${main.class}</mainClass>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
           
            <plugin>
                <groupId>org.moditect</groupId>
                <artifactId>moditect-maven-plugin</artifactId>
                <version>${moditect.maven.plugin}</version>
                <executions>
                    <execution>
                        <id>add-module-info-to-dependencies</id>
                        <phase>package</phase>
                        <configuration>
                            <outputDirectory>${project.build.directory}/modules</outputDirectory>
                            <overwriteExistingFiles>true</overwriteExistingFiles>
                            <!-- Beispiel wie eine Dependency ohne module-info diese bekommen kann! -->
                            <!--
                            <modules>
                                <module>
                                    <artifact>
                                        <groupId>org.json</groupId>
                                        <artifactId>json</artifactId>
                                    </artifact>
                                    <moduleInfo>
                                        <name>org.json</name>
                                    </moduleInfo>
                                </module>
                            </modules>
                            -->

                            <module>
                                <mainClass>${main.class}</mainClass>
                                <moduleInfoFile>${project.build.sourceDirectory}/module-info.java</moduleInfoFile>
                            </module>

                            <jdepsExtraArgs>
                                --ignore-missing-deps
                            </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>${main.module}</module>
                            </modules>
                            <launcher>
                                <name>${launcher}</name>
                                <module>${main.module}</module>
                            </launcher>
                            <compression>2</compression>
                            <jarInclusionPolicy>APP_WITH_DEPENDENCIES</jarInclusionPolicy>
                            <stripDebug>true</stripDebug>
                            <outputDirectory>${project.build.directory}/jlink-image</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>com.github.akman</groupId>
                <artifactId>jpackage-maven-plugin</artifactId>
                <version>${jpackage.maven.plugin}</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>${main.module}/${main.class}</module>
                        </configuration>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-pmd-plugin</artifactId>
                <version>${maven.pmd.version}</version>
                <dependencies>
                    <dependency>
                        <groupId>net.sourceforge.pmd</groupId>
                        <artifactId>pmd-core</artifactId>
                        <version>${pmd.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>net.sourceforge.pmd</groupId>
                        <artifactId>pmd-java</artifactId>
                        <version>${pmd.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>net.sourceforge.pmd</groupId>
                        <artifactId>pmd-javascript</artifactId>
                        <version>${pmd.version}</version>
                    </dependency>
                    <dependency>
                        <groupId>net.sourceforge.pmd</groupId>
                        <artifactId>pmd-jsp</artifactId>
                        <version>${pmd.version}</version>
                    </dependency>
                </dependencies>
                <configuration>
                    <sourceEncoding>${project.build.sourceEncoding}</sourceEncoding>
                    <minimumTokens>100</minimumTokens>
                    <targetJdk>${java.version}</targetJdk>
                    <linkXRef>false</linkXRef>
                    <rulesets>
                        <ruleset>pmd-ruleset.xml</ruleset>
                    </rulesets>
                </configuration>
                <executions>
                    <execution>
                        <phase>prepare-package</phase>
                        <goals>
                            <!-- pmd does not stop build when violations are found -->
                            <goal>pmd</goal>

                            <!-- check stops the build when violations are found -->
                            <!-- <goal>check</goal> -->
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>versions-maven-plugin</artifactId>
                <version>${codehaus.version.plugin}</version>
                <executions>
                    <execution>
                        <phase>validate</phase>
                        <goals>
                            <goal>display-dependency-updates</goal>
                            <goal>display-plugin-updates</goal>
                            <goal>display-property-updates</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>
</project>
Java:
module Testbed {
    requires javafx.base;
    requires transitive javafx.graphics;
    requires javafx.controls;
    requires javafx.web;
    requires javafx.fxml;
    requires org.controlsfx.controls;
    requires org.apache.logging.log4j;

    opens net.schusser.testbed to javafx.fxml;
   
    exports net.schusser.testbed;
   
}
Java:
package net.schusser.testbed;

import java.io.IOException;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;


public class App extends Application {
    static final Logger logger = LogManager.getLogger(App.class);
    
    private Stage primaryStage;
    private BorderPane rootLayout;

    @Override
    public void start(Stage primaryStage) {
        try {
            this.primaryStage = primaryStage;
            this.primaryStage.setTitle("Testbed");
            //this.primaryStage.getIcons().add(logo);
            
            initRootLayout();
            
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

    
    public void initRootLayout() {
        try {
            // Load root layout from fxml file.
            FXMLLoader loader = new FXMLLoader();
            loader.setLocation(App.class.getResource("main.fxml"));
            rootLayout = (BorderPane) loader.load();

            MainController controller = loader.getController();
            controller.setMainApp(this);
                      
          
            Scene scene = new Scene(rootLayout);
            scene.getStylesheets().add(App.class.getResource("application.css").toExternalForm());
            primaryStage.setScene(scene);
            primaryStage.setOnCloseRequest(new EventHandler<WindowEvent>() {
                @Override
                public void handle(WindowEvent e) {
                    if(!controller.canClose()){
                        e.consume(); // prevent closing   
                    }
                }
            });
            
            primaryStage.show();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    public static void main(String[] args) {
        logger.debug("Start App");
        launch(args);
    }
      
}

Anscheinend fehlt ein Eintrag in dem POM-File. Das gleiche Problem hatte ich auch schon mit Logback.
 
Meine erste Vermutung ist, dass JLink das Modul der Abhängigkeit direkt wieder raus geworfen hat beim Bau des Images.

Versuch einmal, in die module-info.java noch ein
requires org.apache.logging.log4j.core;
einzubauen.

Dies besagt, dass deine Applikation dieses Modul benötigt. log4j-api hat keine solche Abhängigkeit, so dass da dieses Modul nicht als Transitive Abhängigkeit mitkommt. Daher fehlt da ein requires.
Prüfen kann man das z.B. auf mvnrepository.org - wenn man sich
anschaut, dann wird da keine compile dependency gelistet.

Das ist ein generelles Problem bei JLink: Module, die nicht gebraucht werden, werden nicht mitgenommen. So liegen die ganzen Informationen zu den Sprachen und Regionen in dem Modul jdk.localedata - weshalb dann nach jlink nur noch englische Formate bekannt sind so man dieses Modul nicht eingebunden hat!

Das ist aber jetzt erst einmal eine Vermutung von meiner Seite - aber es sollte ja einfach auszutesten sein.
 
Ich hatte es noch eben mal schnell versucht so nachzustellen. Das genannte ist nur ein Teil der Lösung. Das log4j-core hat keine module-info, daher schieben wir die noch unter ... da es auch eine multi-release jar ist, kommt da auch noch ein entsprechender Parameter dazu ... dieses moditect executions Element sieht dann bei mir auf die Schnelle so aus:
XML:
                    <execution>
                        <id>add-module-info-to-dependencies</id>
                        <phase>package</phase>
                        <configuration>
                            <jvmVersion>${java.version}</jvmVersion>
                            <outputDirectory>${project.build.directory}/modules</outputDirectory>
                            <overwriteExistingFiles>true</overwriteExistingFiles>
                            <modules>
                                <module>
                                    <artifact>
                                        <groupId>org.apache.logging.log4j</groupId>
                                        <artifactId>log4j-core</artifactId>
                                    </artifact>
                                    <moduleInfo>
                                        <name>org.apache.logging.log4j.core</name>
                                    </moduleInfo>
                                </module>
                            </modules>

                            <module>
                                <mainClass>${main.class}</mainClass>
                                <moduleInfoFile>${project.build.sourceDirectory}/module-info.java</moduleInfoFile>
                            </module>

                            <jdepsExtraArgs>
                                <arg>--ignore-missing-deps</arg>
                                <arg>--multi-release</arg>
                                <arg>${java.version}</arg>
                            </jdepsExtraArgs>
                        </configuration>
                        <goals>
                            <goal>add-module-info</goal>
                        </goals>
                    </execution>

Aber bei meinem Test rennt er gerade auf einen anderen Fehler den ich heute Abend nicht mehr ansehen kann:
Code:
[DEBUG] Running jdeps --generate-module-info /Users/konrad/Projects/GitHub/JavaFXMavenApp/target/moditect --add-modules org.apache.logging.log4j.core,org.apache.logging.log4j,org.osgi.core,com.lmax.disruptor,disruptor,org.jctools.core,com.fasterxml.jackson.core,com.fasterxml.jackson.databind,com.fasterxml.jackson.annotation,com.fasterxml.jackson.dataformat.yaml,org.yaml.snakeyaml,com.fasterxml.jackson.dataformat.xml,org.codehaus.stax2,com.ctc.wstx,org.fusesource.jansi,javax.mail.api,java.activation,java.mail,activation,javax.jms.api,kafka.clients,lz4.java,snappy.java,jeromq,jnacl,org.apache.commons.compress,commons.csv,org.slf4j --module-path /Users/konrad/.m2/repository/org/apache/logging/log4j/log4j-core/2.19.0/log4j-core-2.19.0.jar:/Users/konrad/.m2/repository/org/apache/logging/log4j/log4j-api/2.19.0/log4j-api-2.19.0.jar:/Users/konrad/.m2/repository/org/osgi/org.osgi.core/6.0.0/org.osgi.core-6.0.0.jar:/Users/konrad/.m2/repository/com/lmax/disruptor/3.4.4/disruptor-3.4.4.jar:/Users/konrad/.m2/repository/com/conversantmedia/disruptor/1.2.15/disruptor-1.2.15.jar:/Users/konrad/.m2/repository/org/jctools/jctools-core/3.3.0/jctools-core-3.3.0.jar:/Users/konrad/.m2/repository/com/fasterxml/jackson/core/jackson-core/2.13.4/jackson-core-2.13.4.jar:/Users/konrad/.m2/repository/com/fasterxml/jackson/core/jackson-databind/2.13.4/jackson-databind-2.13.4.jar:/Users/konrad/.m2/repository/com/fasterxml/jackson/core/jackson-annotations/2.13.4/jackson-annotations-2.13.4.jar:/Users/konrad/.m2/repository/com/fasterxml/jackson/dataformat/jackson-dataformat-yaml/2.13.4/jackson-dataformat-yaml-2.13.4.jar:/Users/konrad/.m2/repository/org/yaml/snakeyaml/1.31/snakeyaml-1.31.jar:/Users/konrad/.m2/repository/com/fasterxml/jackson/dataformat/jackson-dataformat-xml/2.13.4/jackson-dataformat-xml-2.13.4.jar:/Users/konrad/.m2/repository/org/codehaus/woodstox/stax2-api/4.2.1/stax2-api-4.2.1.jar:/Users/konrad/.m2/repository/com/fasterxml/woodstox/woodstox-core/6.3.1/woodstox-core-6.3.1.jar:/Users/konrad/.m2/repository/org/fusesource/jansi/jansi/2.4.0/jansi-2.4.0.jar:/Users/konrad/.m2/repository/javax/mail/javax.mail-api/1.6.2/javax.mail-api-1.6.2.jar:/Users/konrad/.m2/repository/javax/activation/javax.activation-api/1.2.0/javax.activation-api-1.2.0.jar:/Users/konrad/.m2/repository/com/sun/mail/javax.mail/1.6.2/javax.mail-1.6.2.jar:/Users/konrad/.m2/repository/javax/activation/activation/1.1/activation-1.1.jar:/Users/konrad/.m2/repository/javax/jms/javax.jms-api/2.0.1/javax.jms-api-2.0.1.jar:/Users/konrad/.m2/repository/org/apache/kafka/kafka-clients/1.1.1/kafka-clients-1.1.1.jar:/Users/konrad/.m2/repository/org/lz4/lz4-java/1.4.1/lz4-java-1.4.1.jar:/Users/konrad/.m2/repository/org/xerial/snappy/snappy-java/1.1.7.1/snappy-java-1.1.7.1.jar:/Users/konrad/.m2/repository/org/zeromq/jeromq/0.5.2/jeromq-0.5.2.jar:/Users/konrad/.m2/repository/eu/neilalexander/jnacl/1.0.0/jnacl-1.0.0.jar:/Users/konrad/.m2/repository/org/apache/commons/commons-compress/1.21/commons-compress-1.21.jar:/Users/konrad/.m2/repository/org/apache/commons/commons-csv/1.9.0/commons-csv-1.9.0.jar:/Users/konrad/.m2/repository/org/slf4j/slf4j-api/1.7.36/slf4j-api-1.7.36.jar --ignore-missing-deps --multi-release 17 /Users/konrad/.m2/repository/org/apache/logging/log4j/log4j-core/2.19.0/log4j-core-2.19.0.jar
Error: Modules java.activation and activation export package javax.activation to module com.lmax.disruptor

Da hat vielleicht noch jemand Anderes auf die Schnelle eine Idee, woher der Fehler kommt. Ansonsten schaue ich da in Ruhe morgen noch einmal drauf.
 
Ich muss jetzt tatsächlich gestehen, dass ich so auf Anhieb keine Lösung finde.

Erster Versuch: module-info.java automatisch generieren lassen wie oben angegeben -> Das führt zu Folgeproblemen. Hintergrund sind beim Aufruf von jdepts die Liste an Modulen - da behindern sich mehrere Module.

Zweiter Versuch: module-info.java durch manuellen jdeps Aufruf generieren lassen und dann eingetragen. Jetzt läuft alles durch, aber beim Start kommt weiter die Meldung, dass der das log4j-core nicht findet.

Mal einfach das moditect plugin von meinem letzten Versuch:
Java:
            <plugin>
                <groupId>org.moditect</groupId>
                <artifactId>moditect-maven-plugin</artifactId>
                <version>${moditect.maven.plugin}</version>
                <executions>
                    <execution>
                        <id>add-module-info-to-dependencies</id>
                        <phase>package</phase>
                        <configuration>
                            <jvmVersion>${java.version}</jvmVersion>
                            <outputDirectory>${project.build.directory}/modules</outputDirectory>
                            <overwriteExistingFiles>true</overwriteExistingFiles>
                            <modules>
                                <!--
                                <module>
                                    <artifact>
                                        <groupId>org.apache.logging.log4j</groupId>
                                        <artifactId>log4j-core</artifactId>
                                    </artifact>
                                    <moduleInfo>
                                        <name>org.apache.logging.log4j.core</name>
                                    </moduleInfo>
                                </module>
                                -->

                                <module>
                                    <artifact>
                                        <groupId>org.apache.logging.log4j</groupId>
                                        <artifactId>log4j-core</artifactId>
                                        <version>${log4j.version}</version>
                                    </artifact>
                                    <moduleInfoSource>
                                    open module org.apache.logging.log4j.core {
                                        requires java.logging;
                                        requires java.rmi;

                                        requires transitive java.compiler;
                                        requires transitive java.desktop;
                                        requires transitive java.management;
                                        requires transitive java.naming;
                                        requires transitive java.scripting;
                                        requires transitive java.sql;
                                        requires transitive java.xml;

                                        exports org.apache.logging.log4j.core;
                                        exports org.apache.logging.log4j.core.appender;
                                        exports org.apache.logging.log4j.core.appender.db;
                                        exports org.apache.logging.log4j.core.appender.db.jdbc;
                                        exports org.apache.logging.log4j.core.appender.mom;
                                        exports org.apache.logging.log4j.core.appender.mom.jeromq;
                                        exports org.apache.logging.log4j.core.appender.mom.kafka;
                                        exports org.apache.logging.log4j.core.appender.nosql;
                                        exports org.apache.logging.log4j.core.appender.rewrite;
                                        exports org.apache.logging.log4j.core.appender.rolling;
                                        exports org.apache.logging.log4j.core.appender.rolling.action;
                                        exports org.apache.logging.log4j.core.appender.routing;
                                        exports org.apache.logging.log4j.core.async;
                                        exports org.apache.logging.log4j.core.config;
                                        exports org.apache.logging.log4j.core.config.arbiters;
                                        exports org.apache.logging.log4j.core.config.builder.api;
                                        exports org.apache.logging.log4j.core.config.builder.impl;
                                        exports org.apache.logging.log4j.core.config.composite;
                                        exports org.apache.logging.log4j.core.config.json;
                                        exports org.apache.logging.log4j.core.config.plugins;
                                        exports org.apache.logging.log4j.core.config.plugins.convert;
                                        exports org.apache.logging.log4j.core.config.plugins.processor;
                                        exports org.apache.logging.log4j.core.config.plugins.util;
                                        exports org.apache.logging.log4j.core.config.plugins.validation;
                                        exports org.apache.logging.log4j.core.config.plugins.validation.constraints;
                                        exports org.apache.logging.log4j.core.config.plugins.validation.validators;
                                        exports org.apache.logging.log4j.core.config.plugins.visitors;
                                        exports org.apache.logging.log4j.core.config.properties;
                                        exports org.apache.logging.log4j.core.config.status;
                                        exports org.apache.logging.log4j.core.config.xml;
                                        exports org.apache.logging.log4j.core.config.yaml;
                                        exports org.apache.logging.log4j.core.filter;
                                        exports org.apache.logging.log4j.core.filter.mutable;
                                        exports org.apache.logging.log4j.core.impl;
                                        exports org.apache.logging.log4j.core.jackson;
                                        exports org.apache.logging.log4j.core.jmx;
                                        exports org.apache.logging.log4j.core.layout;
                                        exports org.apache.logging.log4j.core.layout.internal;
                                        exports org.apache.logging.log4j.core.lookup;
                                        exports org.apache.logging.log4j.core.message;
                                        exports org.apache.logging.log4j.core.net;
                                        exports org.apache.logging.log4j.core.net.ssl;
                                        exports org.apache.logging.log4j.core.osgi;
                                        exports org.apache.logging.log4j.core.parser;
                                        exports org.apache.logging.log4j.core.pattern;
                                        exports org.apache.logging.log4j.core.script;
                                        exports org.apache.logging.log4j.core.selector;
                                        exports org.apache.logging.log4j.core.time;
                                        exports org.apache.logging.log4j.core.time.internal;
                                        exports org.apache.logging.log4j.core.tools;
                                        exports org.apache.logging.log4j.core.tools.picocli;
                                        exports org.apache.logging.log4j.core.util;
                                        exports org.apache.logging.log4j.core.util.datetime;
                                        exports org.apache.logging.log4j.core.util.internal;

                                        provides javax.annotation.processing.Processor with
                                            org.apache.logging.log4j.core.config.plugins.processor.PluginProcessor;
                                        provides org.apache.logging.log4j.core.util.ContextDataProvider with
                                            org.apache.logging.log4j.core.impl.ThreadContextDataProvider;
                                        provides org.apache.logging.log4j.message.ThreadDumpMessage.ThreadInfoFactory with
                                            org.apache.logging.log4j.core.message.ExtendedThreadInfoFactory;
                                        provides org.apache.logging.log4j.spi.Provider with
                                            org.apache.logging.log4j.core.impl.Log4jProvider;

                                    }
                                    </moduleInfoSource>
                                </module>

                            </modules>

                            <module>
                                <mainClass>${main.class}</mainClass>
                                <moduleInfoFile>${project.build.sourceDirectory}/module-info.java</moduleInfoFile>
                            </module>

                            <jdepsExtraArgs>
                                <arg>--ignore-missing-deps</arg>
                                <arg>--multi-release</arg>
                                <arg>${java.version}</arg>
                            </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>${main.module}</module>
                            </modules>
                            <launcher>
                                <name>${launcher}</name>
                                <module>${main.module}</module>
                            </launcher>
                            <compression>2</compression>
                            <!-- <jarInclusionPolicy>NONE</jarInclusionPolicy> -->
                            <jarInclusionPolicy>APP_WITH_DEPENDENCIES</jarInclusionPolicy>
                            <stripDebug>true</stripDebug>
                            <outputDirectory>${project.build.directory}/jlink-image</outputDirectory>
                        </configuration>
                    </execution>
                </executions>
            </plugin>
            <plugin>
                <groupId>com.github.akman</groupId>
                <artifactId>jpackage-maven-plugin</artifactId>
                <version>${jpackage.maven.plugin}</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>${main.module}/${main.class}</module>
                        </configuration>
                    </execution>
                </executions>
            </plugin>


Durch das APP_WITH_DEPENDENCIES liegen die App und alle Abhängigkeiten in dem Verzeichnis jars - und da ist dann auch da log4j-core.jar.

Mit dem log4j 2 kenne ich mich aber auch nicht so tief aus, um hier ggf. noch weiter einzugreifen. Das könnte etwas mit dem Service Loader zu tun haben oder so. Vielleicht kann man da noch irgendwas machen um das als Workaround manuell zu machen ... also direkt in der main oder so. Da müsste aber ein log4j "Spezialist" was zu sagen.

@mrBrown Du hattest im Zusammenhang mit Moditect und Co in der Vergangenheit auch gut weiterhelfen können. Vielleicht hast Du ja etwas Zeit. (Oder irgend jemand Anderes!)

Testprojekt zum Spielen findet sich unter: https://github.com/kneitzel/JavaFXMavenApp/tree/log4j2test
(Habe in meinem Beispiel Mavenprojekt einfach mal die Tests im Branch log4j2test eingecheckt.)
Problem nachvollziehen:
  • Bau der Software mit ./mvnw clean package
  • Start der Software mit ./target/jlink-image/bin/javafxapp
==> Es kommt: "ERROR StatusLogger Log4j2 could not find a logging implementation. Please add log4j-core to the classpath. Using SimpleLogger to log to the console..."

Beim Start direkt aus der IDE oder so kommt die Meldung nicht.
 
Hallo Kneitzel,
danke für die Testes. Genau das Verhalten hatte ich auch. Anscheinend ist "org.apache.logging.log4j.core" nicht im Modulepath/Classpath. Ich denke das ist ein generelles Problem. Das gleiche gibt es wenn man Logback verwendet. Warscheinlich wird man es auch bekommem, wenn man JPA und Hiberernate ins Imgage packen will.
 
Ich habe nochmal etwas nachgeforscht. Das sind die unnamed modules von logj4-core:
org.apache.logging.log4j.core.appender.db.jdbc
org.apache.logging.log4j.core.config.yaml
org.apache.logging.log4j.core.config.builder.api
org.apache.logging.log4j.core
org.apache.logging.log4j.core.config
org.apache.logging.log4j.core.impl
org.apache.logging.log4j.core.appender
org.apache.logging.log4j.core.selector
org.apache.logging.log4j.core.config.plugins.validation.validators
org.apache.logging.log4j.core.config.json
org.apache.logging.log4j.core.net
org.apache.logging.log4j.core.config.plugins.validation
org.apache.logging.log4j.core.appender.mom
org.apache.logging.log4j.core.config.properties
org.apache.logging.log4j.core.config.plugins.processor
org.apache.logging.log4j.core.appender.rolling.action
org.apache.logging.log4j.core.appender.mom.jeromq
org.apache.logging.log4j.core.pattern
org.apache.logging.log4j.core.config.plugins.util
org.apache.logging.log4j.core.time
org.apache.logging.log4j.core.config.xml
org.apache.logging.log4j.core.util
org.apache.logging.log4j.core.net.ssl
org.apache.logging.log4j.core.appender.rewrite
org.apache.logging.log4j.core.script
org.apache.logging.log4j.core.async
org.apache.logging.log4j.core.config.status
org.apache.logging.log4j.core.config.arbiters
org.apache.logging.log4j.core.lookup
org.apache.logging.log4j.core.appender.db
org.apache.logging.log4j.core.config.plugins.convert
org.apache.logging.log4j.core.config.plugins.validation.constraints
org.apache.logging.log4j.core.filter
org.apache.logging.log4j.core.util.datetime
org.apache.logging.log4j.core.config.plugins.visitors
org.apache.logging.log4j.core.jmx
org.apache.logging.log4j.core.config.plugins
org.apache.logging.log4j.core.layout
org.apache.logging.log4j.core.appender.rolling
org.apache.logging.log4j.core.appender.routing
org.apache.logging.log4j.core.appender.nosql
org.apache.logging.log4j.core.appender.mom.kafka
Es muss wohl einen Schalter für Merge Modules in jlink geben. In einer gradle Konfiguration habe ich etwas ähnliches gefunden:

Javascript:
jlink {
...
    mergedModule {
        additive = true
        uses 'org.apache.logging.log4j.util.PropertySource';
        uses 'org.apache.logging.log4j.core.util.ContextDataProvider';
        uses 'org.apache.logging.log4j.core.util.WatchEventService';
        uses 'org.apache.logging.log4j.spi.Provider';
        uses 'org.apache.logging.log4j.message.ThreadDumpMessage.ThreadInfoFactory';
    }
}

Weiß aber nicht wie mich das weiterbringt mit Maven.
 
Ich habe noch etwas gefunden. Die Aussage war: "Vergiss Maven! Benutze Gradle". Ich habe ein Beispiel gefunden und angepasst:



Code:
/*
 * This file was generated by the Gradle 'init' task.
 *
 * This generated file contains a sample Java library project to get you started.
 * For more details take a look at the 'Building Java & JVM projects' chapter in the Gradle
 * User Manual available at https://docs.gradle.org/7.4.2/userguide/building_java_projects.html
 */

plugins {
    id 'application'
    id 'org.javamodularity.moduleplugin' version '1.8.12'
    id 'org.openjfx.javafxplugin' version '0.0.13'
    id "org.beryx.jlink" version "2.25.0"
}

repositories {
    // Use Maven Central for resolving dependencies.
    mavenCentral()
}

sourceCompatibility = "17"
targetCompatibility = "17"

dependencies {
    implementation 'org.apache.logging.log4j:log4j-core:2.19.0'
    implementation group: 'org.controlsfx', name: 'controlsfx', version: '11.1.2'
}

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

application {
    mainClass = "net.schusser.testbed.App"
    mainModule = "testbed"
}

jlink {
    options = ['--strip-debug', '--compress', '2', '--no-header-files', '--no-man-pages']
    launcher {
        name = 'testbed'
    }
    forceMerge('log4j-api')
    jpackage {
        if (org.gradle.internal.os.OperatingSystem.current().windows) {
            installerOptions += ['--win-per-user-install', '--win-dir-chooser', '--win-menu', '--win-shortcut']
            imageOptions += ['--win-console']
        }
    }
}

eclipse {
    project {
        natures 'org.eclipse.buildship.core.gradleprojectnature'
    }

    classpath {
        file {
            whenMerged {
                entries.findAll { isModule(it) }.each {
                    it.entryAttributes['module'] = 'true'
                }

                entries.findAll { isSource(it) && isTestScope(it) }.each {
                    it.entryAttributes['test'] = 'true'
                }

                entries.findAll { isLibrary(it) && isTestScope(it) }.each {
                    it.entryAttributes['test'] = 'true'
                }
            }
        }

        defaultOutputDir = file('build')
        downloadSources = true
        downloadJavadoc = true
    }
}

boolean isLibrary(entry) { return entry.properties.kind.equals('lib') }
boolean isTestScope(entry) { return entry.entryAttributes.get('gradle_used_by_scope').equals('test'); }
boolean isModule(entry) { isLibrary(entry) && !isTestScope(entry); }
boolean isSource(entry) { return entry.properties.kind.equals('src'); }

Leider bekomme ich immer noch einen Fehler im erzeugten Image, aber wenn man das ganze mit Jpackage packen läst ist Log4j-Core mit in dem Packet und es läuft ohne Fehlermeldung. Den "Eclipse" Teil habe ich hin zugefügt, damit der Modulepath in Eclipse richtig gesetzt wird.
 
Generell sind gradle und maven nur build tools. Man muss denen nur sagen, wie etwas gebaut werden soll und dann geht das auch.

Das Problem, dass log4j-core nicht gefunden wurde, war einfach: JLink wurde ohne --bind-services aufgerufen - da fehlte also ein einfaches
<bindServices>true</bindServices> bei der Konfiguration des moditect Plugins.

Das funktioniert damit jetzt fast schon - es gibt gerade noch zur Laufzeit eine Exception weil in der module-info vom core noch eine Sache fehlt. Die muss ich aber morgen raussuchen - heute habe ich keine Zeit mehr dafür. Aber das, was da fehlt, habe ich prinzipiell schon gesehen aber die Stelle muss ich noch einmal raussuchen bzw. den Stacktrace analysieren um daran zu sehen, was fehlt.

Den aktuellen Stand findest Du auf https://github.com/kneitzel/JavaFXMavenApp/tree/log4j2test

(Das mit dem --bind-services hätte ich gleich sehen müssen. Da war ich neulich einfach blind. Als ich das eben mal testweise mit slf4j aufgebaut habe, da war mir das sofort aufgefallen. Aber manchmal hat man einfach Tomaten auf den Augen und braucht etwas Abstand um das Problem zu erkennen.)
 
Log4j ist schon etwas bescheiden - dass die es nicht schaffen, ordentliche module-info zu erstellen.

Ich habe das letzte Problem jetzt noch behoben - es fehlten in der module-info noch zwei 'uses':
Code:
uses org.apache.logging.log4j.core.util.WatchEventService;
uses org.apache.logging.log4j.core.util.ContextDataProvider;

Damit läuft es jetzt einwandfrei. Beispiel findet sich unter https://github.com/kneitzel/JavaFXMavenApp/tree/log4j2test

Zusammengefasst beläuft sich die ganze Problematik einfach auf:
  • Automatische Erstellung der module-info.java durch das moditect Plugin hat so nicht funktioniert - da wurde dann ein jdeps Aufruf gebaut, der so nicht läuft, weil Module mit Konflikten angegeben waren.
  • module-info manuell per jdeps erzeugt war schon gut - es fehlten aber uses Anweisungen. Das wurde dann durch Exceptions angezeigt, z.B.:
Code:
ERROR StatusLogger Unable to load services for service interface org.apache.logging.log4j.core.util.ContextDataProvider
 java.util.ServiceConfigurationError: org.apache.logging.log4j.core.util.ContextDataProvider: module org.apache.logging.log4j.core does not declare `uses`

==> es fehlte ein uses org.apache.logging.log4j.core.util.ContextDataProvider

Also eigentlich noch relativ gradlinig aufzubauen.

Aber ganz wichtig (damit keine falsche Meinung aufkommt):
  • das ist kein Problem von Maven! (wegen "Vergiss Maven! Benutze Gradle")
  • das ist kein generelles Problem (wegen "Das gleiche gibt es wenn man Logback verwendet. Warscheinlich wird man es auch bekommem, wenn man JPA und Hiberernate ins Imgage packen will.")

Das Problem ist einfach auf log4j-core zurück zu führen, da dies ein automodule ist (wie alle Implementation-Jars für die Runtime).
Und spätestens jetzt mit Jakarta EE 10 werden die Macher von log4j das hoffentlich auch bereit stellen - denn da werden modules auch (endlich) unterstützt. Das neue Spring 6 wird Module auch unterstützen so ich das richtig gelesen habe. Also da wird auch auf der Enterprise Front etwas Bewegung in dem Bereich kommen.
 
Super habe Deine Änderungen bei mir eingebaut.
das ist kein Problem von Maven!
Da stimme ich Dir zu. Ich kämpfe mit dem Problem schon zu lange rum. Da kommt etwas Frust auf. Ich denke das generell Problem ist bei all den Libraries, dass sie einfach noch nicht auf das Modulsystem umgestellt sind. Es wird immer auf die neuen Versionen verwiesen (seit Jahren). Da ist noch einen ganz großer Stau in den Pipelines.
Im Moment kostest es einfach zu viel Zeit alles in das Modulsystem zu integrieren.
Vielen Dank für Deine Hilfe!
 
Immer sehr gerne. Es freut mich, wenn ich Dir helfen konnte. Und evtl. auch wichtig: Diese betonten Aussagen gingen nicht gegen Dich sondern sind nur etwas, das ich kurz richtig stellen wollte.

Den Frust kann ich übrigens sehr gut verstehen. Ich war hier ja auch zwischenzeitig etwas überrascht und irritiert, denn gewisse Probleme hatte ich nicht erwartet. Und Google hat mir auch irgendwie sehr wenig bis nichts sinnvolles in diesem Bereich gebracht. Wäre ok, wenn es um etwas ganz neues gehen würde, aber das Modul System gibt es ja doch schon etwas länger ....

Aber große Teile der Java Entwickler-Gemeinde scheinen diese Java Module und das JLink ganz offensichtlich schlicht ignoriert zu haben. Und der Enterprise Bereich mit Java im Backend ist nun einmal der Haupteinsatzbereich (Wenn man mal Android Apps außer acht lässt - wobei da ja Kotlin Java verdrängen dürfte).
 
Ist das Henne Ei Problem. Bibliothek X braucht Y. Solange Y nicht umgestellt ist, lohnt es nicht X umzustellen.
Und wenn man sich mal - vor allem im Enterprise Bereich - die Dependency Listen ansieht, sieht man das Problem.
 
Ich habe es auch nicht persönlich genommen. Ich bin seit Java 17 dabei ein Projekt, was immer noch auf Java 1.8 basiert um zu stellen. Man hat das Gefühl, das keiner die Libraries umstellt. Die Krönung gab es bei Logback. Da gab es Fehlermeldungen, dass es Libraries nicht im Maven Repository gab. Jetzt kann ich mich um die nächste Baustelle "Apache POI" kümmern. Gibt es einen Tipp um die ganzen Abhängigkeiten raus zu bekommen? Google ist da nicht sehr gut. Manchmal denke ich mir, das es einfacher ist die Sourcen der Libraries zu nehmen und selbst mit den Modulinformation zu kompilieren. Hoffentlich hat das alles bald ein Ende.
 
@KonradN : DANKE! Ich stand schon wieder wie der Ochs am Berg und hatte eben genau dasselbe Problem wie @Michael Schusser. Ich habe gegoogelt ohne Ende und bin zu keinem Schluss gekommen. Ich wollte schon alles ins Eck werfen. Ich habe zwar keine Ahnung wie man sich die ganzen Abhängigkeiten anzeigen lassen kann, aber mit deiner Lösung die module-info selbst zu generieren mit moditect hat es bei mir funktioniert. Auch das --bind-services hat geklappt. Auch hier habe ich keine Ahnung warum - aber es geht 😉.

Jedenfalls DANKE!

Grüße,
Thomas
 
Hallo @Thopx14, freut mich, dass ich Dir damit auch weiterhelfen konnte.

Derzeit plane ich da noch ein paar Videos, aber irgendwie ist die Zeit zu begrenzt (zumal ich bei der Aufnahme auch Handwerker, Hund oder Katzen nicht im Video haben will 🙂 ).

Aber ein Video wird das Vorgehen zeigen, das ich genutzt hatte bei der log4j Problematik.

Und ich kann Dich sehr gut verstehen - das sind Probleme, die einen erst einmal kalt erwischen und wo es auch aus meiner Sicht deutlich zu wenig Informationen gibt. Das ist halt noch kein Mainstream - so Abhängigkeiten, die erst zur Laufzeit kommen, brauchte kaum jemand mit module-info. Das kommt erst jetzt (hoffentlich), da die Enterprise Frameworks / APIs jetzt auch nach und nach Module unterstützen. (Spring 6, Jakarta EE 10, ...)

Ich versuche, die Videos dann auch hier zu erwähnen (So ich den Thread dann noch finde). Ansonsten: Auf Mastodon als auch auf PeerTube könntest Du mir auch folgen - da bringe ich nichts anderes. (Also keine Verschwörungstheorien, politische Meinungen und ähnliches ... 🙂 )
 
Hallo Thomas,

probiere mal:
jdeps --ignore-missing-deps --generate-module-info <outputdir> <jarfile>.jar

Die Module-Info wird dann in das <outputdir> geschrieben. Hackelig wird es wenn in dem Jar auf andere Module/Bibliothen verwiesen wird. Dann muß man sich durchhangeln. Ich bin gerade daran zu versuchen für Apache POI die Modul-Info zu erstellen.

Grüße,
Michael
 
Hallo @Thopx14, freut mich, dass ich Dir damit auch weiterhelfen konnte.

Derzeit plane ich da noch ein paar Videos, aber irgendwie ist die Zeit zu begrenzt (zumal ich bei der Aufnahme auch Handwerker, Hund oder Katzen nicht im Video haben will 🙂 ).

Aber ein Video wird das Vorgehen zeigen, das ich genutzt hatte bei der log4j Problematik.

Und ich kann Dich sehr gut verstehen - das sind Probleme, die einen erst einmal kalt erwischen und wo es auch aus meiner Sicht deutlich zu wenig Informationen gibt. Das ist halt noch kein Mainstream - so Abhängigkeiten, die erst zur Laufzeit kommen, brauchte kaum jemand mit module-info. Das kommt erst jetzt (hoffentlich), da die Enterprise Frameworks / APIs jetzt auch nach und nach Module unterstützen. (Spring 6, Jakarta EE 10, ...)

Ich versuche, die Videos dann auch hier zu erwähnen (So ich den Thread dann noch finde). Ansonsten: Auf Mastodon als auch auf PeerTube könntest Du mir auch folgen - da bringe ich nichts anderes. (Also keine Verschwörungstheorien, politische Meinungen und ähnliches ... 🙂 )
Da stimme ich dir zu. Das Problem ist nicht die Komplexität, sondern die Doku. Man findet im Internet oft einfach nix. Wenn du jetzt nicht geholfen hättest, dann hätte ich es wirklich ins Eck geworfen 😅.

Auf Mastodon bin ich noch nicht. Ich schwimme noch auf dem untergehenden Schiff Twitter 🫡.
PeerTube habe ich noch nie gehört. Ist das eine Open-Source Alternative von YouTube? Ich finde Udemy sehr interessant. Da gibts viel Content. Evtl. machst da mal nen Kurs - ich würd ihn kaufen 😊.
 
Hallo Thomas,

probiere mal:
jdeps --ignore-missing-deps --generate-module-info <outputdir> <jarfile>.jar

Die Module-Info wird dann in das <outputdir> geschrieben. Hackelig wird es wenn in dem Jar auf andere Module/Bibliothen verwiesen wird. Dann muß man sich durchhangeln. Ich bin gerade daran zu versuchen für Apache POI die Modul-Info zu erstellen.

Grüße,
Michael
Danke @Michael Schusser . Das Kommando merke ich mir 😊. Ich hoffe ich brauch es nicht mehr so schnell 😅.

Viele Grüße,
Thomas
 

Zurück
Oben