realm / realm-java

Realm is a mobile database: a replacement for SQLite & ORMs
http://realm.io
Apache License 2.0
11.45k stars 1.75k forks source link

IllegalAccessError with Android Gradle Plugin 8.x and Java 17 #7799

Closed mannodermaus closed 1 year ago

mannodermaus commented 1 year ago

How frequently does the bug occur?

Always

Description

In the process of upgrading our codebase to Android Gradle Plugin 8.x, we are forced to update to Java 17+ in order to compile. Note that this is independent of the sourceCompatibility and targetCompatibility settings in the build scripts: Downgrading these values to a lower number does not prevent this issue from happening.

It seems like usage of internal APIs inside io.realm.processor.Utils causes a compilation error at runtime because of a missing add-opens declaration. Previous versions of JDK might've been more relaxed about it, but now those workarounds seem no longer working. I have tried adding the add-opens declaration to my jvmArgs via gradle.properties, but to no avail: The issue remains.

Since I assume that a refactoring of the offending code might not be feasible, could we maybe introduce a proper module-info.java to the annotation processor so it can tell javac about the required modules itself? Are there any plans to update Realm's toolchain to accommodate the increasing strictness of Java's module system?

Stacktrace & log output

After updating to Java 17 (even with sourceCompatibility and targetCompatibility capped at 11), trying to compile the Android project fails with:

Execution failed for task ':app:compileDebugJavaWithJavac'.
> java.lang.IllegalAccessError: class io.realm.processor.Utils (in unnamed module @0x64c9b2bd) cannot access class com.sun.tools.javac.code.Symbol$ClassSymbol (in module jdk.compiler) because module jdk.compiler does not export com.sun.tools.javac.code to unnamed module @0x64c9b2bd

Can you reproduce the bug?

Always

Reproduction Steps

Version

10.13.3-transformer-api

What Atlas App Services are you using?

Local Database only

Are you using encryption?

No

Platform OS and version(s)

macOS Ventura 13.2.1

Build environment

Android Studio version: 2022.3.1 Canary 11 Android Build Tools version: 33.0.2 Gradle version: 8.0.2

Workarounds

Try using the embedded JDK from Android Studio Flamingo

Add a declaration to the global JVM arguments via gradle.properties

# gradle.properties
org.gradle.jvmargs = --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED

Add a declaration to the Java compilation tasks

// app/build.gradle.kts:

android {
    io.realm.transformer.RealmTransformer.register(project)

    tasks.withType<JavaCompile>().configureEach {
        options.forkOptions.jvmArgs?.add("--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED")
    }
}
rorbech commented 1 year ago

Hi @mannodermaus. I am not able to reproduce your observations. I have tried creating a project from scratch using AGP 8.0.0-rc1, Gradle 8.0.2, Realm 10.13.3-transformer-api and the embedded JDK from Android Studio Flamingo (17.0.6). Can you elaborate on the exact version of AGP and any further details in the project configuration that could be relevant (java-only or Kotlin project, etc.), or ideally a sample project that triggers this.

rorbech commented 1 year ago

Ok, managed to reproduce it with Oracle's JDK 17.0.6. Will have to investigate.

rorbech commented 1 year ago

@mannodermaus Seems to work in my end with this addition to the org.gradle.jvmargs in gradle.properties

org.gradle.jvmargs= ... --add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED

Will have to dig a bit more for the appropriate permanent fix.

mannodermaus commented 1 year ago

Thanks for getting back to me so quickly, Claus! I will try using the embedded JDK from AS Flamingo to see if that resolves the problem.

mannodermaus commented 1 year ago

Okay, unfortunately it doesn't seem to work for me. ☹️ However, I am using the AS Giraffe canary, which might cause the problem (the embedded JDK is the same, though). I tried using the org.gradle.jvmargs definition again, with both the embedded JDK of AS Giraffe (17.0.6) and the Zulu 17.0.0 implementation installed as my JAVA_HOME, but neither seem to work and cause the error above. Granted, I'm trying to upgrade an existing project with more complexities, so I will try it on a greenfield project later.

mannodermaus commented 1 year ago

Good news! While the gradle.properties route did not want to work, I tried some other ways to force the add-opens declaration into the JVM. I'm posting below what eventually worked for me! No idea why this works and the global setting doesn't, but this might be because of our project structure and I don't care enough as long as the compilation succeeds. 🤷‍♂️

// app/build.gradle.kts:

android {
    io.realm.transformer.RealmTransformer.register(project)

    tasks.withType<JavaCompile>().configureEach {
        options.forkOptions.jvmArgs?.add("--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED")
    }
}

I'm editing the OP to reflect this block. I still wonder if we can get around the access inside Utils.kt eventually, but at least there are a few workarounds that people can try.

cmelchior commented 1 year ago

The root cause is the change in Java 17 described here: https://blogs.oracle.com/javamagazine/post/a-peek-into-java-17-continuing-the-drive-to-encapsulate-the-java-runtime-internals and here https://openjdk.org/jeps/403.

It looks like we should be able to provide a fix using the manifest file:

It will still be possible to use the [--add-opens](https://openjdk.java.net/jeps/261#Breaking-encapsulation) command-line option, or the [Add-Opens](https://openjdk.java.net/jeps/261#Packaging:-Modular-JAR-files) JAR-file manifest attribute, to open specific packages.

cmelchior commented 1 year ago

This should be fixed with 10.15.0 which was just released.

cmelchior commented 1 year ago

10.15.0 was released too quickly and turned out to not work after all. 10.15.1 is now available on Maven Central and contains a rewrite of the problematic APIs and should thus work correctly with Java 17.