15

I am trying to upgrade my code base to Java 11. Unfortunately, my code has a dependency on a third-party library that internally uses sun.misc.BASE64Encoder and Decoder. Since the sun.misc package has been removed from the Java 11 JRE it is failing. The owner of that library has not replaced that dependency as yet, so I am stuck with it for a while.

If I had control of the code I would use java.util.BASE64 classes, but as I said these are coming in as a transitive dependency from another library, and I cannot change that.

I thought I would be clever and create a new jar with just those classes, but for some reason, that jar is being ignored.

enter image description here

<dependency>
    <groupId>sun.misc</groupId>
    <artifactId>BASE64</artifactId>
    <version>1.8</version>
    <scope>system</scope>
    <systemPath>${project.basedir}/lib/sun.jar</systemPath>
</dependency>

I also tried adding it to the classpath explicitly but still no luck

Is this one of those packages that the JRE prevents you from playing with, or am I missing some module specification, or is this a show stopper?

Here is the output

java.lang.NoClassDefFoundError: sun/misc/BASE64Encoder
at com.propsco.util.support.PropsLoader.save(PropsLoader.java:478) ~[props-client-2.2.1.jar:na]
Vinny Gray
  • 477
  • 5
  • 18
  • Please explain *"but for some reason, that jar is being ignored."* Is your application a modular application and what error with `BASE64Encoder/Decoder` are you facing? – Naman May 31 '19 at 02:30
  • Can you be a bit more specific? Do you see NoClassDefFoundError in your logs? Can you attach the stack trace to the question? – ZhekaKozlov May 31 '19 at 04:56
  • Yes @ZhekaKozlov, I added the output – Vinny Gray May 31 '19 at 13:05
  • 1
    Have you tried to add it in system libs? which are in "JAVA_HOME/jmods" now. – pdem May 31 '19 at 14:59
  • s.misc was just intellij abbreviating. I updated the image to show the whole picture – Vinny Gray May 31 '19 at 15:21
  • 3
    Ok it might be related to the noew option `--patch-module`see https://stackoverflow.com/questions/48148607/how-to-add-a-jar-to-the-boot-classpath-in-java-9 sun/misc is in the jdk.unsupported.jmod module. The argument on cli may be something like `--patch-module modulename=yourmodule.jmod` – pdem May 31 '19 at 15:54

2 Answers2

12

This answer is written using

> java --version
openjdk 11.0.3 2019-04-16
OpenJDK Runtime Environment AdoptOpenJDK (build 11.0.3+7)
OpenJDK 64-Bit Server VM AdoptOpenJDK (build 11.0.3+7, mixed mode)

First, you create the project that will have the overriden BASE64Encoder. Let's call it sun-misc-override. Under src\main\java create the sun.misc package with BASE64Encoder class.

package sun.misc;

public class BASE64Encoder {
    public String encode(byte[] aBuffer) {
        return "Fake it until you make it!";
    }
}

If you try to compile it, you'll get a sun\misc\BASE64Encoder.java:1: error: package exists in another module: jdk.unsupported error.

That gives us a hint that we need to patch module jdk.unsupported. This is because classes from the original sun.misc package have been moved to the jdk.unsupported module when the module system was rolled out in Java 9 and then over time removed (see JEP-260).

With Maven you can configure your compiler plugin like this:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <release>11</release>
        <compilerArgs>
            <arg>--patch-module</arg>
            <arg>jdk.unsupported=${project.basedir}/src/main/java</arg>
        </compilerArgs>
    </configuration>
</plugin>

After com.example:sun-misc-override:1.0.0-SNAPSHOT is built, place the resulting JAR in your "main" project - like you did eg. in a lib directory. I haven't found a way to make it work with a regular Maven dependency.

Now, configure the compiler plugin in your "main" project:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.8.1</version>
    <configuration>
        <release>11</release>
        <compilerArgs>
            <arg>--patch-module=jdk.unsupported=${project.basedir}/lib/sun-misc-override-1.0.0-SNAPSHOT.jar</arg>
        </compilerArgs>
    </configuration>
</plugin>

(Probably due to MCOMPILER-311 I was getting a NPE when I tried to use

<compilerArgs>
    <arg>--patch-module</arg>
    <arg>jdk.unsupported=${project.basedir}/lib/sun-misc-override-1.0.0-SNAPSHOT.jar</arg>
</compilerArgs>

even though the bug was supposed to be fixed with maven-compiler-plugin 3.8.0 and it worked fine in the POM of sun-misc-override.)

Now my "main" project is called j11 and has one class:

package com.example;

import sun.misc.BASE64Encoder;

public class BASE64EncoderTest {
    public static void main(String[] args) {
        System.out.println("Testing - " + new BASE64Encoder().encode(new byte[0]));
    }
}

To run it you need to specify --patch-module again:

> java --patch-module=jdk.unsupported=lib\sun-misc-override-1.0.0-SNAPSHOT.jar -cp target\j11-1.0.0-SNAPSHOT.jar com.example.BASE64EncoderTest
Testing - Fake it until you make it!
Adam Michalik
  • 9,678
  • 13
  • 71
  • 102
3

In past versions, Sun went to great lengths to ensure that there was no way to tamper with the runtime the way you are trying now (had it been as easy as this, everyone could have created their own private variant of the runtime - and surely you'll understand that wouldn't have been a good thing). I don't know the details but they probably boiled down to "if package name is this or that or such or so, then loading will only happen from rt.jar" - hardcoded in the classloaders stuff. It seems very likely those enforcement measures (or similar) are still in effect.

Scrapped the rest of my answer because I think you understand very well what your options are and that wasn't the question.

Erwin Smout
  • 18,113
  • 4
  • 33
  • 52
  • 1
    In older Java versions you can replace `rt.jar` if you change the boot classpath (I think the flag is `-XX:bootclasspath`?). But I don't know how that interacts with the new module system in Java 9+. – Daniel Pryden May 31 '19 at 13:42
  • @Erwin Smout I suspect this is the problem. – Vinny Gray May 31 '19 at 15:46