JDK-8177566 : FX user module gets IllegalAccessException from sun.reflect.misc.Trampoline
  • Type: Bug
  • Component: javafx
  • Sub-Component: base
  • Affected Version: 9
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • Submitted: 2017-03-24
  • Updated: 2018-01-19
  • Resolved: 2017-05-04
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 9
9Fixed
Related Reports
Blocks :  
Relates :  
Description
See http://mail.openjdk.java.net/pipermail/jigsaw-dev/2017-March/011871.html

Caused by: java.lang.IllegalAccessException: class
sun.reflect.misc.Trampoline cannot access class
com.mechanitis.demo.sense.client.user.TwitterUser (in module
com.mechanitis.demo.sense.client) because module
com.mechanitis.demo.sense.client does not export
com.mechanitis.demo.sense.client.user to unnamed module @779d6cc6
at
java.base/jdk.internal.reflect.Reflection.throwIllegalAccessException(Reflection.java:423)
at
java.base/jdk.internal.reflect.Reflection.throwIllegalAccessException(Reflection.java:414)
at
java.base/jdk.internal.reflect.Reflection.ensureMemberAccess(Reflection.java:112)
at
java.base/java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:632)
at
java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:624)
at java.base/java.lang.reflect.Method.invoke(Method.java:539)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:72)
at jdk.internal.reflect.GeneratedMethodAccessor2.invoke(Unknown Source)
at
java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:547)
at java.base/sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:261)
at
javafx.base/com.sun.javafx.property.PropertyReference.getProperty(PropertyReference.java:198)
... 48 more
Comments
This fix should be in jdk-9+169
04-05-2017

Changeset: 7237e032862b Author: kcr Date: 2017-05-04 08:02 -0700 URL: http://hg.openjdk.java.net/openjfx/9-dev/rt/rev/7237e032862b 8177566: FX user module gets IllegalAccessException from sun.reflect.misc.Trampoline Reviewed-by: mchung, ddhill, jgiles
04-05-2017

Fix request approved Approved to push to FX 9-dev for JDK 9.
04-05-2017

For completeness, here are the delta changes (comment changes only) from .01 webrev that I will include when I push: http://cr.openjdk.java.net/~kcr/8177566/webrev.02/delta-webrev.01/
04-05-2017

+1
04-05-2017

Fix Request I request to get this fix in JDK 9. This bug affects FX applications deployed in a module. This bug will be a barrier for applications to convert to deploying in a named module. Currently the error message that is produced is such that an application cannot even figure out what is wrong or how to fix it. Additionally, the restriction that it places on such applications even if they were to be able to figure out what to do is not acceptable (see above comments in this JBS issue). This is a low risk fix, since the code changes (aside from simple refactoring) are very localized, and will only affects applications that are in a named module. There are 30 new unit tests to fully test all of the cases. The majority of the changes in the webrev are for the new unit tests or the API docs. The webrev is here: http://cr.openjdk.java.net/~kcr/8177566/webrev.01/complete-webrev/
04-05-2017

A good point about adding a comment to buildSrc/addExports. I will add one.
03-05-2017

Only one minor comment: did you want to add a comment to buildSrc/addExports about why these two exports were added? I think we'll eventually have a lot of items in here and it will be difficult to recall why these are required, and if they are still required at any point in time. If you feel this should be added, a webrev respin isn't necessary for me to already +1 this regardless.
03-05-2017

Here is the updated webrev with (I hope) all comments addressed: http://cr.openjdk.java.net/~kcr/8177566/webrev.01/complete-webrev/ For those who reviewed the earlier webrev, I have prepared delta webrevs. * Delta webrev for the fix itself (just a slight change to the error message, plus I hid the unused public methods in MethodUtil) : http://cr.openjdk.java.net/~kcr/8177566/webrev.01/delta-fix-only.00/ * No changes in the tests * Delta webrev for the doc changes: http://cr.openjdk.java.net/~kcr/8177566/webrev.01/delta-doc-only.00/ * The sparse javadocs are also updated here: http://cr.openjdk.java.net/~kcr/8177566/webrev.01/javadoc/
03-05-2017

