Repository: jdbi/jdbi
Issue: JDK9 will block setAccessible on modules

We're using `setAccessible(true)` to get reflective access to private API in a couple places. We cannot count on this after JDK9 is released.

In particular, `DefaultMethodHandler` in SQL Object is using `setAccessible` to get access to the `MethodHandle.Lookup` for private members, so that we can invoke the default ("super") method.

See:
- http://jigsaw-dev.1059479.n5.nabble.com/creating-proxies-for-interfaces-with-default-methods-td5711955.html
- http://mlvm-dev.openjdk.java.narkive.com/ocxrCtZV/proxy-an-interface-and-call-a-default-method


Changed files:
sqlobject/src/main/java/org/jdbi/v3/sqlobject/DefaultMethodHandler.java

diff --git a/sqlobject/src/main/java/org/jdbi/v3/sqlobject/DefaultMethodHandler.java b/sqlobject/src/main/java/org/jdbi/v3/sqlobject/DefaultMethodHandler.java
index 9a0db8f47..4727a6a2d 100644
--- a/sqlobject/src/main/java/org/jdbi/v3/sqlobject/DefaultMethodHandler.java
+++ b/sqlobject/src/main/java/org/jdbi/v3/sqlobject/DefaultMethodHandler.java
@@ -18,6 +18,7 @@ import static java.util.Collections.synchronizedMap;
 import java.lang.invoke.MethodHandle;
 import java.lang.invoke.MethodHandles;
 import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
 import java.util.Map;
 import java.util.WeakHashMap;
@@ -25,22 +26,47 @@ import java.util.WeakHashMap;
 import org.jdbi.v3.core.extension.HandleSupplier;
 
 class DefaultMethodHandler implements Handler {
+    // MethodHandles.privateLookupIn(Class, Lookup) was added in JDK 9.
+    // JDK 9 allows us to unreflectSpecial() on an interface default method, where JDK 8 did not.
+    private static final Method PRIVATE_LOOKUP_IN = privateLookupIn();
+
+    private static Method privateLookupIn() {
+        try {
+            return MethodHandles.class.getMethod("privateLookupIn", Class.class, MethodHandles.Lookup.class);
+        } catch (NoSuchMethodException e) {
+            // Method was added in JDK 9
+            return null;
+        }
+    }
+
     private static final Map<Class<?>, MethodHandles.Lookup> privateLookups = synchronizedMap(new WeakHashMap<>());
 
     private static MethodHandles.Lookup lookupFor(Class<?> clazz) {
-        return privateLookups.computeIfAbsent(clazz, type -> {
+        if (PRIVATE_LOOKUP_IN != null) {
             try {
-                // TERRIBLE, HORRIBLE, NO GOOD, VERY BAD HACK
-                // Courtesy of:
-                // https://rmannibucau.wordpress.com/2014/03/27/java-8-default-interface-methods-and-jdk-dynamic-proxies/
+                return (MethodHandles.Lookup) PRIVATE_LOOKUP_IN.invoke(null, clazz, MethodHandles.lookup());
+            } catch (IllegalAccessException | InvocationTargetException e) {
+                String message = String.format(
+                        "Error invoking MethodHandles.privateLookupIn(%s.class, MethodHandles.lookup()) in JDK 9+ runtime",
+                        clazz);
+                throw new RuntimeException(message, e);
+            }
+        }
+
+        // TERRIBLE, HORRIBLE, NO GOOD, VERY BAD HACK
+        // Courtesy of:
+        // https://rmannibucau.wordpress.com/2014/03/27/java-8-default-interface-methods-and-jdk-dynamic-proxies/
 
-                // We can use MethodHandles to look up and invoke the super method, but since this class is not an
-                // implementation of method.getDeclaringClass(), MethodHandles.Lookup will throw an exception since
-                // this class doesn't have access to the super method, according to Java's access rules. This horrible,
-                // awful workaround allows us to directly invoke MethodHandles.Lookup's private constructor, bypassing
-                // the usual access checks.
+        // We can use MethodHandles to look up and invoke the super method, but since this class is not an
+        // implementation of method.getDeclaringClass(), MethodHandles.Lookup will throw an exception since
+        // this class doesn't have access to the super method, according to Java's access rules. This horrible,
+        // awful workaround allows us to directly invoke MethodHandles.Lookup's private constructor, bypassing
+        // the usual access checks.
 
-                // We should get rid of this workaround as soon as a viable alternative exists.
+        // This workaround is only used in JDK 8.x runtimes. JDK 9+ runtimes use MethodHandles.privateLookupIn()
+        // above.
+        return privateLookups.computeIfAbsent(clazz, type -> {
+            try {
 
                 final Constructor<MethodHandles.Lookup> constructor =
                         MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);