Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove dependency on java.desktop and other modules #26884

Open
brunoborges opened this issue Apr 29, 2021 · 8 comments
Open

Remove dependency on java.desktop and other modules #26884

brunoborges opened this issue Apr 29, 2021 · 8 comments

Comments

@brunoborges
Copy link

@brunoborges brunoborges commented Apr 29, 2021

Spring requires the java.desktop module to be present at Java runtimes only so that it can use the classes in the java.beans package.

Would be a good start for modernizing Spring apps on a post Java 9+ era of modules if Spring could at least work on slim Java runtimes produced with jlink.

Other modules to consider adjusting the dependency are java.naming, java.xml, java.sql, jdk.*, java.instrument, java.management,java.rmi, java.scripting.

This is not about making Spring compatible with Java SE modules. This is only to allow developers to have smaller Java runtimes created with jlink.

In an ideal world, a Java runtime, created with the following jlink command should be sufficient to run a Spring Boot Hello World with the Web dependency:

$ jlink \
        --add-modules java.base,java.logging \
        --strip-debug \
        --no-man-pages \
        --no-header-files \
        --compress=2 \
        --output /javaruntime
@dreis2211
Copy link
Contributor

@dreis2211 dreis2211 commented Apr 29, 2021

I guess this is more on the Framework side of things at https://github.com/spring-projects/spring-framework. The java.beans package is quite a central piece if you ask me. Boot has a couple of dependencies to java.beans & java.awt as well, but the heavy lifting must be done on the framework side. Let's see what the team has to say on this and if they want to move the ticket there.

Having that said - I'd love to see this in a perfect world, but I doubt it's easy or straightforward, if at all possible.

@snicoll snicoll transferred this issue from spring-projects/spring-boot Apr 30, 2021
@jhoeller jhoeller self-assigned this Apr 30, 2021
@jhoeller jhoeller added this to the General Backlog milestone Apr 30, 2021
@jhoeller
Copy link
Contributor

@jhoeller jhoeller commented Apr 30, 2021

We are aware of our rather wide-spread dependencies across the traditional JDK-scoped libraries. Some of those are entirely optional (e.g. JNDI, JMX, RMI and JSR-223 Scripting are only used on demand and do not have to be present at runtime - at least from the core Spring Framework perspective) but the java.beans package is indeed a hard case. The JDK's own historic misdesign with the unnecessary AWT dependencies in that package caused its inclusion in java.desktop, unfortunately ignoring the common use of the JavaBeans introspector and some of its API types in server-side libraries.

Our only way out is to reimplement the introspection algorithm ourselves and to replace java.beans.PropertyDescriptor and co with corresponding API types of our own, so that's what we're considering for Spring Framework 6.0 (along with the general JDK baseline upgrade and the introduction of module-info definitions across the codebase). This will cause binary compatibility breakage in a few places, but so will the Jakarta EE 9 API migration with its namespace change (also planned for 6.0). So anything we do in that respect, we'll definitely do it for 6.0 (and might then further refine it in 6.x releases).

@brunoborges
Copy link
Author

@brunoborges brunoborges commented May 1, 2021

Thanks a lot Juergen for sharing the roadmap for 6.0. This will certainly speed up modernization of Java applications with latest JDK releases, as Spring has become a de facto standard of server side development.

In the meantime, is there official documentation and/or tooling to help developers produce customized jlink runtimes for 5 and older?

Your comment that some modules are optional is key, but I haven't found this in the docs.

@sdeleuze
Copy link
Contributor

@sdeleuze sdeleuze commented Sep 20, 2021

That will help on native side as well.

@dsyer
Copy link
Member

@dsyer dsyer commented Sep 28, 2021

Here are some more data points. I found that with a vanilla Spring Boot webflux app jdeps will report that it needs

java.base,java.desktop,java.instrument,java.management,java.naming,java.prefs,java.rmi,java.scripting,java.sql,jdk.httpserver,jdk.jfr,jdk.unsupported

(so no XML modules). If you remove the snakeyaml dependency (which needs java.logging) you can run the app with just

java.base,java.desktop,java.naming

You have to add back java.management to get the full logs including JVM uptime and avoid a warning about the PID not being
discovered.

If Spring 6 could shed java.desktop and java.naming that would be cool. The naming dependency comes from CommonAnnotationBeanPostProcessor (there's a reference to a SimpleJndiBeanFactory which is probably never used), so it seems like that could be removed.

I suppose it's an open question for the JDK why jdeps thinks you need all those other modules when actually you don't, at least for the "normal" code paths.

The code I used to test: https://github.com/dsyer/sample-docker-microservice

UPDATE: you can run without java.naming if you use Spring AOT and -DspringAot=true.

@jhoeller
Copy link
Contributor

@jhoeller jhoeller commented Sep 28, 2021

Our JNDI support and therefore the java.naming module is being referenced in two common places, it seems: CommonAnnotationBeanPostProcessor and StandardServletEnvironment. The JNDI support itself is totally optional, we're only really referring to API types such as javax.naming.NamingException, so we could easily make this defensive even in 5.3.x. I'll see what I can do for 5.3.11 there :-)

@mlchung
Copy link

@mlchung mlchung commented Sep 30, 2021

I suppose it's an open question for the JDK why jdeps thinks you need all those other modules when actually you don't, at least for the "normal" code paths.

jdeps --print-module-deps transitively analyzes libraries on the class path and module path if referenced (see jdeps -h output. I included it below). It finds the compile-time view of the transitive dependences. You can use jdeps --compile-view to look at the dependencies (package-level or class-level to understand what depends on what).

Just spring-boot-2.5.5.jar itself requires java.base, java.desktop, java.logging, java.management, java.naming, java.sql and java.xml

This command will show package-level dependences that you can find out what classes references these modules.

$ jdeps --multi-release 17 -cp $CP ${MAVEN_REPOSITORY}/org/springframework/boot/spring-boot/2.5.5/spring-boot-2.5.5.jar 

(You can use --verbose:class to see the class-level dependencies).
Hope this helps.

$ jdeps --help
  :
  --list-deps                   Lists the module dependences.  It also prints
                                any internal API packages if referenced.
                                This option transitively analyzes libraries on
                                class path and module path if referenced.
                                Use --no-recursive option for non-transitive
                                dependency analysis.
  --list-reduced-deps           Same as --list-deps with not listing
                                the implied reads edges from the module graph.
                                If module M1 reads M2, and M2 requires
                                transitive on M3, then M1 reading M3 is implied
                                and is not shown in the graph.
  --print-module-deps           Same as --list-reduced-deps with printing
                                a comma-separated list of module dependences.
                                This output can be used by jlink --add-modules
                                in order to create a custom image containing
                                those modules and their transitive dependences.

@dsyer
Copy link
Member

@dsyer dsyer commented Sep 30, 2021

Thanks @mlchung that's a useful summary and a good example. The point really is that spring-boot-*.jar has mandatory and optional dependencies and jdeps doesn't know how to tell the difference. I think there might be a way to distinguish if we had module-info in our jars. The module system itself has requires static for optional dependencies at runtime, so that might be the way forward for Spring 6.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
7 participants