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 example application 'GuardTest' is a IntelliJ project that consists of 3 classes.
The contents of 'sample.fxml':
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<?import controls.CustomControl?>
<VBox fx:controller="sample.Controller"
xmlns:fx="http://javafx.com/fxml">
<children>
<CustomControl>
<children>
<Button text="Test"></Button>
</children>
</CustomControl>
</children>
</VBox>
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 "<java.home>/lib/rt.jar"
-libraryjars "<java.home>/lib/javaws.jar"
-libraryjars "<java.home>/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 *;
}
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:
<?import javafx.scene.control.Button?>
<?import javafx.scene.layout.VBox?>
<?import p.a?>
<VBox fx:controller="p.b"
xmlns:fx="http://javafx.com/fxml">
<children>
<a>
<children>
<Button text="Test"></Button>
</children>
</a>
</children>
</VBox>
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.
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 :)
I have found the solution! The problem is that FXML cannot import classes that do not start with an upper case letter. Therefore one has to provide an own list of available names that ProGuard uses for obfuscating. This is done by:
-classobfuscationdictionary obfuscationClassNames.txt
With obfuscationClassNames.txt containing the line seperated list of available class names:
A
B
C
D
...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With