ProGuard разрывает приложение JavaFX

Я пытаюсь запутать приложение JavaFX, но он терпит неудачу. Сгенерированный результат не работает, и я не понимаю, почему. Получающийся jar просто терпит неудачу, потому что файл fxml больше не может загружать весь импорт (ClassNotFoundException).

Рабочий процесс развертывания:

  • Построить runnable jar (в IntelliJ knwon как артефакт)
  • Обфускайте эту банку с помощью ProGuard
  • Исправьте некоторые проблемы в этой банке, которые ProGuard не выполняет

1) Минимальное примерное приложение

Пример приложения GuardTest - это проект IntelliJ, состоящий из 3 классов.

  • sample.Main: содержит точку входа приложения и загружает файл GUI fxml 'sample.fxml'
  • sample.Controller: класс контроллера для 'sample.fxml'
  • controls.CustomControl: простой элемент управления javafx, который наследуется от HBox. Это используется в 'sample.fxml'

Содержимое '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) Обфускация

Теперь я использую ProGuard для полученного файла jar, который создается из вышеупомянутого проекта. Я использую следующие настройки:

-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) Фиксация некоторых (очевидных) ошибок ProGuard

Полученный файл jar "Obfuscated.jar" имеет следующую структуру:

**Obfuscated.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
- sample
--> sample.fxml

Основной класс запускает GUI, загружая файл sample.fxml следующей строкой:

Parent root = FXMLLoader.load(getClass().getResource("sample.fxml"));

Из-за этого мне нужно переместить файл sample.fxml в папку p, чтобы снова сделать вышеприведенную строку. Я также исправляю некоторую проблему в файле fxml, где ProGuard забывает изменить имя класса (теперь запутанное).

Теперь структура выглядит так:

**Obfuscated_fixed.jar**
- META-INF
--> MANIFEST.MF
- p
--> a.class
--> b.class
--> c.class
--> sample.fxml

Файл sample.fxml теперь выглядит следующим образом:

<?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>

Проблема

Теперь эта банка должна действительно работать снова, потому что все в порядке. Но это НЕ! Загрузчик fxml не загружает CustomControl (теперь он называется /obfuscated 'a.class'). Почему это?

Я получаю следующий вывод ошибки при запуске файла jar (я запускаю java версию 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 . . .

Установка загрузчика классов по умолчанию в основном классе с помощью

FXMLLoader.setDefaultClassLoader(this.getClass().getClassLoader());

тоже не помогает.

Файлы проекта

Здесь вы можете найти пример проекта (IntelliJ): https://www.dropbox.com/s/ot51spvwk6lzo4k/GuardTest.zip?dl=0

Сгенерированный артефакт jar от IntelliJ скомпилирован для: . /out/artifacts/JavaFXApp/JavaFXApp.jar

Обфусканный ящик находится под: . /out/obfuscated/Obfuscated.jar

Обфускация, но фиксированная (по крайней мере, она должна быть), как описано выше: . /out/obfuscated/Obfuscated _fixed.jar

И чтобы показать, что оператор import в файле sample.fxml вызывает проблему, я удалил свой пользовательский элемент управления из файла fxml и сохранил его в (рабочем) банке: . /out/obfuscated/Obfuscated _fixed_work.jar

Прошу прощения за длинный вопрос. Надеюсь, ты мне все равно поможешь:)

Ответ 1

Я нашел решение! Проблема заключается в том, что FXML не может импортировать классы, которые не начинаются с буквы верхнего регистра. Поэтому необходимо предоставить собственный список доступных имен, которые ProGuard использует для обфускации. Это делается путем:

-classobfuscationdictionary obfuscationClassNames.txt

С obfuscationClassNames.txt, содержащим список отдельных доступных классов:

A
B
C
D
...

Ответ 2

Я использовал те же самые шаги. Я вижу, что импорт обновляется в файл fxml, но их использование не является.

Получение исключения:

javafx.fxml.LoadException: MenuBarControl не является допустимым типом.

Но если я вижу, что импорт файлов fxml обновляется, но не используется.

< ? import q.A ? >
< ? import r.A ? >

 <VBox fx:id="top">
    <MenuBarControl fx:id="menuBarControl"/>
  </VBox>