Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I use Proguard obfuscation while building a "maven-plugin" package?

The generated output JAR of a "maven-plugin" project (using the maven-plugin-plugin) is broken by the obfuscation performed by the Proguard tool. Attempting to use the obfuscated JAR as a Maven plugin generates exceptions, such as a MojoExecutionException, which terminate the build with an error. What are the proper Proguard configuration options to allow the generation of a working "maven-plugin" JAR containing an automatically generated plugin descriptor?

like image 498
Emily Mabrey Avatar asked Jan 10 '16 14:01

Emily Mabrey


1 Answers

Basics of Maven Plugins and the Proguard Tool

In order to generate a Maven plugin (maven packaging type "maven-plugin", which generates a JAR containing plugin specific configuration resources) we must instruct the maven-plugin-plugin on the location and name of our Mojos. Assuming a properly configured maven-plugin-plugin execution, either using annotations or other configuration options, the generated JAR will contain a plugin.xml file within the META-INF directory at the root of the JAR. This plugin.xml file describes your plugin's goals and configurable parameters using static references to Java class and package names (you can find more information on this file here).

Special care must be taken to incorporate obfuscation into a build of a "maven-plugin" JAR; here we explain the steps taken in using the Proguard obfuscation library. When using the default Proguard configuration for library obfuscation, the generated JAR will no longer work correctly because Proguard renames, shrinks, relocates and obfuscates important files for the Maven plugin. Attempting to use your plugin will likely result in an exception which terminates the build with an error related to the Maven runtime's inability to locate and process the plugin's configuration and class files.

With a little reconfiguration, however, we can instruct Proguard to properly maintain the generated plugin files and directory structure of your "maven-plugin" JAR. The necessary changes to the Proguard options are as follows (please see the notes regarding the following links):


Custom Proguard Configuration

Directory Structure

-keepdirectories

-keepdirectories

This instructs Proguard to maintain the input JAR directory structure instead of moving all files to the root directory. Maven expects the plugin.xml file to be located within the /META-INF/maven/ directory which is preserved alongside all other directories via this option. You can filter the kept directories more specifically by specifying a directory filter, however I have chosen to indiscriminately maintain all input directory structures.

Static Package References

-keeppackagenames

-keeppackagenames org.apache.maven.plugin.my.MyMojo

You should replace the placeholder package with the package containing your Mojo definitions. If your Mojo definitions do not share a common package you should specify each unique package using multiple options as needed. If you are unsure which packages need to be kept, open your generated plugin.xml file in a text editor and examine the "implementation" elements within each "mojo" definition. The "implementation" element specifies a class via a fully qualified name. Each package component of those fully qualified class names are the packages you should be specifying. For example, my basedir-plugin contains a Mojo implementation element value of "com.github.emabrey.maven.plugins.basedir.RootDirectoryGoal", so I would write the option as -keeppackagenames com.github.emabrey.maven.plugins.basedir.

Static Class References

-keepnames

-keepnames class * implements org.apache.maven.plugin.AbstractMojo

This option prevents Proguard from renaming the classes which contain the implementation of a Maven plugin Mojo. If these classes were renamed the aforementioned "implementation" elements would no longer correctly identify the classes containing a Mojo implementation.

Private Class Fields and Methods

-keepclassmembers

-keepclassmembers class * implements org.apache.maven.plugin.AbstractMojo {
    private <fields>;
    private <methods>;
}

This option prevents Proguard from renaming the class level methods and fields within a plugin Mojo implementation. Maven uses the names of these class fields/methods to generate the configuration elements for the plugin. If Proguard renames a field the Maven execution environment will be unable to correctly populate the Mojo implementation with the user configuration.


Completed Proguard Configuration

