13

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.

Crystark
  • 3,693
  • 5
  • 40
  • 61
  • 1
    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). – dan1st Dec 02 '21 at 10:54
  • Are you running it via the Java main class in IDE? Check the https://stackoverflow.com/a/9846103/2000323 for the `exec-maven-plugin` example and how you can pass arguments to the Java Main class. – Andrey Dec 02 '21 at 11:44
  • 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 findings – Crystark Dec 02 '21 at 15:50
  • You can add `jvmArguments` into `spring-boot-maven-plugin`, see https://stackoverflow.com/a/67696568/2000323 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: https://youtrack.jetbrains.com/issues/IDEA – Andrey Dec 07 '21 at 12:41

3 Answers3

5

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

--add-opens java.base/java.lang=ALL-UNNAMED
Pukhraj soni
  • 1,832
  • 2
  • 17
  • 11
2

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.

Jatin Kheradiya
  • 50
  • 1
  • 10
0

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.

Marc Le Bihan
  • 2,308
  • 2
  • 23
  • 41