easymock/objenesis

Serialization object construction doesn't work on JDK9 anymore

Closed this issue · 14 comments

There was an incompatible change introduced in method
sun.reflect.ReflectionFactory.newConstructorForSerialization()
in recent JDK9 b142 build.

I noticed that indeed UnsafeFactoryInstantiator and Objenesis in general are working for standard instantiation. However, serialization isn't working anymore since ObjectStreamClassInstantiator isn't working anymore. Let's fix that.

@henri-tremblay have you tested SunReflectionFactorySerializationInstantiator with JDK9? I was under the impression that calling setAccessible(true) fails on Java 9. See redis/lettuce@a3c36bc .

I did. And it was working. You have a different experience? Do you have a test case?

@mp911de I'm having trouble getting my hands on the latest EA actually..

jdk9

@mp911de could I trouble you for a bit more information about the commit I referenced above?

@johnou sure (mail, gitter, Twitter, …). setAccessible(true) fails for encapsulated elements. That's the Java 8 code path. A different path

MethodHandles.lookup().findSpecial(method.getDeclaringClass(), method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()), method.getDeclaringClass());

works for Java 9 (but not Java 8 and earlier). It's however possible this might change in future Java 9 versions because "the team is fixing loopholes until the Java 9 release" (statement from an Oracle PM I met recently).

@mp911de that's perfect, just wanted to give @henri-tremblay a heads up.

Shoot! The code from above works inside of unnamed modules but not if you create a real Java 9 module and import the library that's going to invoke a default method on an interface. I found an ugly hack via a derived class. It boils down back to where we started: there's no official way to deal with Methodhandles of encapsulated components.

What do you mean by "encapsulated components"? And then it's getting annoying to test... I fell like I should just use the UnsafeInstantiator and it will be then end of it.

I'm still new to the Java 9 terminology so sorry if I don't use the right terms, still learning. I mean by that a Java 9 module with a module descriptor.

Thanks... I'm not that good with the terminology either.

So can you please confirm the current state:
1- I'm in module A which is importing module B
2- I want to create an instance of a class in module B
3- setAccessible fails

Is that it?

setAccessible fails if the source module does not export/opens the package to the framework library.

setAccessible(true) on a module's member fails when called in a framework if:

module com.my.module {

}

setAccessible(true) on a module's member works when called in a framework if:

module com.my.module {

	opens com.my.package to org.framework.module;
	// or 
	//exports com.my.package to org.framework.module;
}

I received, however, a response to private MethodHandle lookup yesterday, see http://mail.openjdk.java.net/pipermail/core-libs-dev/2017-March/046567.html

Oh dear Lord... First time I see "open". So, again, to make sure I get it. Let's say I have:

  • com.mycompany module. It imports org.springframework
  • org.springframework module that is calling Objenesis to create a proxy. It imports org.objenesis
  • org.objenesis module

And then org.springframework tries to create a proxy using org.objenesis to create an instance of a class from com.mycompany.

This will fail unless com.mycompany is defined like this:

module com.my.module {
	opens com.mycompany to org.objenesis;
}

Is that right?

I can confirm that's what you need to do for plain old jars. I'm not sure whether that's still the case when each module comes with a module descriptor and declares its dependencies. I attached a screenshot that shows a Maven and a module-info.java to make a plain old jar working with a Java 9 module. /cc @nicolaiparlog

screenshot 2017-03-01 20 38 28