Examples

Some typical useful configurations:

  1. A typical application
  2. A typical applet
  3. A typical midlet
  4. All possible applications in the input jars
  5. All possible applets in the input jars
  6. All possible midlets in the input jars
  7. All possible servlets in the input jars
  8. Processing a library
  9. Processing native methods
  10. Processing serializable classes
  11. Processing bean classes
  12. Producing useful obfuscated stack traces
  13. Finding dead code
  14. Printing out the internal structure of class files
 

A typical application

To shrink and obfuscate the ProGuard application itself, one would typically create a configuration file proguard.pro and then type:
java -jar proguard.jar @proguard.pro

The configuration file would contain the following options:
-libraryjars <java.home>/lib/rt.jar
-injars      proguard.jar
-outjar      proguard_out.jar
-overloadaggressively
-defaultpackage ''

-keep public class proguard.ProGuard {
    public static void main(java.lang.String[]);
}

Note the use of the <java.home> system property; it is replaced automatically.

Also note that the type names are fully specified: proguard.ProGuard and java.lang.String[].

The access modifiers public and static are not really required in this case, since we know a priori that the specified class and method have the proper access flags. It just looks more familiar this way.

We're using the -overloadaggressively and -defaultpackage options to shave off some extra bytes, but we could leave them out as well. The -defaultpackage directive moves all classes to the given package, in this case the root package. Only proguard.ProGuard keeps its original name.

In general, you might need a few additional directives for processing native methods, serializable classes, or bean classes. For 'simple' applications like ProGuard, that is not required.  

A typical applet

These options shrink and obfuscate the applet mypackage.MyApplet:
-libraryjars <java.home>/lib/rt.jar
-injars      in.jar
-outjar      out.jar

-keep public class mypackage.MyApplet

The typical applet methods will be preserved automatically, since mypackage.MyApplet is an extension of the Applet class in the library rt.jar.

If applicable, you should add directives for processing native methods, serializable classes, or bean classes.  

A typical midlet

These options shrink and obfuscate the J2ME midlet mypackage.MyMIDlet:
-libraryjars /usr/local/java/wtk104/lib/midpapi.zip
-injars      in.jar
-outjar      out.jar

-keep public class mypackage.MyMIDlet

Note how we're now targeting the J2ME run-time environment midpapi.zip, instead of the J2SE run-time environment rt.jar.

The typical midlet methods will be preserved automatically, since mypackage.MyMIDlet is an extension of the MIDlet class in the library midpapi.zip.

If applicable, you should add directives for processing native methods, serializable classes, or bean classes.

You should preverify your midlets after having processed them, using J2ME's preverify tool.

Please note the in-depth article "Obfuscating MIDlet Suites with ProGuard" at wireless.java.sun.com.  

All possible applications in the input jars

These options shrink and obfuscate all public applications in in.jar:
-libraryjars <java.home>/lib/rt.jar
-injars      in.jar
-outjar      out.jar
-printseeds

-keepclasseswithmembers public class * {
    public static void main(java.lang.String[]);
}

Note the use of -keepclasseswithmembers. We don't want to preserve all classes, just all classes that have main methods, and those methods.

The -printseeds option prints out which classes exactly will be preserved, so we know for sure we're getting what we want.

If applicable, you should add directives for processing native methods, serializable classes, or bean classes.  

All possible applets in the input jars

These options shrink and obfuscate all public applets in in.jar:
-libraryjars <java.home>/lib/rt.jar
-injars      in.jar
-outjar      out.jar
-printseeds

-keep public class * extends java.applet.Applet

We're simply keeping all classes that extend the Applet class.

Again, the -printseeds option prints out which applets exactly will be preserved.

If applicable, you should add directives for processing native methods, serializable classes, or bean classes.  

All possible midlets in the input jars

These options shrink and obfuscate all public J2ME midlets in in.jar:
-libraryjars /wtk104/lib/midpapi.zip
-injars      in.jar
-outjar      out.jar
-printseeds

-keep public class * extends javax.microedition.midlet.MIDlet

We're simply keeping all classes that extend the MIDlet class.

And again, the -printseeds option prints out which midlets exactly will be preserved.

If applicable, you should add directives for processing native methods, serializable classes, or bean classes.

You should preverify your midlets after having processed them, using J2ME's preverify tool.  

All possible servlets in the input jars

These options shrink and obfuscate all public servlets in in.jar:
-libraryjars <java.home>/lib/rt.jar
-libraryjars /usr/local/java/servlet/servlet.jar
-injars      in.jar
-outjar      out.jar
-printseeds

-keep public class * implements javax.servlet.Servlet

Keeping all servlets is very similar to keeping all applets. The servlet API is not part of the standard run-time jar, so we're specifying it as a library. Don't forget to use the right path name.

