How to solve: "Symbol is declared in module X which does not export package Y"
Tags: java, module, build, kotlin
Published: 2021-02-18 20:21:43

If you are here, I assume that you have encounter a message like one of these.
```
Symbol is declared in module 'java.desktop' which does not export package 'sun.awt.image'
Symbol is declared in module 'java.xml' which does not export package 'com.sun.org.apache.xerces.internal.xni.parser'
Symbol is declared in module 'java.base' which does not export package 'sun.net.www.protocol.http'
```

# What is happening?

You are accessing a class in a package that was not intended to be used by external library. The class is part of an **internal package that is subject to change**. When updating the library, it is likely that it will break your application.

These modules restrictions were introduce in Java 9.

### Option 1: Long-term fix

If you are working on a project that has long term stability in mind, you should look for an equivalent function that is public. This may required an additional library.

Here is a simple example in Kotlin where `Strings.isNullOrEmpty` was use by accident. The function call can simply be replace by `CharSequence.isNullOrEmpty()`.
```kotlin
import jdk.internal.joptsimple.internal.Strings.isNullOrEmpty

fun main() {
    print("Enter your name : ")
    val userName = readLine()!!
    if (isNullOrEmpty(userName)) { //FIX: userName.isNullOrEmpty()
        println("Hello, $userName")
    }
}
```
*Example taken from [stackoverflow.com](https://stackoverflow.com/questions/57196948/kotlin-symbol-is-declared-in-module-jdk-internal-opt-which-does-not-export-pa)*


# "I know what am doing"

You may still want to use internal API regardless of the recommendation from the compiler. Here are the solution for Java and Kotlin.

### Option 2: Java fix

In order to still use the internal API, you will need to pass additional arguments to the compiler (`--add-exports ...`). This can be configured in your Maven or Gradle build.

Here is an example of Maven configuration:
```xml
<build>
    <plugins>
        <plugin>
            <artifactId>maven-compiler-plugin</artifactId>
            <configuration>
                <source>1.9</source>
                <target>1.9</target>
                <compilerArgs>
                    <!-- Few examples ... only pick the ones needed -->
                    <arg>--add-exports</arg><arg>java.xml/com.sun.org.apache.xerces.internal.impl.dtd=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.xml/com.sun.org.apache.xerces.internal.xni.parser=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.net.www.protocol.http=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>jdk.unsupported/sun.misc=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.security.ssl=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.security.util=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/com.sun.net.ssl.internal.ssl=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.security.jca=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.net.util=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/jdk.internal.misc=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.security.internal.interfaces=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.security.provider.certpath=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.security.internal.spec=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.security.validator=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.security.action=ALL-UNNAMED</arg>
                    <arg>--add-exports</arg><arg>java.base/sun.security.x509=ALL-UNNAMED</arg>
                </compilerArgs>
            </configuration>
        </plugin>
```

### Option 3: Kotlin fix

Kotlin does not have a compiler option to turn off specific module restrictions. You can however add an annotation at the beginning of your file to disable module restriction completely.

```kotlin
@file:Suppress("JAVA_MODULE_DOES_NOT_EXPORT_PACKAGE")

import com.sun.org.apache.xerces.internal.util.SAXInputSource
import com.sun.org.apache.xerces.internal.xni.parser.XMLErrorHandler
import com.sun.org.apache.xerces.internal.xni.parser.XMLParseException
[...]
```

# Conclusion

While those new module restrictions may seems drastic, they are great safe guard that will help long term maintainability. Before adding exception, take the time to investigate if you really need the internal API.