maven-jpackage-template: Jdeps missing dependencies

Hello,

I’m somewhat reluctant to ask because I have a hunch I’m overlooking something silly, but I feel I’ve checked everything I could think of a couple of times over. I must confess this is the first time I’m using Java modules, and I have a hunch there is something not quite right in that general direction. This is what I did so far:

  • set up the environment according to the readme.md;
  • created a new repo using your template;
  • cloned it locally, tried it out, got a TestApp.dmg, installed that and it works fine;
  • merged the code from my original application in that repository;
  • changed some settings, like adding the javafx-fxml and javafx-web modules (I should replace the -web one with something more light-weight, I only use it to render rich text);
  • added the required Spring boot, H2 and FXWeaver dependencies and the H2 db files …

and hit mvn clean install

Result: git 2021-03-04 01-36-31 2

There’s a small .jar with only the classes from the src/ directory, and one of around ~30Mb in the shaded-jar directory. The unpacked-shade dir looks as if it contains what I would expect (eg Spring, H2, Hibernate etc.). But it goes wrong in the jdeps step. The java-tool reports:

Failed to execute goal io.github.wiverson:jtoolprovider-plugin:1.0.25:java-tool (jdeps) on project ithildin: jdeps 1 Details:

[INFO] 
[INFO] --- jtoolprovider-plugin:1.0.25:java-tool (jdeps) @ ithildin ---
[ERROR] jdeps failed with error code [1]
[ERROR]    --add-modules
[ERROR]    javafx.base,javafx.controls,javafx.graphics,javafx.fxml,javafx.web,java.logging
[ERROR]    --generate-module-info
[ERROR]    /Users/luthien/git/aduial/ithildin-app/target/work
[ERROR]    --module-path
[ERROR]    /Users/luthien/git/aduial/ithildin-app/mac-javafx/javafx-sdk-15.0.1/lib/
[ERROR]    /Users/luthien/git/aduial/ithildin-app/target/shaded-jar/ithildin.jar
[INFO] Error: Missing dependencies: classes not found from the module path and classpath.
To suppress this error, use --ignore-missing-deps to continue.
ithildin
   ch.qos.logback.classic.ViewStatusMessagesServlet   -> javax.servlet.http.HttpServletRequest              not found
   ch.qos.logback.classic.ViewStatusMessagesServlet   -> javax.servlet.http.HttpServletResponse             not found
... (4500 more lines like this)

It’s a LONG list 😉 - it looks like, well, as if I would generate the “Effective POM” in IntelliJ and then spell out every method included therein. Again, I have a hunch this is something really silly, but I can’t find it … 😓

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 51 (23 by maintainers)

Most upvoted comments

I guess that goes to show I’m way more used to building backend services than desktop gui applications!

Thanks for the suggestions! MyBatis and JOOQ sound like they could be useful, I’ll check those out.

On 16 Mar 2021, at 21:48, Will Iverson @.***> wrote:

Looks like the javafx-weaver project hasn’t been updated in around a year.

Technically it’s possible to pick-and-choose bits of Spring. For example, you could just grab the dependency-injection core and Spring Data JPA and embed only those components. That said, my guess is that it’s going to be a lot of work sorting out the various components for relatively little reward.