reviewed basic changes and tests. +1 suggested a wording change for the javadoc, don't care either way.
02-05-2017

OK, here is the review: Webrev: http://cr.openjdk.java.net/~kcr/8177566/webrev.00/complete-webrev/ If you want to review the entire webrev, or download and run the patch, use the above link. This is a fairly large webrev, so in order to make it easier for reviewers, I have also created separate webrevs for the fix itself, the doc changes, and the unit tests (the majority of the new files are the tests). The separate webrevs are named fix-only, doc-only, and test-only, respectively, and you can find them here: http://cr.openjdk.java.net/~kcr/8177566/webrev.00/ Whether you choose to review it as one big webrev or three somewhat more manageable ones, here are some (I hope) helpful hints to guide the review: 1. Reviewing the fix http://cr.openjdk.java.net/~kcr/8177566/webrev.00/fix-only/ The primary changes to the code are: * The sun/reflect/misc/MethodUtil.java class is copied from the java.base module to a new package in the javafx.base module. The only change (other than those that are due to the move iteself to a new module and package) is the addition of a single new method to return the "unnamed" module of the Trampoline class. You can see the diffs relative to the original sun/reflect/misc/ModuleUtil.java file here: http://cr.openjdk.java.net/~kcr/8177566/webrev.00/fix-only/modules/javafx.base/src/main/java/com/sun/javafx/reflect/MethodUtil.java.udiff.html * A new MethodHelper class was created in each of the javafx.base, javafx.fxml, and javafx.web module. The copy in each module is identical except for the package name. This helper class has an invoke() method that wraps the one in MethodUtil. It checks to see whether the declaring class of the method being invoked: - is exported unconditionally -- in this case it just calls MethodUtil.invoke - is is open to the javafx.* module of the MethodHelper class -- in this case it does a package access check, opens the module being called to the unnamed Trampoline module, and calls MethodUtil.invoke - otherwise, a suitable error message is thrown indicating that the class is not open to the javafx.* module. * All existing calls to MethodUtil.invoke are refactored to MethodHelper.invoke There were two other minor changes needed, one in controls and one in FXML, relating to exception handling: * FXMLLoader was swallowing IllegalAccessException when calling the initialize method of an FXML controller reflectively. The reason for doing this no longer exists, so I restored the commented-out code that rethrows the exception to provide an earlier and more accurate indication of the failure to the application. * PropertyValueFactory and TreeItemPropertyValueFactory did not catch the expection nor log it properly, causing the exception to be thrown back to the application, which was unintended. ----------------------------------------------------------------------------------- 2. Reviewing the docs http://cr.openjdk.java.net/~kcr/8177566/webrev.00/doc-only/ This was largely the same basic change in several places, with some additional suggested cleanup in the *PropertyValueFactory classes. In case you also want to see the generated docs, you can look here: http://cr.openjdk.java.net/~kcr/8177566/webrev.00/javadoc/ (sparse API docs) and search for the following classes: Bindings (look for any of the "select" methods) JavaBeanBooleanProperty (class docs) PropertyValueFactory (class docs) TreeItemPropertyValueFactory (class docs) FXML (class docs) Intro to FXML doc: http://cr.openjdk.java.net/~kcr/8177566/webrev.00/javadoc/javafx/fxml/doc-files/introduction_to_fxml.html#deploy_as_module ----------------------------------------------------------------------------------- 3. Reviewing the tests http://cr.openjdk.java.net/~kcr/8177566/webrev.00/test-only/ I wrote 30 new unit tests, which are really 6 new tests with 5 variants of each test. They are: - AppTableView<Variant> - AppTreeTableView<Variant> - AppBeans<Variant> - AppBindings<Variant> - AppJSCallback<Variant> - AppFXML<Variant> Where <Variant> is one of: 1 - Unexported -- the module neither exports nor opens the package being tested 2 - Exported -- the module exports the package being tested unconditionally 3 - QualExported -- the module exports the package being tested to the necessary javafx.* module 4 - Opened -- the module opens the package being tested unconditionally 5 - QualOpened -- the module opens the package being tested to the necessary javafx.* module Each of the packages being tested is named myapp*/pkg1, pkg2, etc. The classes (and in the case of the last test, the .fxml files) in each package are identical except for the package name. This duplication perhaps could have been avoided, but only with some clever build logic to populate the package dirs with a parameterized file and preprocessor. I opted instead to write a validation script to ensure that the files only differ in the package name as expected. Similarly, there are only two unique test apps among the 5 variants of each test (except for FXML which has 4 unique tests). The test apps are launched from the ModuleLauncherTest unit test. If anyone wants to review these, I would recommend looking at the "QualOpened" and "Unexported" variants and skipping the rest. Also, the AppTableView and AppTreeTableView tests are very similar.
02-05-2017

