6

I get this error when I run this test using JDK 17:

java.lang.reflect.InaccessibleObjectException: Unable to make field final transient java.lang.Class java.util.EnumSet.elementType accessible: module java.base does not "opens java.util" to unnamed module @60addb54
    @Test
    public void testThatDeepCopyCopiesEmptySet() {
        SetOfEnumUserType setOfEnumUserType = createSetOfEnumUserType();
        EnumSet<PaymentMethodType> src = EnumSet.noneOf(PaymentMethodType.class);
        EnumSet<?> dest = (EnumSet<?>) setOfEnumUserType.deepCopy(src);
        assertThat(dest, (is(src)));
        assertThat(dest, not(isSameInstanceAs(src)));
        Class<?> srcType = (Class<?>) ReflectionTestUtils.getField(src, "elementType");
        Class<?> destType = (Class<?>) ReflectionTestUtils.getField(dest, "elementType");
        assertThat(srcType, (is(destType)));
    }

I tried adding this to my pom.xml based on other answers:

           <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                    <compilerArgs>
                        <arg>--add-opens java.base/java.lang=ALL-UNNAMED</arg>
                        <arg>--add-opens java.base/java.util=ALL-UNNAMED</arg>
                    </compilerArgs>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-surefire-plugin</artifactId>
                <version>${surefire.plugin.version}</version>
                <configuration>
                    <argLine>--add-opens java.base/java.lang=ALL-UNNAMED</argLine>
                    <argLine>--add-opens java.base/java.util=ALL-UNNAMED</argLine>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>test</goal>
                        </goals>
                        <inherited />
                    </execution>
                </executions>
            </plugin>

But when I build, I now get this error:

Fatal error compiling: error: invalid flag: --add-opens java.base/java.lang=ALL-UNNAMED
  • 2
    Your question's tag is `java-9` but you have `1.8` in your POM? Where did you get `--add-opens` from? – Gerold Broser Oct 28 '21 at 11:33
  • 1
    `argLine` based approach is very easy to get wrong. Personally I prefer `jvm.config` to set JVM parameters where you set all JVM arguments simply in a text file and Maven will pick it up automatically. See https://maven.apache.org/configure.html#mvn-jvm-config-file. – Kedar Joshi Oct 28 '21 at 11:37
  • I don't think `1.17` or `17` is a thing. –  Oct 29 '21 at 08:36
  • 3
    [this?](https://stackoverflow.com/questions/64920521/how-to-pass-add-opens-jdk-module-configuration-to-maven-test/64920692#64920692) – Eugene Oct 31 '21 at 20:34
  • 3
    `--add-opens` controls the runtime behavior, therefore, there is no point in trying to specify it to the compiler. Just remove it from from the ``. Maybe, the `` argument would do its job if you didn’t try to specify the argument to the compiler as well. But still, the point of this test is unclear. There is no sense in applying a deep-copy operation to an `EnumSet`. Use `EnumSet> dest = src.clone();` and you’ll know that you’ll get a correct result without the need for such a testcase. – Holger Nov 01 '21 at 08:59
  • 5
    “This error is appearing in thousands of places in the codebase.” is another way to say “we have thousand of places in our code base hacking into internals of the Java library, relying on unspecified implementation details”. You already got answers about how to make this error (temporarily) disappear. But this won’t guard you from the other problems you’ll get when implementation details change or the Java code developers raise the protection against such hacks even more. The long term solution *is* to eliminate these unnecessary hacks. – Holger Nov 01 '21 at 09:18
  • 4
    “*but what about maven-compiler-plugin*”—I suggest to reread [this comment](https://stackoverflow.com/questions/69753263/?noredirect=1#comment123369983_69753263), especially the first half. As the (now deleted) answer told you, the compiler doesn’t support this option. For the same reason I explained in the comment. This is a runtime option and trying to specify it to the compiler is pointless. Just specify it to the runtime, like Eugene suggested and not to the compiler and it should work. – Holger Nov 01 '21 at 09:41

1 Answers1

6

@Holger is right, you should not hack into JDK internals. He also explained correctly that --add-opens is not a valid Maven Compiler option. Because he did not write an answer but comments instead, I am writing one. The credit is his.

As for the irritating comments about source and target versions 1.8, this is of course not the root cause of your problem. It is perfectly legal to compile under JDK 17 with compliance level 1.8.

Here is an MCVE - you should have provided one in the first place instead of an uncompilable code snippet and an incomplete POM:

Maven POM:

Please note:

  • --add-opens is now only in the Surefire configuration where it belongs, no longer in the compiler configuration.
  • I removed the illegal option <inherited /> from the configuration.
  • I am relying on the default Surefire execution instead of specifying another one. In your use case that might not be what you want, but for the MCVE it is the simplest option.
  • Instead of two <argLine> tags only one of which will be picked up by Surefire, you have to specify both --add-opens options within the same tag. You can conveniently separate them by newlines.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>SO_Java_AddOpens_69753263</artifactId>
  <version>1.0-SNAPSHOT</version>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <surefire.plugin.version>2.22.2</surefire.plugin.version>
  </properties>

  <build>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.8.1</version>
      </plugin>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-surefire-plugin</artifactId>
        <version>${surefire.plugin.version}</version>
        <configuration>
          <argLine>
            --add-opens java.base/java.lang=ALL-UNNAMED
            --add-opens java.base/java.util=ALL-UNNAMED
          </argLine>
        </configuration>
      </plugin>
    </plugins>
  </build>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.13.2</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

</project>

Test code and helper enum:

package org.example;

public enum PaymentMethodType {
  FOO, BAR, ZOT
}

I simplified your test case as follows:

package org.example;

import org.junit.Test;

import java.lang.reflect.Field;
import java.util.EnumSet;

import static org.junit.Assert.assertEquals;

public class MyTest {
  @Test
  public void testThatDeepCopyCopiesEmptySet() throws NoSuchFieldException, IllegalAccessException {
    EnumSet<PaymentMethodType> src = EnumSet.noneOf(PaymentMethodType.class);
    Field elementType = EnumSet.class.getDeclaredField("elementType");
    elementType.setAccessible(true);
    Class<?> srcType = (Class<?>) elementType.get(src);
    assertEquals(PaymentMethodType.class, srcType);
  }
}
kriegaex
  • 63,017
  • 15
  • 111
  • 202
  • Also you can use these: --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.base=ALL-UNNAMED – Efecan AHMETOĞLU Apr 20 '22 at 07:53
  • @EfecanAHMETOĞLU: Sure, you can open as many modules as you wish, but why? You did not give any reason. Why would you open more modules than you actually need? You should open as few modules and packages as possible. – kriegaex Apr 20 '22 at 10:48