We're then keeping all classes that implement the Servlet interface. We're using the implements keyword because it looks more familiar in this context, but it is equivalent to extends, as far as ProGuard is concerned.

And again, the -printseeds option prints out which servlets exactly will be preserved.

If applicable, you should add directives for processing native methods, serializable classes, or bean classes.  

Processing a library

These options shrink and obfuscate an entire library, keeping all public and protected classes and class members, native method names, and serialization code:
-libraryjars  <java.home>/lib/rt.jar
-injars       in.jar
-outjar       out.jar
-printmapping out.map

-renamesourcefileattribute SourceFile
-keepattributes InnerClasses,SourceFile,LineNumberTable,Deprecated

-keep public class * {
    public protected *;
}

-keepclassmembernames class * {
    static Class class$(java.lang.String);
    static Class class$(java.lang.String, boolean);
}

-keepclasseswithmembernames class * {
    native <methods>;
}

-keepclassmembers class * implements java.io.Serializable {
    static final long serialVersionUID;
    private void writeObject(java.io.ObjectOutputStream);
    private void readObject(java.io.ObjectInputStream);
    Object writeReplace();
    Object readResolve();
}

This configuration should preserve everything we'll ever want to access in the library. Only if there are any other non-public classes or methods that are invoked dynamically, they should be specified using additional -keep directives.

Note the use of -keepclassmembernames for the static class$ methods. These methods are inserted by the javac compiler and the jikes compiler respectively, to implement the .class construct. ProGuard will automatically detect them and deal with them; no problem at this point. However, ProGuard may be used again to shrink an application that uses this obfuscated library. Without having preserved the method names, the automatic detection would fail the second time around.

We've added some directives for keeping the "Deprecated" attribute, for useful stack traces, for native methods, and for serializable classes, which are discussed in their respective examples.

The "InnerClasses" attribute (or more precisely, its source name part) has to be preserved as well, for any inner classes that can be referenced from outside the library. The javac compiler would be unable to find the inner classes otherwise.  

Processing native methods

If your application, applet, servlet, library, etc., contains native methods, you'll want to preserve their names and their classes' names, so they can still be linked to the native library. The following additional option will ensure that:
-keepclasseswithmembernames class * {
    native <methods>;
}

Note the use of -keepclasseswithmembernames. We don't want to preserve all classes or all native methods; we just want to keep the relevant names from being obfuscated.  

Processing serializable classes

More complex applications, applets, servlets, libraries, etc., may contain classes that are serialized. Depending on the way in which they are used, they may require special attention:

Note that the above directives may preserve more classes and class members than strictly necessary. For instance, a large number of classes may implement the Serialization interface, yet only a small number may actually ever be serialized. Knowing your application and tuning the configuration will often produce more compact results.  

Processing bean classes

If your application, applet, servlet, library, etc., uses extensive introspection on bean classes to find bean editor classes, or getter and setter methods, then configuration may become laborious. There's not much else you can do than making sure the bean class names, or the getter and setter names don't change. For instance:
-keep public class mypackage.MyBean {
    public void setMyProperty(int);
    public int getMyProperty();
}

-keep public class mypackage.MyBeanEditor
 

Producing useful obfuscated stack traces

These options let obfuscated applications or libraries produce stack traces that can still be deciphered later on:
-printmapping out.map

-renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable

We're keeping all source file attributes, but we're replacing their values by the string "SourceFile". We could use any string. This string is already present in all class files, so it doesn't take up any extra space.

We're also keeping the line number tables of all methods.

Whenever both of these attributes are present, the Java run-time environment will include line number information when printing out exception stack traces.

The information will only be useful if we can map the obfuscated names back to their original names, so we're saving the mapping to a file out.map. The information can then be used by the ReTrace tool to restore the original stack trace.  

Finding dead code

These options list unused fields and methods in the application mypackage.MyApplication:
-libraryjars <java.home>/lib/rt.jar
-injars      in.jar
-dontobfuscate
-printusage

-keep public class mypackage.MyApplication {
    public static void main(java.lang.String[]);
}

We're not specifying an output jar, just printing out some results.

We're saving a little bit of time by not passing through the obfuscation phase.  

Printing out the internal structure of class files

These options print out the internal structure of all class files in the input jar:
-injars in.jar
-dontwarn
-dontnote
-dontshrink
-dontobfuscate
-dump

Note how we don't need to specify the Java run-time jar, because we're not processing the input jar at all. In this case, we can safely ignore any warnings about unresolved references, so we use the -dontwarn option. We're also not interested in any advice about dynamic invocations, so we use the -dontnote option.


Copyright © 2002 Eric Lafortune.