While writing the unit tests and documentation for this it became clear to me that it requiring applications to export as public API all packages used by FXML, Java Beans, and JavaScript callbacks from WebVIew is not a reasonable requirement. The fact that we use sun.misc.reflect.MethodUtil.invoke is an implementation detail that should not dictate our API specification. As such, I have been working on a proposed fix as opposed to just documenting the existing limitation and producing a more understandable error message. The fix will allow applications to open their package to the javafx.base module (for Java Beans), or the javafx.fxml module (for FXML), or the javafx.web module (for JavaScript callbacks). This will allow applications to keep their FXML controller, for example, in a non-pubilc package, which is as it should be since the FXML controller is in almost all cases an implementation detail of the application. I have a fix that should be ready for review early next week.
28-04-2017

Functionally, this is working correctly, although the error message is misleading and could be improved. In the sense-nine application, the leaderboard.fxml file creates a TableView control as follows: <TableView fx:id="leaders" prefHeight="1000"> <columns> <TableColumn text="Twitter Handle" prefWidth="300"> <cellValueFactory> <PropertyValueFactory property="twitterHandle"/> </cellValueFactory> </TableColumn> <TableColumn text="Count" prefWidth="180"> <cellValueFactory> <PropertyValueFactory property="tweetCount"/> </cellValueFactory> </TableColumn> </columns> </TableView> The PropertyValueFactory instances tell each TableColumn the name of the property to reflectively access for that column ("twitterHandle" and "tweetCount", respectively). The application then sets the table's items to a list of TwitterUser objects: LeaderboardController: @FXML private TableView<TwitterUser> leaders; public void setData(LeaderboardData data) { leaders.setItems(data.getItems()); // <--- a list of TwitterUser objects. } When TableView subsequently accesses the data for each cell, it uses reflection to call the "twitterHandle" or "tweetCount" method on the TwitterView instance. This fails if TwitterUser is not in an exported package. The reason the exception complains about the package not being exported to the unnamed module is because JavaFX beans uses sun.misc.reflect.MethodUtil.invoke to invoke the method on the bean rather than calling Method.invoke directly. That utility method uses the sun.reflect.misc.Trampoline class, which is intentionally defined in the unnamed module. If JavaFX beans were able to call it directly then the error message would indicate javafx.base as the module that couldn't access the class in question. The upshot of this is that in JDK 9 it is required that any object that is reflected on in this manner by JavaFX beans, specifically the objects passed to TableView, will need to be in a package that is exported unconditionally. To address the confusing error message, I propose to modify the appropriate places in JavaFX beans to check whether a class being used as a bean is in an exported package and throw an exception with a better error message if not. Further, I will document at least the obvious places as requiring that the class in question be in an exported package. Ideally, an application would be able to do a qualified export of the package in question to javafx.base rather than exporting it unconditionally, so we can consider that as an enhancement for JDK 10.
10-04-2017