If you are just looking for something to make queries easier, I’d check out MyBatis (https://blog.mybatis.org) and JOOQ instead. (https://www.jooq.org). Both of those are easier for embedding into an app like JavaFX than Spring Boot (IMHO). I love Spring Boot as a web development framework, but my (entirely personal, subjective) take is that it’s probably more work to embed in your situation than not.

As an aside, I actually do think there is a scenario for creating a Spring Boot local desktop app, more along the lines of an Electron (or even more precisely, https://neutralino.js.org) app. That would allow a Java/Spring Boot or JavaScript dev to build an app with a Spring Boot backend and ordinary HTML/JavaScript frontend.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or unsubscribe.

Ok, I’ve updated the plugin to play better with Maven (e.g. running install again without running clean first), and also gone through and massively cleaned up, simplified, and documented the pom.xml and the rest of the documentation.

Let me know what you think!

So, good news!

I have rebuilt this template entirely around the idea of skipping the entire “modularize your app” concept and instead switched to “use jlink to build a trimmed custom JVM and just bundle your app with that.”

Basically, the template now builds an installer by creating a custom JVM based on the modules you pick (basically just JavaFX modules), collects all of the Maven dependencies as jars and then just builds an ordinary Java app with that. It’s much, much more simple.

I’ve confirmed that this new, refactored template is now working both with the installers and by running javafx:run inside of IntelliJ, so that all looks pretty good.

The hilarious part is that the final generated installer and application sizes are very similar if not basically identical to the sizes of the app I was getting going through all of the stuff with jdeps, etc.

RE: the time put in, this has all been very helpful research for me! Plus it’s made me rethink my overall strategy, which is actually a good thing IMHO.

I think I’m going to pursue / refactor this template with a strategy along the lines of what’s described in this article:

https://www.devdungeon.com/content/how-create-java-runtime-images-jlink

In this scenario, the “small JVM” will still be created, and the developer will just get to pick and choose which parts of the JDK will be bundled. The article even includes sample configurations for Swing and JavaFX.

I agree that it’s a surprising mess and I have a theory as to what happened…

The JDK had swollen to become an unmanageable mess of circular dependencies internally. Way too many deprecated or useless things had strange dependencies. Modules were created as a way to absolutely enforce breaking the JDK up into pieces as an internal tool. This allowed them to do things like break out JavaFX, begin to increase the pain for continuing to rely on unsupported/deprecated options (e.g. some of the com.sun.unsafe stuff).

They wrote it all up as a spec and got it to work for the JDK itself… and then just kind of dropped the ball on evangelizing and supporting it. I think the management of the JDK team got what it wanted (a way to manage the JVM development) and just didn’t have the energy or interest to develop it further.

I’m going to file a bug/feature request with the JDK issue tracker to ask for a combination of either documenting how to get jlink and jdeps to work with Spring Boot and/or providing enhancements for jlink/jdeps to make the process reasonable.

I’m going to directly call out how Spring Boot is the most popular web services tool, and that GraalVM and Spring Native are directly competing in that space.

With regard to desktop development in general, I think the problem is the overall lack of a business model for desktop. It’s incredible to me that back in the 1995-97 era, Java was bundled in with every browser and was a direct competitor to Flash. If they had invested in client-side Java back then…

My not-so-secret goal is to make Java desktop development easy and then offer cloud services to make it much easier to work with them - ChangeNode.com will offer automatic updates, crash reporting, and analytics. As part of that effort, I need very simple templates to make it easy to build desktop apps.

I thought that by just writing some Maven plugin stuff to automate the process, it would be possible to add in a few lines of scripts and make an app modular. Now that I’ve gone through this, I realize that’s just not going to happen. Heck, even the build time for running jdeps to process all of the modules is kind of awful.

So, onward and upward. At least I got a pretty solid deep dive into the module system, lol.

The repo is very helpful.

First, I removed the extra dependencies you added - the goal is for you not to need to do that.

I was able to get pretty far using the configuration below. The debug line spams the console with a lot more information. When I run this, it completes the module-info generations via jdeps, which is absolutely the hard part, but it’s failing when trying to attach the module-info to the jar.

For this run, it’s failing with the line: Adding info for log4j-to-slf4j-2.13.3.jar

…and looking at the logs, for some reason jdeps is generating the file for this as org.apache.logging.slf4j, so the reason it’s failing is just because the jar name doesn’t match.

So, this is just a bug in the plugin, where I’m not matching the generated module-info to the jar because sometimes the packages and file names don’t match.

Shouldn’t be too bad to fix, hopefully can do it today. I’ll test against your pom.xml to make sure it’s working. This is a very good test case with “jars in the wild” for this plugin. 😃

                    <configuration>
                        <providedModuleDirectories>
                            <directory>${javafx.mods}</directory>
                            <directory>${java.home}\jmods\</directory>
                        </providedModuleDirectories>
                        <ignoreJars>
                            <jar>javafx-controls-15.jar</jar>
                            <jar>javafx-fxml-15.jar</jar>
                            <jar>javafx-web-15.jar</jar>
                            <jar>javafx-graphics-15.jar</jar>
                            <jar>javafx-base-15.jar</jar>
                            <jar>jakarta.xml.bind-api</jar>
                            <jar>jaxb-runtime</jar>
                        </ignoreJars>
                        <stripJars>
                            <jar>HikariCP</jar>
                            <jar>classmate</jar>
                            <jar>istack-commons-runtime</jar>
                            <jar>log4j-api</jar>
                            <jar>txw2</jar>
                        </stripJars>
                        <debug>true</debug>
                    </configuration>

FYI, just published the updated plugin to Maven Central and the updated template to this repo as the “copy-dep” branch. I’ll be cleaning up this afternoon. I’ll post here again when that’s done.

Check out how much cleaner the pom is in the new version, lol. Also runs faster too.

So, a bit of good news, a bit of a head scratcher.

The good news: I added a new command to the jtoolprovider Maven plugin that:

  • Looks at all the declared dependencies
  • Drops all of the declared dependencies that are already modules into one folder
  • Drops all of the declared non-module dependencies into another folder
  • Uses jdeps and moditect to automatically turn all of the non-module dependencies into modules
  • No longer uses a shaded jar at all, which means stuff like Ikonli should work with no configuration.

I’ve posted these changes as WIP to the current jtoolprovider plugin. You can check it out, run mvn install and update the version in your project to use the simplified command. I’ve created a branch on the jpackage-template called copy-dep you can look at to see my WIP testing with the template - and how much smaller the pom is now.

I started testing HikariCP and ran into an interesting problem. Apparently, HikariCP actually IS a module, but for some reason jdeps is insisting on having all of the HikariCP dependencies declared in the HikariCP module-info present, even though virtually all of them are declared as optional. I think the fix for this would be to (sigh) add a command to optionally strip the HikariCP module-info.

I’m declaring victory for the evening, should have more time to look at this later. FWIW I’m tremendously encouraged by how much smaller the pom is for this scenario, and how much more robust it will be going forward. Ironically, apparently there are a LOT of incorrect module-info declarations, which is just…

https://github.com/sormuras/modules/blob/main/doc/suspicious/impostor-modules.md https://github.com/sormuras/modules/tree/main/doc/suspicious

So, in order to deal with incorrect declarations I need to have the ability to quickly mark them as bogus and regenerate anyways. 😛

I think one reason the template is blowing up is because it generates the shaded jar by blowing away all of the manifest entries in the declared jars. So, I think the problem with HikariCP is that if you include it in the shaded JAR the removal of the manifest for multi-release gets broken, and if you exclude from the shaded JAR it’s not a module independently. The fix (I think) is to generate a module info for HikariCP specifically, making it a module, and then excluding it from the shaded jar. Yeesh.

So, I’m literally working on a Maven plugin right now that loops through all of the declared dependencies in the Maven project, figures out if the dependency is a module or not, and then (hopefully) if it’s not a module I can generate the module-info.class via jdeps and attach it. My hope is that this will work for multi-release and make the entire thing just automatic. The strange variety of jar files over the years is kind of crazy - services, multi-release, modules… 😛

Crossed-fingers…

I will note as a fallback that technically jpackage will work without modules - I believe you can just point it at a classpath and have it go. The only real drawback is that then you will include the entire JDK, which I think will probably add about 50mb to your build. You might want to try that angle if the extra 50mb or so isn’t a problem.

Hmm.

It might be possible to use https://maven.apache.org/plugins/maven-dependency-plugin/copy-dependencies-mojo.html to just drop all of the Maven dependencies into a folder and point module path and/or classpath at that.

A Spring Boot template version of this app has been on my to do list. This is interesting stuff, might be able to put some time in on it next week FWIW. I am hoping for a solution that takes the burden of picking-and-choosing dependencies manually and doing all this config off of the developer. Hmm.