How can I specify --add-opens from a project level and make sure it is taken into account whatever the means to run my app?
Asked Answered
J

4

26

I've recently moved to Java 17 and with it came a couple restrictions requiring me to use --add-opens because of one dependency when running my application.

I need to add this when the java -jar command is ran. For now I found these solutions:

  • I can add it to the command line argument in my Dockerfile that runs the project
java --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/sun.util.calendar=ALL-UNNAMED -jar my.jar
  • I can add it in my MANIFEST.MF through my maven pom.xml
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>
    <configuration>
        <archive>
            <manifestEntries>
                <Add-Opens>java.base/sun.util.calendar java.base/java.util</Add-Opens>
            </manifestEntries>
        </archive>
    </configuration>
</plugin>

Both work fine for production apparently. However when running my app through IntelliJ, it's not picking up the options which is normal I guess. I have to set them in my run configuration (which is also committed to my project by the way) as VM arguments.

I'm looking for a way to ensure consistency automatically and not have to maintain in parallel two places where I declare my add-opens.

EDIT: I'm wondering if something is doable with argfiles. Like have an argfile inside my project that would be referenced in the jar and that could be referenced in an y run configuration. I haven't found much evidence yet but that's the path I'm currently pursuing.

EDIT 2: I added an addopens file at the root of my project and can now reference this from the various points where I need it. For tests, I added this and it worked out of the box with IntelliJ tests AND maven tests together:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <configuration>
        <!-- This adds the options contained in the addopens file to the test JVM arguments -->
        <argLine>@addopens @{argLine}</argLine>
    </configuration>
</plugin>

I also can ship that addopens file in my docker to use in production. I still need to add to my Run configuration in IntteliJ the @addopens manually.

Jumpy answered 2/12, 2021 at 8:18 Comment(4)
If you use argfiles, you still need a JVM argument each time but you could try to tell your IDE to use that JVM argument for every invocation and add it to the maven-exec-plugin and similar (if used).Carrissa
Are you running it via the Java main class in IDE? Check the https://mcmap.net/q/99942/-run-main-class-of-maven-project-duplicate for the exec-maven-plugin example and how you can pass arguments to the Java Main class.Belief
It is a spring boot app so I'm running it in IntelliJ with the classic "Application" configuration type. The exec-maven-plugin doesn't do much apparently regarding this. I had this working with the surfire plugin though. See the EDIT 2 i added for my latest findingsJumpy
You can add jvmArguments into spring-boot-maven-plugin, see https://mcmap.net/q/539843/-how-do-you-execute-maven-39-s-spring-boot-run-when-you-have-jdk-1-8-code and launch spring-boot:run Maven task from Maven tool window. But Spring Boot Application run configuration will not pick up these arguments automatically. Feel free to file a request for this: youtrack.jetbrains.com/issues/IDEABelief
S
8

I add here the current solution I'm using.

My context is that Spring-Boot generates the final executable through a org.springframework.boot:spring-boot-maven-plugin:repackage goal, on Maven install phase.

The idea is to ask for an org.apache.maven.plugins:maven-jar-plugin:jar execution before it, where Add-Exports and Add-Opens are added to the MANIFEST.MF.

  • This must be done during the Maven package phase
  • The id of that execution should be <id>default-jar</id> to override Spring-Boot execution of the jar plugin it already does.
    Else, Maven will complains that it is in front of what it believes to be a second jar, and will ask you to rename it by the mean of a classifier.
  • For a mysterious reason, the =ALL-UNNAMED ending the --add-opens or --add-exports arguments in the command line shall disappear in the manifest entries. Else, they won't be taken into account at execution time.
<!-- Modifier le MANIFEST.MF pour y ajouter les add-opens, add-exports -->
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-jar-plugin</artifactId>

    <executions>
        <execution>
            <id>default-jar</id>
            <phase>package</phase>

            <goals>
                <goal>jar</goal>
            </goals>

            <configuration>
                <archive>
                    <manifestEntries>
                        <Add-Exports>java.base/sun.nio.ch</Add-Exports>
                        <Add-Opens>java.base/java.util java.base/java.io java.base/java.nio java.base/java.lang java.base/java.lang.invoke java.base/sun.security.util java.base/sun.security.action</Add-Opens>
                    </manifestEntries>
                </archive>
            </configuration>
        </execution>
    </executions>
