Jdbi: JDK9 will block setAccessible on modules

Created on 21 Sep 2016  ·  22Comments  ·  Source: jdbi/jdbi

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:

bug

Most helpful comment

This should be supported in the next Jdbi release--probably v3.2.0

All 22 comments

I've tested the latest JDBI snapshot on JDK 9-ea+136 and, unfortunately, default methods don't work.

java.lang.RuntimeException: java.lang.IllegalAccessException: access to public member failed: org.jdbi.v3.sqlobject.TestSqlObject$Dao.doesTransactionAnnotationWork()boolean/invokeSpecial, from org.jdbi.v3.sqlobject.TestSqlObject$Dao/2 (unnamed module @34b7bfc0)

    at org.jdbi.v3.sqlobject.DefaultMethodHandler.invoke(DefaultMethodHandler.java:63)
    at org.jdbi.v3.sqlobject.TransactionDecorator.lambda$invoke$0(TransactionDecorator.java:54)
    at org.jdbi.v3.core.transaction.LocalTransactionHandler.inTransaction(LocalTransactionHandler.java:173)
    at org.jdbi.v3.core.Handle.inTransaction(Handle.java:478)
    at org.jdbi.v3.sqlobject.TransactionDecorator.invoke(TransactionDecorator.java:57)
    at org.jdbi.v3.sqlobject.SqlObjectFactory.lambda$createInvocationHandler$18(SqlObjectFactory.java:241)
    at com.sun.proxy.$Proxy16.doesTransactionAnnotationWork(Unknown Source)
    at org.jdbi.v3.sqlobject.TestSqlObject.testTransactionAnnotationWorksOnInterfaceDefaultMethod(TestSqlObject.java:115)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@9-ea/Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@9-ea/NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@9-ea/DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(java.base@9-ea/Method.java:535)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.junit.rules.ExternalResource$1.evaluate(ExternalResource.java:48)
    at org.junit.rules.ExpectedException$ExpectedExceptionStatement.evaluate(ExpectedException.java:239)
    at org.junit.rules.RunRules.evaluate(RunRules.java:20)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:117)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:42)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:262)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:84)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(java.base@9-ea/Native Method)
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(java.base@9-ea/NativeMethodAccessorImpl.java:62)
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(java.base@9-ea/DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(java.base@9-ea/Method.java:535)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147)
Caused by: java.lang.IllegalAccessException: access to public member failed: org.jdbi.v3.sqlobject.TestSqlObject$Dao.doesTransactionAnnotationWork()boolean/invokeSpecial, from org.jdbi.v3.sqlobject.TestSqlObject$Dao/2 (unnamed module @34b7bfc0)
    at java.lang.invoke.MemberName.makeAccessException(java.base@9-ea/MemberName.java:915)
    at java.lang.invoke.MethodHandles$Lookup.checkAccess(java.base@9-ea/MethodHandles.java:1924)
    at java.lang.invoke.MethodHandles$Lookup.checkMethod(java.base@9-ea/MethodHandles.java:1864)
    at java.lang.invoke.MethodHandles$Lookup.getDirectMethodCommon(java.base@9-ea/MethodHandles.java:2013)
    at java.lang.invoke.MethodHandles$Lookup.getDirectMethodNoSecurityManager(java.base@9-ea/MethodHandles.java:2007)
    at java.lang.invoke.MethodHandles$Lookup.unreflectSpecial(java.base@9-ea/MethodHandles.java:1539)
    at org.jdbi.v3.sqlobject.DefaultMethodHandler.invoke(DefaultMethodHandler.java:51)
    ... 38 more

Sadly, I also don't see a workaround for this.

That's kind of strange all people in the Internet who experience the same problem, end up with this hack as the only solution. It looks like it was a little bit oversight from the JDK team to not pay attention to the "default methods in proxies" use case.

Hm, got it working with:

Field field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
field.setAccessible(true);
MethodHandles.Lookup lookup = (MethodHandles.Lookup) field.get(null);
Class<?> declaringClass = method.getDeclaringClass();
return lookup.unreflectSpecial(method, declaringClass)
                    .bindTo(target)
                    .invokeWithArguments(args);

No idea why this works with JDK9.

Sigh. We should probably follow up with the openjdk devs about this sooner than later...

I've been watching jigsaw-dev for the past several days. There's been some discussion on this exact use case, but from what I can tell it's been more of a footnote relative to a raging discussion on the value of restricting setAccessible. Seems like a lot of people are not happy about that move.

I am removing this from release blockers. An upstream fix doesn't seem likely to appear soon, and JDK9 isn't released yet. The eventual fix should not affect public API.

I contacted the jigsaw-dev mailing list about this. http://jigsaw-dev.1059479.n5.nabble.com/Invoking-default-methods-from-a-Proxy-s-InvocationHandler-in-JDK9-td5714878.html.

We'll see what comes out of it.

Bumped it on core-libs-dev again.

This should be resolved for us in recent JDK9 builds. At least in theory.

Basically, they changed the security checks so that anyone can
unreflectSpecial on a default method.

When JDK9 lands, we'll have to dynamically switch approaches depending on
the Java version JDBI is running in.

On Mar 1, 2017 2:36 PM, "Steven Schlansker" notifications@github.com
wrote:

Bumped it on core-libs-dev again.


You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/jdbi/jdbi/issues/497#issuecomment-283478025, or mute
the thread
https://github.com/notifications/unsubscribe-auth/AACW5VBktrJYJatnsABFKay_-Ci5WD_Sks5rheTCgaJpZM4KCb6m
.

Ah, yeah. From core-libs-dev:

You can use MethodHandles.privateLookupIn (new in JDK 9) to replace Constructor hack to get a Lookup object with private access for the target class.
Mandy

That's refreshing to hear! At least we can get rid of this mess in JDK9.

Hi I have solved a similar problem on my project OWNER that calls default methods on interfaces using JDK9, please have a look at my utility class maybe it can help you.

Any word on this? When will I be able to use JDBI with JDK 9?

We're very interested in Java 9 support but frankly I haven't started looking into it yet. Last I looked the ecosystem clearly still needed time to settle, but maybe it is time to bump the priority of this.

Anything new here? JDK 10 ist out since a few days...

There is no obvious change in jdk9, it is better to go straight to 10

It sounds like we may have to branch the code and use reflection to take advantage of the new methods added in JDK9 without breaking compatibility for 8. The quickest way forward here is to submit a patch or at least some legwork towards figuring out the minimum change necessary :) otherwise I'll try to take a look at this sometime this week.

And hopefully the majority of the work to get to 10 will actually be in getting to 9 -- I don't think there's any big changes in 10 we expect to break things, whereas we certainly know 9 broke things...

I have a branch on this that doesn't need a dual build, but it needs a fix first

OK I won't start hacking on it then.

This should be supported in the next Jdbi release--probably v3.2.0

Was this page helpful?
0 / 5 - 0 ratings

Related issues

nonameplum picture nonameplum  ·  5Comments

electrum picture electrum  ·  3Comments

jimmyhmiller picture jimmyhmiller  ·  6Comments

anjeyy picture anjeyy  ·  3Comments

rherrmann picture rherrmann  ·  4Comments