Unable to make field final transient java.lang.Class java.util.EnumSet.elementType accessible: module java.base does not "opens java.util" to unnamed
Asked Answered
S

1

9

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
Scrim answered 28/10, 2021 at 11:27 Comment(7)
Your question's tag is java-9 but you have <source|target>1.8</source|target> in your POM? Where did you get --add-opens from?Anaemic
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 maven.apache.org/configure.html#mvn-jvm-config-file.Magenta
I don't think <source>1.17</source> or <source>17</source> is a thing.Scrim
this?Spiritoso
--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 <compilerArgs>. Maybe, the <argLine> 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.Fir
“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.Fir
but what about maven-compiler-plugin”—I suggest to reread this comment, 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.Fir
M
9

@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);
  }
}
Maida answered 3/11, 2021 at 8:51 Comment(2)
Also you can use these: <argLine> --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 </argLine>Thorpe
@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.Maida

© 2022 - 2024 — McMap. All rights reserved.