I have attached a very simple example program that demonstrates this issue without using FXML. Running the example program produces the same exception as reported: Exception in thread "JavaFX Application Thread" java.lang.RuntimeException: java.lang.IllegalAccessException: class sun.reflect.misc.Trampoline cannot access class mydata.MyData (in module mymod) because module mymod does not export mydata to unnamed module @61a9aad0 at javafx.base/com.sun.javafx.property.PropertyReference.getProperty(PropertyReference.java:200) at javafx.controls/javafx.scene.control.cell.PropertyValueFactory.getCellDataReflectively(PropertyValueFactory.java:147) at javafx.controls/javafx.scene.control.cell.PropertyValueFactory.call(PropertyValueFactory.java:120) at javafx.controls/javafx.scene.control.cell.PropertyValueFactory.call(PropertyValueFactory.java:99) at javafx.controls/javafx.scene.control.TableColumn.getCellObservableValue(TableColumn.java:593) at javafx.controls/javafx.scene.control.TableColumn.getCellObservableValue(TableColumn.java:578) at javafx.controls/javafx.scene.control.TableCell.updateItem(TableCell.java:646) at javafx.controls/javafx.scene.control.TableCell.indexChanged(TableCell.java:469) at javafx.controls/javafx.scene.control.IndexedCell.updateIndex(IndexedCell.java:120) at javafx.controls/javafx.scene.control.skin.TableSkinUtils.resizeColumnToFitContent(TableSkinUtils.java:119) at javafx.controls/javafx.scene.control.skin.TableSkinUtils.resizeColumnToFitContent(TableSkinUtils.java:86) at javafx.controls/javafx.scene.control.skin.TableColumnHeader.doColumnAutoSize(TableColumnHeader.java:573) at javafx.controls/javafx.scene.control.skin.TableColumnHeader.updateScene(TableColumnHeader.java:516) at javafx.controls/javafx.scene.control.skin.TableColumnHeader.lambda$new$0(TableColumnHeader.java:159) at javafx.controls/com.sun.javafx.scene.control.LambdaMultiplePropertyChangeListenerHandler.lambda$new$1(LambdaMultiplePropertyChangeListenerHandler.java:49) at javafx.base/javafx.beans.value.WeakChangeListener.changed(WeakChangeListener.java:89) at javafx.base/com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:181) at javafx.base/com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:80) at javafx.base/javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74) at javafx.base/javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102) at javafx.graphics/javafx.scene.Node$ReadOnlyObjectWrapperManualFire.fireSuperValueChangedEvent(Node.java:1054) at javafx.graphics/javafx.scene.Node.invalidatedScenes(Node.java:1116) at javafx.graphics/javafx.scene.Node.setScenes(Node.java:1154) at javafx.graphics/javafx.scene.Parent$2.onChanged(Parent.java:391) at javafx.base/com.sun.javafx.collections.TrackableObservableList.lambda$new$0(TrackableObservableList.java:45) at javafx.base/com.sun.javafx.collections.ListListenerHelper$Generic.fireValueChangedEvent(ListListenerHelper.java:329) at javafx.base/com.sun.javafx.collections.ListListenerHelper.fireValueChangedEvent(ListListenerHelper.java:73) at javafx.base/javafx.collections.ObservableListBase.fireChange(ObservableListBase.java:233) at javafx.base/javafx.collections.ListChangeBuilder.commit(ListChangeBuilder.java:482) at javafx.base/javafx.collections.ListChangeBuilder.endChange(ListChangeBuilder.java:541) at javafx.base/javafx.collections.ObservableListBase.endChange(ObservableListBase.java:205) at javafx.base/javafx.collections.ModifiableObservableListBase.setAll(ModifiableObservableListBase.java:90) at javafx.base/com.sun.javafx.collections.VetoableListDecorator.setAll(VetoableListDecorator.java:116) at javafx.controls/javafx.scene.control.skin.NestedTableColumnHeader.updateContent(NestedTableColumnHeader.java:572) at javafx.controls/javafx.scene.control.skin.NestedTableColumnHeader.updateTableColumnHeaders(NestedTableColumnHeader.java:498) at javafx.controls/javafx.scene.control.skin.NestedTableColumnHeader.checkState(NestedTableColumnHeader.java:632) at javafx.controls/javafx.scene.control.skin.NestedTableColumnHeader.computePrefHeight(NestedTableColumnHeader.java:344) at javafx.graphics/javafx.scene.Parent.prefHeight(Parent.java:1053) at javafx.graphics/javafx.scene.layout.Region.prefHeight(Region.java:1561) at javafx.controls/javafx.scene.control.skin.TableHeaderRow.computePrefHeight(TableHeaderRow.java:376) at javafx.controls/javafx.scene.control.skin.TableHeaderRow.computeMinHeight(TableHeaderRow.java:369) at javafx.graphics/javafx.scene.Parent.minHeight(Parent.java:1081) at javafx.graphics/javafx.scene.layout.Region.minHeight(Region.java:1527) at javafx.controls/javafx.scene.control.SkinBase.computeMinHeight(SkinBase.java:311) at javafx.controls/javafx.scene.control.Control.computeMinHeight(Control.java:512) at javafx.graphics/javafx.scene.Parent.minHeight(Parent.java:1081) at javafx.graphics/javafx.scene.layout.Region.minHeight(Region.java:1527) at javafx.graphics/javafx.scene.layout.Region.computeChildPrefAreaHeight(Region.java:1982) at javafx.graphics/javafx.scene.layout.Region.getMaxAreaHeight(Region.java:2199) at javafx.graphics/javafx.scene.layout.Region.computeMaxPrefAreaHeight(Region.java:2101) at javafx.graphics/javafx.scene.layout.StackPane.computePrefHeight(StackPane.java:311) at javafx.graphics/javafx.scene.Parent.prefHeight(Parent.java:1053) at javafx.graphics/javafx.scene.layout.Region.prefHeight(Region.java:1561) at javafx.graphics/javafx.scene.Scene.getPreferredHeight(Scene.java:1836) at javafx.graphics/javafx.scene.Scene.resizeRootToPreferredSize(Scene.java:1801) at javafx.graphics/javafx.scene.Scene.preferredSize(Scene.java:1772) at javafx.graphics/javafx.scene.Scene$2.preferredSize(Scene.java:394) at javafx.graphics/com.sun.javafx.scene.SceneHelper.preferredSize(SceneHelper.java:66) at javafx.graphics/javafx.stage.Window$12.invalidated(Window.java:1098) at javafx.base/javafx.beans.property.BooleanPropertyBase.markInvalid(BooleanPropertyBase.java:110) at javafx.base/javafx.beans.property.BooleanPropertyBase.set(BooleanPropertyBase.java:145) at javafx.graphics/javafx.stage.Window.setShowing(Window.java:1186) at javafx.graphics/javafx.stage.Window.show(Window.java:1201) at javafx.graphics/javafx.stage.Stage.show(Stage.java:276) at mymod/myapp.Main.start(Main.java:46) at javafx.graphics/com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$9(LauncherImpl.java:919) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runAndWait$11(PlatformImpl.java:449) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$9(PlatformImpl.java:418) at java.base/java.security.AccessController.doPrivileged(Native Method) at javafx.graphics/com.sun.javafx.application.PlatformImpl.lambda$runLater$10(PlatformImpl.java:417) at javafx.graphics/com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:96) at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication._runLoop(Native Method) at javafx.graphics/com.sun.glass.ui.gtk.GtkApplication.lambda$runLoop$11(GtkApplication.java:277) at java.base/java.lang.Thread.run(Thread.java:844) Caused by: java.lang.IllegalAccessException: class sun.reflect.misc.Trampoline cannot access class mydata.MyData (in module mymod) because module mymod does not export mydata to unnamed module @61a9aad0 at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:370) at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:589) at java.base/java.lang.reflect.Method.invoke(Method.java:555) at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:72) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.base/java.lang.reflect.Method.invoke(Method.java:563) at java.base/sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:261) at javafx.base/com.sun.javafx.property.PropertyReference.getProperty(PropertyReference.java:198) ... 73 more
10-04-2017

sun.reflect.misc.MethodUtil::invoke would only work if the target Method to be invoked is an exported API. If the Method being invoked is from user code, it's safe to use Method.invoke and there is no need to call MethodUtil.invoke to invoke the target Method through a trampoline.
24-03-2017