Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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?

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.

like image 943
Crystark Avatar asked Sep 04 '25 01:09

Crystark


2 Answers

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.

like image 177
Marc Le Bihan Avatar answered Sep 06 '25 16:09

Marc Le Bihan


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

--add-opens java.base/java.lang=ALL-UNNAMED
like image 43
Pukhraj soni Avatar answered Sep 06 '25 16:09

Pukhraj soni



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!