module java.base does not "opens java.lang" to unnamed module

84 views
Skip to first unread message

tim_mac...@yahoo.co.uk

unread,
Jun 21, 2024, 9:45:59 AM (8 days ago) Jun 21
to GWT Users
SInce upgrading to Java 11,  throwing a new IllegalArgumentException in an RPC server-side implementation gives  InaccessibleObjectException. It can be stopped by using a VM argument --add-opens, but apparently this is not recommended.
Can anyone  clarify ?

Colin Alworth

unread,
Jun 21, 2024, 11:20:04 AM (8 days ago) Jun 21
to GWT Users
Can you share a little more detail, like the full error message with stack trace, and the GWT version you're using? Some improvements were made in this area for GWT 2.11, and some messages of this kind are merely warnings, indicating that reflection was attempted and some fallback can usually be used instead.

Tim Macpherson

unread,
Jun 21, 2024, 1:34:59 PM (8 days ago) Jun 21
to google-we...@googlegroups.com
It happens with the latest tbroyer archetype, just change the server method so it always throws 


--
You received this message because you are subscribed to the Google Groups "GWT Users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-tool...@googlegroups.com.
To view this discussion on the web visit
https://groups.google.com/d/msgid/google-web-toolkit/6c4d8967-066c-4f36-a95f-9bf8df2e2589n%40googlegroups.com
.

Craig Mitchell

unread,
Jun 21, 2024, 9:53:38 PM (8 days ago) Jun 21
to GWT Users
This is because, from Java 9, access to the Java classes has been restricted.  More info:  https://stackoverflow.com/questions/41265266/how-to-solve-inaccessibleobjectexception-unable-to-make-member-accessible-m

In the case of GWT, if you want exceptions passed back via RPC, GWT needs access to the java.lang classes to serialise them to contain all the Java class details.

So, you need to add:  --add-opens java.base/java.lang=ALL-UNNAMED  when running.

I didn't know it was not recommended.  Why is it bad to do?

Tim Macpherson

unread,
Jun 22, 2024, 5:50:43 AM (7 days ago) Jun 22
to google-we...@googlegroups.com
This is with gwt 2.11.0 
On app engine there's no obvious problem.
With local jetty the onFailure method is never reached so the message is not displayed.

console output:

[WARNING] Exception while dispatching incoming RPC call
java.lang.reflect.InaccessibleObjectException: Unable to make field private java.lang.String java.lang.Throwable.detailMessage accessible: module java.base does not "opens java.lang" to unnamed module @5e4b956
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:178)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:172)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeClass(ServerSerializationStreamWriter.java:781)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeImpl(ServerSerializationStreamWriter.java:826)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeClass(ServerSerializationStreamWriter.java:799)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeImpl(ServerSerializationStreamWriter.java:826)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeClass(ServerSerializationStreamWriter.java:799)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeImpl(ServerSerializationStreamWriter.java:826)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeClass(ServerSerializationStreamWriter.java:799)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeImpl(ServerSerializationStreamWriter.java:826)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serialize(ServerSerializationStreamWriter.java:698)
at com.google.gwt.user.client.rpc.impl.AbstractSerializationStreamWriter.writeObject(AbstractSerializationStreamWriter.java:130)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter$ValueWriter$8.write(ServerSerializationStreamWriter.java:167)
at com.google.gwt.user.server.rpc.impl.ServerSerializationStreamWriter.serializeValue(ServerSerializationStreamWriter.java:606)
at com.google.gwt.user.server.rpc.jakarta.RPC.encodeResponse(RPC.java:645)
at com.google.gwt.user.server.rpc.jakarta.RPC.encodeResponseForFailure(RPC.java:420)
at com.google.gwt.user.server.rpc.jakarta.RPC.invokeAndEncodeResponse(RPC.java:606)
at com.google.gwt.user.server.rpc.jakarta.RemoteServiceServlet.processCall(RemoteServiceServlet.java:350)
at com.google.gwt.user.server.rpc.jakarta.RemoteServiceServlet.processCall(RemoteServiceServlet.java:320)
at com.google.gwt.user.server.rpc.jakarta.RemoteServiceServlet.processPost(RemoteServiceServlet.java:390)
at com.google.gwt.user.server.rpc.jakarta.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:63)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:520)
at jakarta.servlet.http.HttpServlet.service(HttpServlet.java:587)
at org.eclipse.jetty.servlet.ServletHolder$NotAsync.service(ServletHolder.java:1419)
at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:764)
at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1665)
at com.historytrees.MyJakartaFilter.doFilter(MyJakartaFilter.java:37)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:210)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
at org.eclipse.jetty.websocket.servlet.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:170)
at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:202)
at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1635)
at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:527)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:131)
at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:598)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:223)
at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1580)
at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:221)
at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1381)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:176)
at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:484)
at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1553)
at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:174)
at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1303)
at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:129)
at org.eclipse.jetty.server.handler.ContextHandlerCollection.handle(ContextHandlerCollection.java:149)
at org.eclipse.jetty.server.handler.HandlerList.handle(HandlerList.java:51)
at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:122)
at org.eclipse.jetty.server.Server.handle(Server.java:563)
at org.eclipse.jetty.server.HttpChannel$RequestDispatchable.dispatch(HttpChannel.java:1598)
at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:753)
at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:501)
at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:287)
at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:314)
at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:100)
at org.eclipse.jetty.io.SelectableChannelEndPoint$1.run(SelectableChannelEndPoint.java:53)
at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:969)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.doRunJob(QueuedThreadPool.java:1194)
at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1149)
at java.base/java.lang.Thread.run(Thread.java:842)



