ProGuard rompe la aplicación JavaFX
classnotfoundexception fxml (2)
¡He encontrado la solución! El problema es que FXML no puede importar clases que no comiencen con una letra mayúscula. Por lo tanto, uno debe proporcionar una lista propia de los nombres disponibles que ProGuard usa para ofuscar. Esto es hecho por:
-classobfuscationdictionary obfuscationClassNames.txt
Con offuscationClassNames.txt que contiene la lista separada por líneas de nombres de clases disponibles:
A
B
C
D
...
Estoy tratando de ofuscar mi aplicación JavaFX pero falla. El resultado generado no funciona y no entiendo por qué. El archivo jar resultante simplemente falla porque el archivo fxml ya no puede cargar todas las importaciones (ClassNotFoundException).
El flujo de trabajo de implementación:
- Crear jar ejecutable (en IntelliJ knwon como artefacto)
- Ofuscar ese frasco con ProGuard
- Soluciona algunos problemas en ese contenedor que ProGuard no puede hacer
1) La aplicación de ejemplo mínima
La aplicación de ejemplo ''GuardTest'' es un proyecto IntelliJ que consta de 3 clases.
- sample.Main: contiene el punto de entrada de la aplicación y carga el archivo fxml de la GUI ''sample.fxml''
- sample.Controller: la clase de controlador para ''sample.fxml''
- controls.CustomControl: un control javafx simple que hereda de HBox. Esto se usa en ''sample.fxml''
El contenido de ''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>
2) Ofuscación
Ahora uso ProGuard para el archivo jar resultante que se genera a partir del proyecto anterior. Utilizo la siguiente configuración:
-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 *;
}
3) Reparar algunas fallas de ProGuard (obvias)
El archivo jar resultante ''Obfuscated.jar'' tiene la siguiente estructura:
**Obfuscated.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
- sample
--> sample.fxml
La clase principal inicia la GUI cargando el archivo ''sample.fxml'' con la siguiente línea:
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));
Por eso tengo que mover el archivo ''sample.fxml'' a la carpeta p también para que la línea anterior funcione nuevamente. También soluciono algunos problemas en el archivo fxml donde ProGuard se olvida de cambiar un nombre de clase (ahora ofuscado).
Ahora la estructura se ve así:
**Obfuscated_fixed.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
--> sample.fxml
El archivo sample.fxml ahora se ve así:
<?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>
El problema
Ahora este frasco debería funcionar de nuevo porque todo está bien de nuevo. ¡Pero NO LO HACE! El cargador de fxml no puede cargar CustomControl (ahora denominado / ofuscado ''a.class''). ¿Porqué es eso?
Obtengo el siguiente resultado de error al iniciar el archivo jar (estoy ejecutando Java versión 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 . . .
Configurar el cargador de clases predeterminado en la clase principal con
FXMLLoader.setDefaultClassLoader(this.getClass().getClassLoader());
tampoco ayuda.
Archivos de proyecto
Aquí puede encontrar el proyecto de ejemplo (IntelliJ): https://www.dropbox.com/s/ot51spvwk6lzo4k/GuardTest.zip?dl=0
El artefacto de jar generado por IntelliJ se compila para: ./out/artifacts/JavaFXApp/JavaFXApp.jar
El Jar ofuscado se encuentra en: ./out/obfuscated/Obfuscated.jar
El contenedor ofuscado pero fijo (al menos debería serlo) como se describe arriba: ./out/obfuscated/Obfuscated_fixed.jar
Y para mostrar que la declaración de importación en el archivo ''sample.fxml'' causa el problema, eliminé mi control personalizado del archivo fxml y lo guardé en el jar (operativo): ./out/obfuscated/Obfuscated_fixed_work.jar
Lo siento por la larga pregunta. Espero que me ayudes de todos modos :)
Usé los mismos pasos. Puedo ver que las importaciones se actualizan en un archivo fxml, pero su uso no es así.
Obteniendo la excepción:
javafx.fxml.LoadException: MenuBarControl no es un tipo válido.
Pero si veo que las importaciones del archivo fxml están actualizadas, pero no su uso.
< ? import q.A ? >
< ? import r.A ? >
<VBox fx:id="top">
<MenuBarControl fx:id="menuBarControl"/>
</VBox>