MrJman006 MrJman006 - 2 months ago 17
Java Question

Javafx WebView can not access a member of class ... with modifiers "public"

In a project I am working on, I need to define an abstract class with some methods implemented in packageone. In packagetwo the remainder of the abstract methods will be implemented when the class is instantiated. The instance is then exposed to Javascript code in a JavaFX webview. (An online example from Oracle - https://blogs.oracle.com/javafx/entry/communicating_between_javascript_and_javafx)

My problem is, the methods implemented in packagetwo are not callable from the Javascript environment. I get the following error:

Exception in thread "JavaFX Application Thread" netscape.javascript.JSException: java.lang.IllegalAccessException: Class sun.reflect.misc.Trampoline can not access a member of class packagetwo.MainWindow$1 with modifiers "public"
at com.sun.webkit.dom.JSObject.fwkMakeException(JSObject.java:128)
at com.sun.webkit.WebPage.twkExecuteScript(Native Method)
at com.sun.webkit.WebPage.executeScript(WebPage.java:1439)
at javafx.scene.web.WebEngine.executeScript(WebEngine.java:982)
at packagetwo.MainWindow.makeUpCall(MainWindow.java:130)
at packagetwo.MainWindow.access$000(MainWindow.java:22)
at packagetwo.MainWindow$2.changed(MainWindow.java:83)
at packagetwo.MainWindow$2.changed(MainWindow.java:68)
at com.sun.javafx.binding.ExpressionHelper$SingleChange.fireValueChangedEvent(ExpressionHelper.java:182)
at com.sun.javafx.binding.ExpressionHelper.fireValueChangedEvent(ExpressionHelper.java:81)
at javafx.beans.property.ReadOnlyObjectPropertyBase.fireValueChangedEvent(ReadOnlyObjectPropertyBase.java:74)
at javafx.beans.property.ReadOnlyObjectWrapper.fireValueChangedEvent(ReadOnlyObjectWrapper.java:102)
at javafx.beans.property.ObjectPropertyBase.markInvalid(ObjectPropertyBase.java:112)
at javafx.beans.property.ObjectPropertyBase.set(ObjectPropertyBase.java:146)
at javafx.scene.web.WebEngine$LoadWorker.updateState(WebEngine.java:1260)
at javafx.scene.web.WebEngine$LoadWorker.dispatchLoadEvent(WebEngine.java:1371)
at javafx.scene.web.WebEngine$LoadWorker.access$1200(WebEngine.java:1253)
at javafx.scene.web.WebEngine$PageLoadListener.dispatchLoadEvent(WebEngine.java:1240)
at com.sun.webkit.WebPage.fireLoadEvent(WebPage.java:2400)
at com.sun.webkit.WebPage.fwkFireLoadEvent(WebPage.java:2244)
at com.sun.webkit.network.URLLoader.twkDidFinishLoading(Native Method)
at com.sun.webkit.network.URLLoader.notifyDidFinishLoading(URLLoader.java:838)
at com.sun.webkit.network.URLLoader.lambda$didFinishLoading$96(URLLoader.java:829)
at com.sun.javafx.application.PlatformImpl.lambda$null$173(PlatformImpl.java:295)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.javafx.application.PlatformImpl.lambda$runLater$174(PlatformImpl.java:294)
at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(InvokeLaterDispatcher.java:95)
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method)
at com.sun.glass.ui.win.WinApplication.lambda$null$148(WinApplication.java:191)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.IllegalAccessException: Class sun.reflect.misc.Trampoline can not access a member of class packagetwo.MainWindow$1 with modifiers "public"
at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
at java.lang.reflect.Method.invoke(Method.java:491)
at sun.reflect.misc.Trampoline.invoke(MethodUtil.java:71)
at sun.reflect.GeneratedMethodAccessor1.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at sun.reflect.misc.MethodUtil.invoke(MethodUtil.java:275)
at com.sun.webkit.Utilities.lambda$fwkInvokeWithContext$55(Utilities.java:94)
at java.security.AccessController.doPrivileged(Native Method)
at com.sun.webkit.Utilities.fwkInvokeWithContext(Utilities.java:94)
... 29 more


Though calling methods implemented in packageone works fine. Calling both sets of methods from Java also works fine. Below are My classes and Javascript calls.

Packagetwo

package packagetwo;

import packageone.Calls;



import java.io.File;
import java.net.URL;
import java.util.ResourceBundle;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.BorderPane;
import javafx.scene.web.WebView;
import netscape.javascript.JSObject;


public class MainWindow implements Initializable
{

@FXML
private BorderPane mainWindow;

@FXML
private WebView mainWebView;

@FXML
private AnchorPane mainWebViewLayout;

@Override
public void initialize(URL url, ResourceBundle rb)
{
Calls jc = new Calls()
{
@Override
public void aMethod1()
{
System.out.printf("Calls: Abstract Method Impl 1 Called.\n");
}
};

// Add new load complete listener.
ChangeListener<Worker.State> onLoadComplete = buildOnLoadCompleteListener(mainWebView, jc);
mainWebView.getEngine()
.getLoadWorker()
.stateProperty()
.addListener(
onLoadComplete);


// Attempt to load the current web package.
mainWebView.getEngine().load("file:///" + new File("index.html").getAbsolutePath());

}

private ChangeListener<Worker.State> buildOnLoadCompleteListener(
WebView webview, Calls javaUpCalls)
{
return new ChangeListener<Worker.State>()
{
@Override
public void changed(
ObservableValue<? extends Worker.State> ov,
Worker.State lastState, Worker.State currentState)
{
if (currentState == Worker.State.SUCCEEDED)
{
// Hook up Java up calls in the Javascript environment.
JSObject jsEnvironment = (JSObject) webview.getEngine().executeScript("window");
jsEnvironment.setMember("JavaUpCalls", javaUpCalls);

makeUpCall(webview);
}
}
};
}

private void makeUpCall(WebView webview)
{
String setUpUpCall =
"function makeAnUpCall()" +
"{" +
"if(typeof JavaUpCalls !== \"undefined\")" +
"{" +
"JavaUpCalls.method2();" +
"JavaUpCalls.aMethod1();" +
"}" +
"}";


webview.getEngine().executeScript(setUpUpCall);
webview.getEngine().executeScript("makeAnUpCall()");
}
}


Packageone

package packageone;

public abstract class Calls()
{
public abstract void aMethod1();
public void method2()
{
System.out.printf("Calls: Method 1 Called.\n");
}
}


Index.html is just a standard html page that load successfully with a
<p>
tag that says the page loaded.

Any idea why the Javascript environment can't call my methods implemented in packagetwo?

EDIT:

I researched a bit online and found this StackOverflow question:



I attempted the solution listed there (calling setAccessible(true) on the packagetwo method instance) and the error was still thrown.

I also tried initializing and calling the Javascript methods from packageone, but the still got the error.

Answer

The anonymous implementation of Calls isn't available as a public class, so the javascript engine can't get to it.

If you create a public implementation in packagetwo, even as an inner class within MainWindow and then create an instance of that to pass through it will work as you expect.