</plugin>

<!-- Générer l'application finale (un fat jar) -->
<plugin>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-maven-plugin</artifactId>

    <executions>
        <!-- Créer le fat jar exécutable de l'application -->
        <execution>
            <id>executable-jar</id>
            <phase>install</phase>
            
            <goals>
                <goal>repackage</goal>
            </goals>
            
            <configuration>
                <executable>true</executable>
            </configuration>
        </execution>
    </executions>
</plugin>

This produces this MANIFEST.MF:

Manifest-Version: 1.0
Created-By: Maven JAR Plugin 3.2.2
Build-Jdk-Spec: 17
Implementation-Title: Application-Backend métier : Services d'enrichiss
 ement Open Data
Implementation-Version: 0.0.12-SNAPSHOT
Add-Exports: java.base/sun.nio.ch
Add-Opens: java.base/java.util java.base/java.io java.base/java.nio java
 .base/java.lang java.base/java.lang.invoke java.base/sun.security.util 
 java.base/sun.security.action
Main-Class: org.springframework.boot.loader.JarLauncher
Start-Class: fr.ecoemploi.run.Application
Spring-Boot-Version: 2.7.9
Spring-Boot-Classes: BOOT-INF/classes/
Spring-Boot-Lib: BOOT-INF/lib/
Spring-Boot-Classpath-Index: BOOT-INF/classpath.idx
Spring-Boot-Layers-Index: BOOT-INF/layers.idx

And I can now substitute

java --add-exports java.base/sun.nio.ch=ALL-UNNAMED \
   --add-opens java.base/java.util=ALL-UNNAMED \
   --add-opens java.base/java.io=ALL-UNNAMED \
   --add-opens java.base/java.nio=ALL-UNNAMED \
   --add-opens java.base/java.lang=ALL-UNNAMED \
   --add-opens java.base/java.lang.invoke=ALL-UNNAMED \
   --add-opens java.base/sun.security.util=ALL-UNNAMED \
   --add-opens java.base/sun.security.action=ALL-UNNAMED \
   -jar target/application-metier-et-gestion.jar

by a:

java -jar target/application-metier-et-gestion.jar

Which is far more convenient for distribution to end users, and will avoid them to change all their command lines, services to run java jars.

Saprophagous answered 13/7, 2023 at 4:31 Comment(1)
IMHO you are missing <jvmArguments>--add-opens... for the spring-boot:run configuration. Which unfortunately duplicates the declaration in pom.xml leading to possible errors.Doubletalk
D
4

You can use the option to add the JDK parameters in the maven plugins like surefire plugin.

       <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-surefire-plugin</artifactId>
            <configuration>
                    <argLine>
                        --add-opens java.base/java.time=ALL-UNNAMED
                        ${surefireArgLine}
                    </argLine>
            </configuration>
       </plugin>

I have tried the above approach for IntelliJ IDE and Java 17 (Temurin-17.0.1). It works fine on running via java -jar command, as well as on running the app via IDE.

If you have multiple such JVM options to add, try keeping those assigned to a property and use that property here in the argLine.

Disequilibrium answered 7/12, 2021 at 9:31 Comment(1)
Note -- the ${surefireArgLine} value is not available, nor I think needed, in Spring Boot 3.2...Parisparish
M
2

Follow steps: edit run/debug configuration -> add the below option in VM options:

--add-opens java.base/java.lang=ALL-UNNAMED
Melodics answered 18/10, 2022 at 7:2 Comment(1)
This doesn't meet the requirements of the question. We shouldn't have to maintain the list here in our IDE configurations and also in some other file that specifies how to run our app in production, or in a CICD pipeline, or anywhere other than our IDE.Counsel
P
1

In case someone is hitting the issue with an old library and gradle with Kotlin, here is the snippet from above answers

tasks.withType<JavaExec> {
    jvmArgs("--add-opens", "java.base/java.lang=ALL-UNNAMED")
}
Planer answered 29/5 at 4:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.