We have a java web start application that's working on java 8, but when running on Java 9 it crashes.
The application loads a new JavaFx scene that contains a webview which shows a web page that contains a menu, that menu contains different buttons, each button opens a new window which content uses thinlet.
For security reasons, we're encrypting the jar. So when we run the application we replace the classloader with our own classloader that will first decrypt the encrypted classes of the jar.
On click any of the buttons we use our own classloader to call the encrypted class using reflection and make the default classloader of that class our own, so all the rest of the classes will be able to be launched after decrypt it.
That's working fine until java 9. However on running the application on java 9, the first time I click any of the buttons the application is properly launched but after that every time I click the buttons I'm getting the a
java.lang.reflect.InvocationTargetException
caused by a java.lang.NullPointerException at jdk classes. The trace is different among the different button but there's a common part, here is the exception I get on running a simple sample:
java.lang.reflect.InvocationTargetException
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.base/java.lang.reflect.Method.invoke(Unknown Source)
at com.glt.cryptoclass.DecryptingClassLoader$PrivilegedLaunch.run(DecryptingClassLoader.java:56)
at java.base/java.security.AccessController.doPrivileged(Native Method)
at com.glt.cryptoclass.DecryptingClassLoader.launch(DecryptingClassLoader.java:179)
at com.glt.replace.Browser$1.call(Browser.java:58)
at com.glt.replace.Browser$1.call(Browser.java:42)
at javafx.graphics/javafx.concurrent.Task$TaskCallable.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.lang.NullPointerException
at java.desktop/java.awt.Window.addToWindowList(Unknown Source)
at java.desktop/java.awt.Window.init(Unknown Source)
at java.desktop/java.awt.Window.<init>(Unknown Source)
at java.desktop/java.awt.Frame.<init>(Unknown Source)
at com.thinlet.FrameLauncher.<init>(FrameLauncher.java:32)
at com.glt.replace.start.miau(start.java:32)
at com.glt.replace.start.main(start.java:22)
... 12 more
I've been researching for a long time and I've seen that maybe I had to open and export some modules in order to make it work. I've open and export at the jnlp the next modules:
<j2se version="1.6+" href="http://java.sun.com/products/autodl/j2se" java-vm-args="--add-opens=java.management/sun.management=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.base/java.security=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED --add-opens=java.base/java.util.concurrent=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=java.desktop/java.awt=ALL-UNNAMED --add-exports java.management/sun.management=ALL-UNNAMED --add-exports java.base/java.lang=ALL-UNNAMED --add-exports java.base/java.util=ALL-UNNAMED --add-exports java.base/java.security=ALL-UNNAMED --add-exports java.base/java.lang.reflect=ALL-UNNAMED --add-exports java.base/java.util.concurrent=ALL-UNNAMED --add-exports=java.desktop/java.awt=ALL-UNNAMED --add-modules=java.xml.ws"/>
Moreover, I've ran the jdep -jdkinternals command to see if I'm using any conflictive library but I'm not getting anything.
Furthermore, if I run the application without encryptation, or with encryptation but locally, it works properly. Can anyone point me what could I be doing wrong? Or has any idea of why it is behaving different on running remotly whith the jnlp than when I run it locally?
I also add some code that could be helpfull: Main Class(Contains the Web Engine):
public void start(Stage primaryStage) throws Exception {
this.stage = primaryStage;
SwingUtilities.invokeLater(this);
}
public void run() {
initAndShowGUI();
}
/**
* Invokes platform run later to start the application
* on the event dispatcher
*/
private void initAndShowGUI() {
Platform.runLater(new Runnable() {
public void run() {
initFX(stage);
}
});
}
/**
* initialise the stage get as a parameter
* @param stage stage to initialise
*/
private void initFX(Stage stage){
// browser = new Browser();
browser = new Browser();
scene = new Scene(browser,1050,700,Color.web("#666970"));
stage.setScene(scene);
stage.setTitle("TEST");
scene.getStylesheets().add("webviewsample/BrowserToolbar.css");
stage.show();
//edit action on close
stage.setOnCloseRequest(new EventHandler<WindowEvent>() {
@Override
public void handle(WindowEvent event) {
// try {
// //WARNING uninstall all the javaws applications TODO fin the way to know the jnlp path
// Runtime.getRuntime().exec("javaws -uninstall");
// } catch (IOException ex) {
// ex.printStackTrace();
// }
System.out.println("INFO: Stage is closing...");
Platform.exit();
System.exit(0);
}
});
}
/**
* class that extends Region and implements the handler for
* the html content from the home applet web page
* @param args
*/
public static void main(String[] args){
String base = "";
System.out.println("telekey.TeleKeyJnlp.main(). DEBUG: Reading arguments.");
for(String arg : args){
System.out.println("telekey.TeleKeyJnlp.main(). DEBUG: argument: " + arg);
if(arg.contains(BASE_URL) || arg.contains(SHORT_BASE_URL)){
System.out.println("telekey.TeleKeyJnlp.main(). DEBUG: Argument found.");
String []parts = arg.split(ARGUMENT_SEPARATOR);
if(parts.length > 1){
System.out.println("telekey.TeleKeyJnlp.main(). DEBUG: Setting Base URL.");
base = parts[1];
}
}
}
if(base.isEmpty()){
System.out.println("telekey.TeleKeyJnlp.main(). DEBUG: Base URL empty, setting base URL to " + DEFAULT_TK);
base = DEFAULT_TK;
}
launch(args);
}
Our own WebEngine:
public void launch(final String className, final String[] args) {
try {
initWebView();
//start new thread to the new window
javafx.concurrent.Task configTask = new javafx.concurrent.Task<Void>() {
@Override
protected Void call() {
//ModConfig.main(args) // ModBrowser.main(args);
try {
if (dcl == null) {
KeyGetter key = new KeyGetter("https://www.telekey.nl/", "getReplaceKeyDetails.jsp");
//
key.fetchKeyDetails();
System.out.println(".actionPerformed(): DEBUG. key details: "
+ key.getAlgorithm() + "\n"
+ key.getCypherDesc() + "\n"
+ key.getRawKey());
dcl = new DecryptingClassLoader(key);
}
Class[] parTypes = {String[].class};
dcl.launch(className, "main", args, parTypes, true);
} catch (Exception ex) {
ex.printStackTrace();
} finally {
return null;
}
}
};
new Thread(configTask).start();
} catch (Exception ex) {
ex.printStackTrace();
}
}
public void processNewLocation(String newLocation){
String []args = null;
if(newLocation != null && !newLocation.equals("https://www.google.nl/")){
launch("com.glt.replace.start", args);
}
}
public void initWebView() {
if (getChildren().contains(browser)) {
getChildren().remove(browser);
}
browser = null;
browser = new WebView();
webEngine = browser.getEngine();
//Set user agent to indicate we are on java fx webView
webEngine.setUserAgent(webEngine.getUserAgent() + " JavaFX-WebView");
webEngine.setJavaScriptEnabled(true);
webEngine.locationProperty().addListener((ObservableValue<? extends String> observable, String oldLocation, String newLocation) -> {
processNewLocation(newLocation);
});
webEngine.load("https://www.google.nl/"); // works but it does not have style
getChildren().add(browser);
}
The class we run on change search something on google(change location):
public static void main(String[] args) {
start s = new start();
s.miau();
}
private void miau() {
FrameLauncher f = new FrameLauncher("secundary", new Thinlet(), 300, 300);
System.out.println("com.glt.main.start.main(). DEBUG: Miau!!");
f.setVisible(true);
}
UPDATE 1:
I've found that the problem is coming on set the security manager. If I comment that line, my simple program works. However, we have to replace the classloader in order to have some permissions. Does anyone know the reason of why I cannot set the security manager at a javafx9 application? Or, what could I be doing wrong? The application is signed using jarsigner.
UPDATE 2: I've also send a bug request to java here is the url to it: https://bugs.openjdk.java.net/browse/JDK-8186568