The full configuration for version 2.0.13 (more versions here; please see the notes regarding the plugin's version), including the default library configuration alongside the mentioned modifications, is provided here for your convenience (remember to specify the ${tool.proguard.version} property with the latest version of the proguard-base artifact and replace the placeholder package "org.apache.maven.plugin.my.MyMojo" with the appropriate value(s)):

<!-- Configures Proguard obfuscation tool to generate an
     obfuscated version of the JAR file that replaces the
     default unobfuscated JAR.
-->
<plugin>
    <groupId>com.github.wvengen</groupId>
    <artifactId>proguard-maven-plugin</artifactId>
    <version>2.0.13</version>
    <executions>
        <execution>
            <id>obfuscation-packaging</id>
            <phase>package</phase>
            <goals>
                <goal>proguard</goal>
            </goals>
            <configuration>
                <proguardVersion>${tool.proguard.version}</proguardVersion>
                <obfuscate>true</obfuscate>
                <attach>true</attach>
                <appendClassifier>false</appendClassifier>
                <addMavenDescriptor>true</addMavenDescriptor>
                <injar>${project.build.finalName}.jar</injar>
                <injarNotExistsSkip>true</injarNotExistsSkip>
                <libs>
                    <lib>${java.home}/lib/rt.jar</lib>
                </libs>

                <options>
                    <option>-keepdirectories</option>
                    <option>-keeppackagenames org.apache.maven.plugin.my.MyMojo</option>
                    <option>-keepnames class * implements org.apache.maven.plugin.AbstractMojo</option>
                    <option>-keepclassmembers class * implements org.apache.maven.plugin.AbstractMojo {
                        private <![CDATA[<fields>]]>;
                        private <![CDATA[<methods>]]>;
                    }
                    </option>
                    <option>-keepparameternames</option>
                    <option>-renamesourcefileattribute SourceFile</option>
                    <option>-keepattributes Exceptions,InnerClasses,Signature,Deprecated,
                        SourceFile,LineNumberTable,*Annotation*,EnclosingMethod
                    </option>
                    <option>-target 1.8</option>
                    <option>-keep public class * {
                        public protected *;
                        }
                    </option>
                    <option>-keepclassmembernames class * {
                        java.lang.Class class$(java.lang.String);
                        java.lang.Class class$(java.lang.String, boolean);
                        }
                    </option>
                    <option>-keepclasseswithmembernames,includedescriptorclasses class * {
                        native <![CDATA[<methods>]]>;
                        }
                    </option>
                    <option>-keepclassmembers,allowoptimization enum * {
                        public static **[] values();
                        public static ** valueOf(java.lang.String);
                        }
                    </option>
                    <option>-keepclassmembers class * implements java.io.Serializable {
                        static final long serialVersionUID;
                        private static final java.io.ObjectStreamField[] serialPersistentFields;
                        private void writeObject(java.io.ObjectOutputStream);
                        private void readObject(java.io.ObjectInputStream);
                        java.lang.Object writeReplace();
                        java.lang.Object readResolve();
                        }
                    </option>
                </options>
            </configuration>
        </execution>
    </executions>
    <dependencies>
        <dependency>
            <groupId>net.sf.proguard</groupId>
            <artifactId>proguard-base</artifactId>
            <version>${tool.proguard.version}</version>
        </dependency>
    </dependencies>
</plugin>  

Notes

Link Issues (9-04-2019)

The Proguard website has some sort of issue that means my links to the program options are not always going to the location of the anchor the way they should be for an anchor-containing link. Simply slightly scroll upwards if you don't see the option you clicked on being initially displayed on their webpage.

Version Issues of proguard-maven-plugin (9-04-2019)

The current version of the com.github.wvengen:proguard-maven-plugin is 2.0.14 as of March 2017 until the making of this edit. I will be leaving the original configuration intact with version number 2.0.13 because version 2.0.14 contains a potentially breaking change. It now includes the seed and map files as part of the output artifact of the final Proguard obfuscated artifact. It is unlikely that most use cases would actually have any issue consuming additional files in the artifact, but instead of ninja editing the configuration to point to 2.0.14, I am instead leaving this note and letting you evaluate which version is appropriate for your project. That said, simply altering the version to <version>2.0.14</version> should be okay, as no plugin configuration changes are noted in the commit history of version 2.0.14.

like image 128
Emily Mabrey Avatar answered Oct 06 '22 01:10

Emily Mabrey