by Jonathan Knudsen
August 23, 2002
Download: ProGuardWTKGlue.java
One challenge of MIDP development is keeping applications small and
light. While an obfuscator is designed to make applications harder to
reverse-engineer, a happy side effect is that it also makes
them as small as possible. ProGuard is an open source obfuscator that does an
excellent job of
reducing the size of MIDlet suite JARs. This tech tip explains how to use
ProGuard on the command line, with Ant, and with the J2ME Wireless Toolkit.
To learn more about obfuscators in general, see:
ProGuard is freely available under the GNU General Public License.
If you can live without the support that accompanies a payware product,
ProGuard is an excellent addition to your MIDlet development toolbox.
Downloading and Installing ProGuard
The current version of ProGuard is 1.2. It is distributed as either a
.tar.gz file or a .zip file. Once you download and unpack the
archive you prefer, you'll have a proguard1.2 directory with several
subdirectories. The obfuscator itself is contained in lib/proguard.jar.
Creating a Configuration File
ProGuard accepts arguments either on the command line or packaged in a
configuration file. Several examples are shown in the examples directory
of the ProGuard installation. Like most obfuscators, ProGuard needs only
a few pieces of information from you. For MIDlet suites it needs:
- The location of libraries. ProGuard needs to know where to find the MIDP APIs.
- The location of the input classfiles. ProGuard expects these to be
packaged in one or more JARs.
- A destination for the obfuscated classfiles. ProGuard generates a second JAR.
- Classes that should not be obfuscated. Specify the MIDlet
classes themselves; if they're obfuscated, the application
manager software won't be able to find them.
For the PeekAndPick 2.0 MIDlet suite I used the following configuration
file, peekandpick.pro:
-libraryjars /wtk104/lib/midpapi.zip
-injars proguard/PeekAndPick20-input.jar
-outjar proguard/PeekAndPick20-output.jar
-keep public class * extends javax.microedition.midlet.MIDlet
These configuration files are pretty powerful. Notice how the * character
tells ProGuard to leave all MIDlet classes untouched, sparing me the need
to name each MIDlet explicitly.
Running ProGuard From the Command Line
When you run ProGuard, you must supply several pieces of information.
-
Your
CLASSPATH must include the ProGuard classes.
-
The name of the ProGuard class is
proguard.ProGuard .
-
You need to tell ProGuard the name of your configuration file.
The following example (from Windows) runs ProGuard using
peekandpick.pro. Note that you could specify configuration options on the
command line instead.
C:\>set proguard_jar="\Program Files\proguard1.2\lib\proguard.jar"
C:\>java -jar %proguard_jar% @peekandpick.pro
ProGuard, version 1.2
Reading library jar [/wtk104/lib/midpapi.zip]
Reading program jar [proguard/PeekAndPick20-input.jar]
Writing output jar [proguard/PeekAndPick20-output.jar]...
C:\>
|
Running ProGuard From an Ant Script
Ant is a great tool
for building projects. It enables you to combine separate steps like
compiling, preverifying, and packging into a single straightforward build
process. Ant makes it easy to insert extra steps like obfuscation
into the MIDP development cycle. For more information on Ant, see
Managing Wireless Builds with Ant.
Running ProGuard from Ant is similar to running it from the command line. The same
information is needed, but the syntax is different. The following example,
from PeekAndPick's build.xml script, shows the obfuscation step of the
build process. Here I pass arguments directly on the command line instead
of using an external configuration file.
<java fork="yes" classname="proguard.ProGuard"
classpath="${proguard}">
<arg line="-libraryjars ${midp_lib}"/>
<arg line="-injars build/proguard/${project}-input.jar"/>
<arg line="-outjar build/proguard/${project}-output.jar"/>
<arg line="-keep 'public class * extends javax.microedition.midlet.MIDlet'"/>
</java>
|
In PeekAndPick 2.0, ProGuard saves an impressive 20%, cutting
the size of the
MIDlet suite JAR size from 48,235 bytes to 38,673.
Using ProGuard with the J2ME Wireless Toolkit
The J2ME Wireless Toolkit, starting with version 1.0.4, provides a mechanism for
including an obfuscator in the build cycle. Support for RetroGuard is packaged
with the toolkit, but it will accept any obfuscator if you
supply a little glue.
The basic technique is to write an implementation of
com.sun.kvem.environment.Obfuscator that invokes the obfuscator of
your choice. The Obfuscator interface declares two methods:
You can use the following class to integrate ProGuard into the J2ME Wireless
Toolkit. Note that, like the RetroGuard integration class, it assumes that
proguard.jar is present in the bin directory of the J2ME Wireless
Toolkit. (You may download this file
if you wish.)
import java.io.*;
import com.sun.kvem.environment.Obfuscator;
public class ProGuardWTKGlue
implements Obfuscator {
public void createScriptFile(
File jadFilename, File projectDir) {
try {
File scriptFile = new File(projectDir, "script.pro");
OutputStream rawOut = new FileOutputStream(scriptFile);
PrintWriter out = new PrintWriter(rawOut);
out.println("-keep public class * extends " +
"javax.microedition.midlet.MIDlet");
out.flush();
out.close();
}
catch (IOException ioe) {
System.out.println(ioe);
}
}
public void run(File jarFileObfuscated, String wtkBinDir,
String wtkLibDir, String jarFilename,
String projectDir, String classPath,
String emptyApi) {
// Create the command to run ProGuard.
StringBuffer buffer = new StringBuffer();
buffer.append("java -jar ");
buffer.append(wtkBinDir);
buffer.append("proguard.jar ");
buffer.append("-libraryjars ");
buffer.append(wtkBinDir);
buffer.append("..");
buffer.append(File.separatorChar);
buffer.append("lib");
buffer.append(File.separatorChar);
buffer.append("midpapi.zip ");
buffer.append("-injars ");
buffer.append(jarFilename);
buffer.append(' ');
buffer.append("-outjar ");
buffer.append(jarFileObfuscated.getPath());
buffer.append(' ');
buffer.append('@');
buffer.append(projectDir);
buffer.append(File.separatorChar);
buffer.append("script.pro");
String command = buffer.toString();
System.out.println(command);
// Run the ProGuard command.
Runtime runtime = Runtime.getRuntime();
try {
Process p = runtime.exec(command);
redirect(p.getErrorStream(), System.out);
redirect(p.getInputStream(), System.out);
}
catch (IOException ioe) {
System.out.println(ioe);
}
// Now add the resource files to the JAR.
buffer = new StringBuffer();
buffer.append("jar uvf ");
buffer.append(jarFileObfuscated.getPath());
buffer.append(" -C ");
buffer.append(projectDir);
buffer.append(File.separatorChar);
buffer.append("res .");
command = buffer.toString();
System.out.println(command);
// Run the jar command.
try {
Process p = runtime.exec(command);
redirect(p.getInputStream(), System.out);
}
catch (IOException ioe) {
System.out.println(ioe);
}
}
private void redirect(InputStream rawIn, OutputStream rawOut)
throws IOException {
BufferedReader in = new BufferedReader(
new InputStreamReader(rawIn));
PrintWriter out = new PrintWriter(rawOut);
String line;
while ((line = in.readLine()) != null)
out.println(line);
in.close();
out.flush();
}
}
|
There's nothing exotic about this class. It creates the same command
string you would enter manually and executes the command. Output from the obfuscator is
piped into the console window. Because ProGuard does not pass resource
files through to the output file, the second
part of the run() method adds them by invoking jar .
To compile this class, you'll need to include wtklib/kenv.zip from the
J2ME Wireless Toolkit directory in your classpath. Once you've successfully
compiled ProGuardWTKGlue , you need to tell the toolkit about it by
editing the ktools.properties file. On Windows, this file is located in
the wtklib/Windows directory under the J2ME Wireless Toolkit directory.
This file tells the toolkit both the name and the location
of the obfuscator glue class.
Edit the two obfuscator.runner properties as follows.
Adjust the obfuscator.runner.classpath as appropriate
for your system.
obfuscator.runner.class.name: ProGuardWTKGlue
obfuscator.runner.classpath: c:\\projects\\ProGuardWTKGlue
|
Restart the J2ME Wireless Toolkit and open a project. When you choose
Project > Package > Create Obfuscated Package, the
toolkit will use the class
you just installed to obfuscate the package. Assuming everything is
set up correctly, you should see the output of the glue class in the console
window of the J2ME Wireless Toolkit, including the output of both the
obfsucator and the jar command.
Summary
ProGuard is a powerful, free tool for optimizing your MIDlet's code size. Its
friendly license, attractive price tag, compelling perfomance, and powerful
configuration options make it an excellent addition to your MIDlet development
toolbox. ProGuard can easily be integrated into a build cycle based on Ant or
the J2ME Wireless Toolkit.
About the Author: Jonathan Knudsen
[e-mail]
[home page]
is the author of several books,
including
Wireless Java (second edition),
The Unofficial Guide to LEGO MINDSTORMS Robots,
Learning Java (second edition), and
Java 2D Graphics.
Jonathan has written
extensively about Java and Lego robots,
including articles for JavaWorld, EXE, NZZ Folio,
and the O'Reilly Network.
Jonathan
holds a degree in mechanical engineering from Princeton University.
Back To Top
|