Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InaccessibleObjectException with JDK 16 #1875

Closed
tomparle opened this issue Mar 19, 2021 · 13 comments · Fixed by #1902
Closed

InaccessibleObjectException with JDK 16 #1875

tomparle opened this issue Mar 19, 2021 · 13 comments · Fixed by #1902

Comments

@tomparle
Copy link

tomparle commented Mar 19, 2021

GSon (2.8.6) has trouble with JDK 16, seemingly due to https://bugs.openjdk.java.net/browse/JDK-8256358 and https://bugs.openjdk.java.net/browse/JDK-8260600 which prevent access through reflection to JDK internals.

This leads to these errors (for example when trying to serialize an empty list constructed with Collections.emptyList()) :

java.lang.reflect.InaccessibleObjectException: Unable to make private java.util.Collections$EmptyList() accessible: module java.base does not "opens java.util" to unnamed module @ffaa6af
	at com.google.gson.internal.reflect.UnsafeReflectionAccessor.makeAccessible(UnsafeReflectionAccessor.java:44)
	at com.google.gson.internal.ConstructorConstructor.newDefaultConstructor(ConstructorConstructor.java:103)
	at com.google.gson.internal.ConstructorConstructor.get(ConstructorConstructor.java:85)
	at com.google.gson.internal.bind.CollectionTypeAdapterFactory.create(CollectionTypeAdapterFactory.java:54)
	at com.google.gson.Gson.getDelegateAdapter(Gson.java:541)
	at io.gsonfire.gson.WrapTypeAdapterFactory.create(WrapTypeAdapterFactory.java:26)
	at com.google.gson.Gson.getAdapter(Gson.java:458)
	at com.google.gson.internal.bind.TypeAdapterRuntimeTypeWrapper.write(TypeAdapterRuntimeTypeWrapper.java:56)
	at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapterFactory.java:208)
	at com.google.gson.internal.bind.MapTypeAdapterFactory$Adapter.write(MapTypeAdapterFactory.java:145)
	at com.google.gson.Gson.toJson(Gson.java:704)
	at com.google.gson.Gson.toJson(Gson.java:683)
...

I would be happy to contribute but I do not even know how to fix this in Gson. Any ideas ?

@Marcono1234
Copy link
Collaborator

Marcono1234 commented Mar 21, 2021

The problem might be that the internal class ConstructorConstructor always first tries to get the constructor using reflection before using a default constructor for Collection subtypes.

Though I am not sure if this can easily be solved. Maybe the simplest solution which also keeps backward compatibility would be to change this line to handle the exception from setAccessible and return null as well:

} catch (NoSuchMethodException e) {
return null;
}

Though InaccessibleObjectException which is thrown there only exists since Java 9, but this project targets Java 6.
Therefore it might be necessary to catch RuntimeException (the subclass).

This is related to #1540, but not the same since here the issue is caused by Gson; Collections$EmptyList is the runtime type; the compile-time type is likely List<...> which Gson's built-in adapters would be able to handle, especially since ConstructorConstructor is only used for deserialization where only the compile-time type matters.

Note that I am not a maintainer of this project.

Edit: Having newDefaultConstructor return null if the constructor is inaccessible (as proposed above in this comment) would be a backward incompatible change and might introduce difficult to track down bugs for compile-time classes with an inaccessible constructor, because then Unsafe would be used to create the instance. Instead it would be better to create an ObjectCreator which always throws an exception, see #1902 for an implementation.

@roman-dzhadan
Copy link

@tomparle
One of many reasons, why Java introduced its modules was an attempt to encapsulate the internal JDK code and make sure that end-developers will not use it. It gives more flexibility for the Java language evolution, it allows to change the internals quickly and without obligation to support backward compatibility.

In the case of "gson" library, which uses reflection quite a lot. You still can use it with JDK16.
But you have to manually open and export the required modules with JAVA_OPTS arguments.

@tomparle
Copy link
Author

Thank you for all your answers.
I tried a few hours but eventually could not even build the gson project, alone trying any modification.
I tried with gradle (plugin maven id not found) and Maven (ConcurrentException in bnd-maven-plugin) as well.
Unless someone kindly helps me to build the project, I unfortunately will not be able to progress any more for now.

@tomparle
Copy link
Author