Tim Macpherson

unread,
Jun 22, 2024, 6:07:19 AM (7 days ago) Jun 22
to google-we...@googlegroups.com

touches on the issue:
Using --add-opens should be considered a workaround.
The right thing is for libraries performing illegal access to fix their issues.

reflection  removes encapsulation: makes every element in a code unit part of its API
Strong encapsulation aids in maintainability and security.


The result is a user class that is tightly coupled to the internal implementation of the JDK.
If enough developers abuse this openness, this leads to a situation in which it is difficult or impossible 
to make changes to the internals, because to do so would break deployed libraries and applications.
This is one of the problems that modules were invented to solve.


I have found:

On app engine there's no need for --add-opens java.base/java.lang=ALL-UNNAMED, only on local jetty.
This java fix for LinkedHashMap reflection failure is probably more worrying:
...
Field field = LinkedHashMap_CustomFieldSerializer.class.getDeclaredField("reflectionHasFailed");
field.setAccessible(true);
...



Colin Alworth

unread,
Jun 22, 2024, 2:48:59 PM (7 days ago) Jun 22
to GWT Users
GWT-RPC's purpose is to enable serialization of objects to and from the server. Usually, those types either are written by the application authors, or have well supported interfaces to interact with them, but there are exceptions (Throwable, as you first noted, and LinkedHashMap has one of the other main ones I can think of).

As we know it today, GWT-RPC was written for around Java 1.5 - reflection was not desirable even then because it was slow, but JPMS was many years away from being considered.

For Throwable, we serialize this in two main cases: sending logs to the server so client errors can be tracked, solved, and sending errors to the client so they know an operation has failed in some way. So, we need to both read and write the `detailMessage` field of throwable - and there is no guaranteed way to either read _or_ write this without reflection. Reading example: a custom exception type could override getMessage() and make use of super.getMessage(), so the private field is needed but cannot be read directly. Writing example: a custom exception type could exist that calls super(String) but doesn't pass in a constructor argument directly, so the detailMessage field can't be reliably written.

If this is unacceptable, the best option would be to define a CustomFieldSerializer for Throwable and supported subtypes that have their own fields, and deal with the exceptions described above as you see fit, by deciding how to handle the loss of data in those cases. GWT doesn't make this decision for you.

For LinkedHashMap the `accessOrder` constructor argument is effectively hidden state - there is no nice way to interogate a LinkedHashMap to ask if it is based on insertion or access order directly. There is a not-very-nice way though, and this is what GWT does if reflection is not available (clone the collection, clear the clone, insert two keys, access the second, iterate to see which was first). This is why I was asking about GWT 2.11, which has this a fix to better handle Java 17 (see https://github.com/gwtproject/gwt/pull/9791):

You should not need to apply a workaround via more reflection - this will fail the first time it is attempted, and track that for future reference, so the error will only happen once.

For both of these cases, we have to decide what it means to not have access to this data, and how likely these internals are to change. Is Throwable going to lose its detailMessage? Is LinkedHashMap going to change how accessOrder works? Especially for just two cases, the answer is very likely no, and we run CI with GWT (including tests for serialization for these types) against so-called LTS JDKs and the current latest version, so that if they do change, we know about it.

Tim Macpherson

unread,
Jun 23, 2024, 12:23:33 PM (6 days ago) Jun 23
to google-we...@googlegroups.com
Thanks Colin, so this is an issue that is ongoing.  Still wondering how app engine deployment get around it.
On Sat, Jun 22, 2024 at 7:49 PM, Colin Alworth

Craig Mitchell

unread,
Jun 23, 2024, 9:50:44 PM (6 days ago) Jun 23
to GWT Users
My Google App Engine didn't get around it.  Well, I don't think it did.  In my app.yaml I have the add-opens:

runtime: java17
instance_class: F1
entrypoint: java --add-opens java.base/java.lang=ALL-UNNAMED -Xms150m -Xmx350m -jar drift-team.war

Tim Macpherson

unread,
Jun 24, 2024, 6:48:25 AM (5 days ago) Jun 24
to google-we...@googlegroups.com
Apparently: when running in a sandboxed environment on the cloud there is a controlled module structure which ensures proper access permissions. This typically prevents the need for reflection to access private fields.

This seems to be the case for throwing IllegalArgumentException on App Engine: no --add-opens required, but with Spring the situation may be different. I haven't tested illegal access for LinkedHashMap_CustomFieldSerializer yet.



Reply all
Reply to author
Forward
0 new messages