I'm trying to obfuscate my JavaFX application but it fails. The generated result does not work and I do not understand why. The resulting jar just fails because the fxml file cannot load all imports anymore (ClassNotFoundException).
The Deployment workflow:
- Build runnable jar (in IntelliJ knwon as an artifact)
- Obfuscate that jar with ProGuard
- Fix some issues in that jar that ProGuard fails to do
1) The minimal example application
The example application 'GuardTest' is a IntelliJ project that consists of 3 classes.
- sample.Main: contains the application entry point and loads the GUI fxml file 'sample.fxml'
- sample.Controller: the controller class for 'sample.fxml'
- controls.CustomControl: A simple javafx control that inherits from HBox. This is used in 'sample.fxml'
The contents of 'sample.fxml':
2) Obfuscation
Now I use ProGuard for the resulting jar file that is generated from the above project. I use the following settings:
-target 8 -injars ./out/artifacts/JavaFXApp/JavaFXApp.jar -outjars ./out/obfuscated/Obfuscated.jar -ignorewarnings -printmapping ./out/obfuscated/proguard.map -dontusemixedcaseclassnames -dontshrink -dontoptimize -dontskipnonpubliclibraryclasses -dontskipnonpubliclibraryclassmembers #-flattenpackagehierarchy -repackageclasses 'p' -allowaccessmodification -libraryjars "/lib/rt.jar" -libraryjars "/lib/javaws.jar" -libraryjars "/lib/ext/jfxrt.jar" -adaptresourcefilecontents **.fxml,**.properties,META-INF/MANIFEST.MF,images/*.jar,publicCerts.store,production.version -keepattributes javafx.fxml.FXML,Exceptions,InnerClasses,Signature,Deprecated,SourceFile,LineNumberTable,LocalVariable*Table,*Annotation*,Synthetic,EnclosingMethod -keepclassmembers class * { @javafx.fxml.FXML *; } -keepclassmembernames public class com.javafx.main.Main, com.nywelt.sharkload.application.Main { public static void main(java.lang.String[]); } -keepclasseswithmembers public class com.javafx.main.Main, com.product.main.EntryFX, net.license.LicenseEntryPoint { public *; public static *; } 3) Fixing some (obvious) ProGuard failures
The resulting jar file 'Obfuscated.jar' has the following structure:
**Obfuscated.jar** - META-INF --> MANIFEST.MF - p --> a.class --> b.class --> c.class - sample --> sample.fxml The main class starts the GUI by loading the 'sample.fxml' file with the following line:
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml")); Because of that I have to move the 'sample.fxml' file to the folder p as well to make the above line work again. I also fix some issue in the fxml file where ProGuard forgets to change a (now obfuscated) class name.
Now the structure looks like this:
**Obfuscated_fixed.jar** - META-INF --> MANIFEST.MF - p --> a.class --> b.class --> c.class --> sample.fxml The sample.fxml file now looks like this:
The Problem
Now this jar should really work again because everything is ok again. But it DOESN'T! The fxml loader fails to load the CustomControl (now named/obfuscated 'a.class'). Why is that?
I get the following error output when starting the jar file (I'm running java version 1.8.0_40):
E:\Eigene Programme\GuardTest\out\obfuscated>java -jar Obfuscated_fixed.jar Exception in Application start method Exception in thread "main" java.lang.reflect.InvocationTargetException at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) at java.lang.reflect.Method.invoke(Unknown Source) at sun.launcher.LauncherHelper$FXHelper.main(Unknown Source) Caused by: java.lang.RuntimeException: Exception in Application start method at com.sun.javafx.application.LauncherImpl.launchApplication1(Unknown So urce) at com.sun.javafx.application.LauncherImpl.lambda$launchApplication$152( Unknown Source) at com.sun.javafx.application.LauncherImpl$$Lambda$49/849460928.run(Unkn own Source) at java.lang.Thread.run(Unknown Source) Caused by: javafx.fxml.LoadException: file:/E:/Eigene%20Programme/GuardTest/out/obfuscated/Obfuscated_fixed.jar!/p/sam ple.fxml at javafx.fxml.FXMLLoader.constructLoadException(Unknown Source) at javafx.fxml.FXMLLoader.importClass(Unknown Source) at javafx.fxml.FXMLLoader.processImport(Unknown Source) at javafx.fxml.FXMLLoader.processProcessingInstruction(Unknown Source) at javafx.fxml.FXMLLoader.loadImpl(Unknown Source) at javafx.fxml.FXMLLoader.loadImpl(Unknown Source) at javafx.fxml.FXMLLoader.loadImpl(Unknown Source) at javafx.fxml.FXMLLoader.loadImpl(Unknown Source) at javafx.fxml.FXMLLoader.loadImpl(Unknown Source) at javafx.fxml.FXMLLoader.loadImpl(Unknown Source) at javafx.fxml.FXMLLoader.loadImpl(Unknown Source) at javafx.fxml.FXMLLoader.load(Unknown Source) at p.c.start(Main.java:13) at com.sun.javafx.application.LauncherImpl.lambda$launchApplication1$159 (Unknown Source) at com.sun.javafx.application.LauncherImpl$$Lambda$52/663980963.run(Unkn own Source) at com.sun.javafx.application.PlatformImpl.lambda$runAndWait$172(Unknown Source) at com.sun.javafx.application.PlatformImpl$$Lambda$46/410424423.run(Unkn own Source) at com.sun.javafx.application.PlatformImpl.lambda$null$170(Unknown Sourc e) at com.sun.javafx.application.PlatformImpl$$Lambda$48/1149216748.run(Unk nown Source) at java.security.AccessController.doPrivileged(Native Method) at com.sun.javafx.application.PlatformImpl.lambda$runLater$171(Unknown S ource) at com.sun.javafx.application.PlatformImpl$$Lambda$47/1963387170.run(Unk nown Source) at com.sun.glass.ui.InvokeLaterDispatcher$Future.run(Unknown Source) at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) at com.sun.glass.ui.win.WinApplication.lambda$null$145(Unknown Source) at com.sun.glass.ui.win.WinApplication$$Lambda$36/237061348.run(Unknown Source) ... 1 more Caused by: java.lang.ClassNotFoundException at javafx.fxml.FXMLLoader.loadType(Unknown Source) ... 26 more E:\Eigene Programme\GuardTest\out\obfuscated>Pause Drücken Sie eine beliebige Taste . . . Setting the default class loader in the main class with
FXMLLoader.setDefaultClassLoader(this.getClass().getClassLoader()); does not help either.
Project Files
Here you can find the example project (IntelliJ): https://www.dropbox.com/s/ot51spvwk6lzo4k/GuardTest.zip?dl=0
The generated jar artifact by IntelliJ is compiled to: ./out/artifacts/JavaFXApp/JavaFXApp.jar
The obfuscated Jar is found under: ./out/obfuscated/Obfuscated.jar
The obfuscated but fixed (at least it should be) jar as described above: ./out/obfuscated/Obfuscated_fixed.jar
And to show that the import statement in the 'sample.fxml' file causes the problem I removed my custom control from the fxml file and saved that to the (working) jar: ./out/obfuscated/Obfuscated_fixed_work.jar
I'm sorry for the long question. I hope you will help me anyway :)