tomparle commented May 4, 2021

Update : I found it too hard to patch gson directly, so in a custom TypeAdapter I check if a type is not accessible (like the private class java.util.Collections$EmptyCollection), and use the corresponding accessible ArrayList instead. It works for now, so I consider it a good workaround for now.

@tomparle tomparle closed this as completed May 4, 2021
@tomparle tomparle reopened this May 4, 2021
@tomparle tomparle closed this as completed May 4, 2021
@Marcono1234
Copy link
Collaborator

Would you mind leaving the issue open anyways?
That way other others might find this issue more easily, and it indicates to the maintainers that this issue is still unresolved (even if it may have a workaround).

@tomparle
Copy link
Author

tomparle commented May 4, 2021

@Marcono1234 Sure, I wanted to mark it as "workaround available" but could not find how, other than closing it.
Reopened !

@masbaehr
Copy link

masbaehr commented Jun 9, 2021

image
Also having issues with JDK16 Cant use LinkdTreeMap anymore.
e.g to do the following:
LinkedTreeMap translationMapR = gson.fromJson(reader, LinkedTreeMap.class);

Downgrade to 2.8.5 helps

@Marcono1234
Copy link
Collaborator

@masbaehr, that is unrelated to this issue. As the package name suggests (com.google.gson.internal) LinkedTreeMap is an internal Map implementation of Gson. This issue has nothing to do with JDK 16; 2.8.6 added a module declaration which enforces that classes from the internal package cannot be used.
For your case should be able to simply use Map instead:

Map translationMapR = gson.fromJson(reader, Map.class);

(Also note that for better type safety should should probably use TypeToken.)

@Marcono1234
Copy link
Collaborator

@tomparle (and everyone else interested), to spare you the duplicate work, I have implemented a potential fix for this in #1902. I don't know when / if the maintainers will merge it, but feel free to try it out and let me know whether is solves this issue.
(The incompatibility mentioned in that pull request won't affect you if you are running with JDK 16 or if you explicitly using --illegal-access=deny.)

@tomparle
Copy link
Author

@Marcono1234 congratulations for this PR !
It seems to have been quite some work, and to handle errors better than before.
I'm still not sure about what would be ideal for my use case, other than mapping some specific types like java.util.Collections$EmptyCollection to their corresponding, constructible equivalent like ArrayList.
I'll consider this issue as a documentation for the workaround, as you suggested !

@Marcono1234
Copy link
Collaborator

Marcono1234 commented Jun 12, 2021

The change required to fix this was actually not that big, most of it are whitespace changes because I changed the scope of the try block. The other changes are mostly to get helpful exception messages and to add a useful test for this.

I'm still not sure about what would be ideal for my use case, other than mapping some specific types like java.util.Collections$EmptyCollection to their corresponding, constructible equivalent like ArrayList.

Just to make sure we are on the same page: The pull request should exactly handle your use case. Gson only uses the ConstructorConstructor during deserialization, however it creates it when the type adapter is request, even if only serialization is performed. In your use case the runtime type java.util.Collections.EmptyList will only ever be used using serialization, so you should never see the exception which the ObjectConstructor would throw, since you are likely either not performing any deserialization at all, or are using public types such as java.util.List as compile-time type (which Gson would then pick for deserialization).

@KENNYSOFT
Copy link

KENNYSOFT commented Jul 10, 2021

Workaround: add --add-opens java.base/java.util=ALL-UNNAMED to JVM.

Maybe java.base/java.io or something could help also.

@lvca
Copy link

lvca commented Nov 2, 2021

Adding --add-opens java.base/java.net=ALL-UNNAMED worked for me (the issue was with using Stripe libs that depend on GSON).

hanwen pushed a commit to GerritCodeReview/gerrit that referenced this issue Feb 8, 2022
The official release was not conducted yet, that fixed Java 17
compatibility issue with JEP 403: [1],[2].

[1] google/gson#1875
[2] Strongly Encapsulate JDK Internals https://openjdk.java.net/jeps/403
Change-Id: I2802513866ec3f36be0895a174fc5a22d8b03c3a
patchwork01 added a commit to gchq/sleeper that referenced this issue Aug 10, 2022
Tests like WebSocketResultsOutputIT were failing due to this issue:
google/gson#1875
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants