Why doesn't the ProGuard output work?

Hello!

I’m using ProGuard to shrink my exported game’s jar from a 1.14 MiB to 193 KiB.

However when I run the shrunk jar file I get the following exception:


C:\Users\Me\Desktop>"C:\Program Files (x86)\Java\jre6\bin\java.exe" -jar test-
fix.jar localhost
Exception in thread "AWT-EventQueue-0" java.lang.RuntimeException: java.lang.NoS
uchMethodException: com.sun.opengl.impl.windows.k.create(java.nio.ByteBuffer)
        at com.sun.opengl.impl.JAWT_DrawingSurfaceInfo.a(Unknown Source)
        at com.sun.opengl.impl.JAWT_DrawingSurfaceInfo.a(Unknown Source)
        at com.sun.opengl.impl.windows.i.d(Unknown Source)
        at com.sun.opengl.impl.windows.b.b(Unknown Source)
        at com.sun.opengl.impl.f.a(Unknown Source)
        at com.sun.opengl.impl.g.a(Unknown Source)
        at javax.media.opengl.q.a(Unknown Source)
        at javax.media.opengl.q.d(Unknown Source)
        at javax.media.opengl.q.paint(Unknown Source)
        at sun.awt.RepaintArea.paintComponent(Unknown Source)
        at sun.awt.RepaintArea.paint(Unknown Source)
        at sun.awt.windows.WComponentPeer.handleEvent(Unknown Source)
        at java.awt.Component.dispatchEventImpl(Unknown Source)
        at java.awt.Component.dispatchEvent(Unknown Source)
        at java.awt.EventQueue.dispatchEvent(Unknown Source)
        at java.awt.EventDispatchThread.pumpOneEventForFilters(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForFilter(Unknown Source)
        at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.pumpEvents(Unknown Source)
        at java.awt.EventDispatchThread.run(Unknown Source)
Caused by: java.lang.NoSuchMethodException: com.sun.opengl.impl.windows.k.create
(java.nio.ByteBuffer)
        at java.lang.Class.getMethod(Unknown Source)
        ... 21 more

I can run the jar fine when it is not passed through ProGuard. As you see I’m using Jogl.

ProGuard is warning me about some stuff when I made “test-fix.jar”, but I don’t know what it means:


ProGuard, version 4.3
Reading program jar [C:\Users\Me\Desktop\test.jar]
Reading library jar [C:\Program Files (x86)\Java\jre6\lib\rt.jar]
Note: com.sun.opengl.util.JOGLAppletLauncher$6: can't find dynamically referenced class net.java.games.joal.impl.NativeLibLoader
Note: com.sun.opengl.impl.x11.X11SunJDKReflection$1: can't find dynamically referenced class sun.awt.X11GraphicsDevice
Note: com.sun.opengl.impl.x11.X11SunJDKReflection$1: can't find dynamically referenced class sun.awt.X11GraphicsConfig
Note: com.sun.gluegen.runtime.NativeLibLoader: can't find dynamically referenced class org.jdesktop.applet.util.JNLPAppletLauncher
Note: com.sun.opengl.util.JOGLAppletLauncher calls '(java.applet.Applet)Class.forName(variable).newInstance()'
Note: com.sun.opengl.impl.Java2D$2: can't find dynamically referenced class sun.java2d.opengl.OGLUtilities
Note: com.sun.opengl.impl.Java2D$1: can't find dynamically referenced class sun.java2d.opengl.OGLUtilities
Note: com.sun.opengl.impl.Java2D$1: can't find dynamically referenced class sun.java2d.opengl.CGLSurfaceData
Note: com.sun.opengl.impl.NativeLibLoader: can't find dynamically referenced class org.jdesktop.applet.util.JNLPAppletLauncher
Note: com.sun.opengl.util.JOGLAppletLauncher$6 accesses a method 'begin()' dynamically
      Maybe this is program method 'com.sun.opengl.impl.x11.DRIHack { void begin(); }'
      Maybe this is library method 'java.nio.channels.spi.AbstractInterruptibleChannel { void begin(); }'
      Maybe this is library method 'java.nio.channels.spi.AbstractSelector { void begin(); }'
Note: com.sun.opengl.util.JOGLAppletLauncher$6 accesses a method 'end()' dynamically
      Maybe this is program method 'com.sun.opengl.impl.x11.DRIHack { void end(); }'
      Maybe this is program method 'javax.media.opengl.glu.GLUtessellatorCallback { void end(); }'
      Maybe this is program method 'javax.media.opengl.glu.GLUtessellatorCallbackAdapter { void end(); }'
      Maybe this is library method 'java.awt.PrintJob { void end(); }'
      Maybe this is library method 'java.nio.channels.spi.AbstractSelector { void end(); }'
      Maybe this is library method 'java.util.regex.MatchResult { int end(); }'
      Maybe this is library method 'java.util.regex.Matcher { int end(); }'
      Maybe this is library method 'java.util.zip.Deflater { void end(); }'
      Maybe this is library method 'java.util.zip.Inflater { void end(); }'
      Maybe this is library method 'javax.swing.undo.CompoundEdit { void end(); }'
      Maybe this is library method 'javax.swing.undo.StateEdit { void end(); }'
      Maybe this is library method 'javax.swing.undo.UndoManager { void end(); }'
      Maybe this is library method 'sun.font.LayoutPathImpl { double end(); }'
      Maybe this is library method 'sun.font.LayoutPathImpl$EmptyPath { double end(); }'
      Maybe this is library method 'sun.font.LayoutPathImpl$SegmentPath { double end(); }'
      Maybe this is library method 'sun.print.PrintJob2D { void end(); }'
Note: com.sun.opengl.util.JOGLAppletLauncher$6 accesses a method 'disableLoading()' dynamically
      Maybe this is program method 'com.sun.gluegen.runtime.NativeLibLoader { void disableLoading(); }'
      Maybe this is program method 'com.sun.opengl.impl.NativeLibLoader { void disableLoading(); }'
Note: com.sun.opengl.impl.x11.X11SunJDKReflection$1 accesses a declared method 'getScreen()' dynamically
      Maybe this is library method 'sun.awt.Win32GraphicsDevice { int getScreen(); }'
Note: com.sun.opengl.impl.x11.X11SunJDKReflection$1 accesses a declared method 'getVisual()' dynamically
      Maybe this is library method 'sun.awt.Win32GraphicsConfig { int getVisual(); }'
Note: com.sun.opengl.impl.Java2D$1 accesses a declared method 'isQueueFlusherThread()' dynamically
      Maybe this is program method 'com.sun.opengl.impl.Java2D { boolean isQueueFlusherThread(); }'
      Maybe this is library method 'sun.java2d.opengl.OGLRenderQueue { boolean isQueueFlusherThread(); }'
Note: there were 8 unresolved dynamic references to classes or interfaces.
      You should check if you need to specify additional program jars.
Note: there were 1 class casts of dynamically created class instances.
      You might consider explicitly keeping the mentioned classes and/or
      their implementations (using '-keep').
Note: there were 6 accesses to class members by means of introspection.
      You should consider explicitly keeping the mentioned class members
      (using '-keep' or '-keepclassmembers').
Preparing output jar [C:\Users\Me\Desktop\test-fix.jar]
  Copying resources from program jar [C:\Users\Me\Desktop\test.jar]
Processing completed successfully

In the end it completes successfully. It suggests that I should consider using “-keep” or “-keepclassmembers” but I don’t know where I should write that, or what exactly what I should write.

I’m using ProGuard 4.3 GUI-version.

Anybody can solve this mystery?

Using obfuscators/optimizers is advanced area, I don’t know GUI version, but best way to use ProGuard is by using configuration files. There are some templates for common app types. Also not all classes can be renamed, eg. when they are instantized by reflection or when native methods are present (like in JOGL).

In your case I recommend to not obfuscate JOGL classes, maybe just remove unused methods but that can be tricky. For other parts of code, ProGuard outputs various warnings that can help, best is to really know how each library that you’re obfuscating works, if they use reflection or not, etc.

Besides using ProGuard you can also save good amount of space by using Pack200 compression for classes. This type of compression can be used only for saving download size because of it’s nature it can’t be directly loaded by JVM. So applet/webstart engine downloads it and uncompress to normal JAR before execution.

Thanks for the reply!

Do you know where I can find a ProGuard configuration file for a Java Application that uses JOGL? I really need to get this to work since it not only make the code smaller - it also protects it from being reverse engineered which helps me sleep at night. I don’t know how to build a config file myself, I’m new to ProGuard and have only used the GUI version.

I will search for and try to check out Pack200 after I’ve got the ProGuard output to work, thanks for the tip.

I would not obfuscate JOGL at all. Because you would have to also update native methods somehow and recompile for each platform… Also remember that obfuscation is not strong protection :slight_smile: Java is very highlevel so protecting code is problematic, rather focus on other areas as protecting is not much good even with lowlevel languages and very interesting protection schemes (every game/app that is popular enough gets cracked).

Well I know that the protection isn’t a true protection, I just want to make the code so that nobody will have the willpower to actually understand what they have decompiled. And if they do somehow make use of my code after it’s been obfuscated, I think they have earned it. ;D If someone actually bothers I can feel proud over that I must have made a decent game.

So… how do I not obfuscate JOGL at all then? I export my game using Eclipse to a runnable Jar, from this I get only 1 jar file. Then I select that jar in ProGuard and process it. How can I tell it to let all JOGL stuff be?

I do not do anything visual with OpenGL (only GPGPU), but I have used Proguard with JOGL. It is hard to help you, when you did not include your Proguard configuration file.

First, you need to specify the JOGL libraries with a path:
-libraryjars path/jogl.jar -libraryjars path/gluegen-rt.jar
You are have to be doing this already, or Proguard would probably not even work.

Wait, looking at the error message, just a wild guess, are you specifying jogl.jar and gluegen-rt.jar as -injars? There probably is no class in com.sun.opengl.impl.windows called k, but I am too lazy to confirm.

If I am correct above, actually putting the jogl libraries into the final jar obviously causes a lot of issues to fight through. It also ties you to a specific platform. Do you plan on only deploying to the Windows platform?

If you goal is just ruining would be copycatter’s day, specifying
# jam everything down to into 1 package, '_' -repackageclasses _ -overloadaggressively does a pretty good job.

As I mentioned I’m using the GUI version and can’t edit the config file directly, I can view it before pressing the process button though. I don’t know how to use ProGuard with a config file.

How does placing the libraries in the final jar tie me to Windows? I thought it was the binaries that only needed switching between systems. (libraries = jar files, binaries = dll files)

All I’m doing is export my project to one runnable jar using Eclipse. Then I want to shrink and obfuscate that jar with ProGuard. Are you telling me I can’t include everything (except the binaries of course) in one jar? I have never used separate jar files before, I always export everything into one runnable jar file since that is cleaner compared to having a lot of files.

“If you goal is just ruining would be copycatter’s day, specifying”
Having some trouble reading your English there but I guess what you’re saying is that by using a config file with those three lines would obfuscate and put everything in one package? No idea how I should use that.

Lets handle the easiest of your questions first. repackaging & overloading. A Package hierarchy is useful information. Why else would anyone make them? This also makes it invaluable for reverse engineering. Unless you specify to repackage, only the classes get renamed, so you might end up with something like:
controls.a
controls.b
controls.c
controls.keyboard.a
surface.a

Repackaging into a package, say ‘q’, would yield just q.a - q.e, making it a bigger job to make out how a system works. On the proguardgui.jar program you are using, it is specified on the Obfuscation Tab.

Unless you specify overloadaggressively, a class with 26 methods would get renamed a-z. With overloadaggressively, unless the arguments are the same, they all will get named a. Not going to stop anyone from decompiling and “reading” your source. Understanding is another matter. It too is specified on the Obfuscation tab. FYI, Android applications Will not work, when this is used.


Next easiest is your lack of configuration file. The proguardgui.jar program provides for not only viewing the configuration file generated, but saving it as well. You can also load a previously saved configuration file from the Proguard tab to avoid specifying things every time.


Finally, Proguard is always much easier when you are dealing with all your own code, because you generally know if you are doing any classForNames, native methods, callbacks & can make sure those get both preserved and unchanged. When dealing with the jars of other’s things can get really complicated. JOGL is a very indirect mechanism. JOGL.JAR has classes for all the OS’s (linux, mac, solaris, & windows). Temporarily change the extension to ZIP, and browse with Explorer to see for yourself.

If you got Proguard to work with JOGL.JAR you would have make sure the classes of the other os’s were kept to be multi-platform. Seeing all the messages the Proguard session produced does not bode well for even getting one system to work. Part of the reason you got such great compression is it got rid of probably everything, because nothing is being called directly by gluegen, so proguard gets rid of it. This is very nice for cleaning out unused code from libraries, but that unused code is the other platforms, in this case.


I would recommend that in Eclipse you opt for generating a jar of just your own code. Never used it, so you are on your own there. Then using proguardgui.jar, specify your jar as in, and JOGL & gluegen-rt as libraries. Then distribute the output jar and a JavaWebstart jnlp file. No other jars or dlls AT ALL!! JavaWebstart does everything for you on all platforms, including Windows i586 & Windows amd64.

You can even use JavaWebstart, when you are not using the web for distribution. Just put the 2 files, and maybe an Icon into a directory, my_product, and tell people to install to c:\ .

In your jnlp put a tag like:
.

There is more to it than that, but there are other threads here and at sun which talk about this.

Thank you very much, I’m glad someone is trying to help me in detail!

I’m don’t know what you meant by Android applications?

Also the jnlp file, how will it make the game work without DLL files? Say one want to play the game on a computer without internet so nothing can be downloaded by jnlp, won’t a exception be thrown as soon as the game tries to draw a frame in JOGL since the binaries are missing? I have never made a jnlp file before, but am willing to learn since people will be able to play my game online with one click. Maybe you have a link to give me so I can build a proper jnlp file? It would be great if the user don’t have to place it in a fixed place on the hard disk also, since in some places they won’t be allowed to do so (such as schools).

I’ll try to export only my code in Eclipse now, however I still don’t feel confident in this area.

It was a lot of work and in the end I failed.

By using this tutorial: http://www.cokeandcode.com/webstarthowto

I made this JNLP file:

<?xml version="1.0" encoding="utf-8"?>
<!-- Test for Astro Prime Web Start Deployment -->
<jnlp spec="1.0+" codebase="file://localhost/C:/Users/Me/Desktop/test/" href="test.jnlp">
  <information>
    <title>Testing</title>
    <vendor>Me</vendor>
    <homepage href="http://www.webcrawler.com/"/>
    <description>This is a test.</description>
    <description kind="short">A test.</description>
    <icon href="testpic.gif"/>
    <icon kind="splash" href="testpic.gif"/>
    <offline-allowed/>
  </information>
  <security>
    <all-permissions/>
  </security>
  <resources>
    <j2se href="http://java.sun.com/products/autodl/j2se" version="1.4+"/>
    <jar href="test.jar"/>
    <jar href="jogl.jar"/>
    <jar href="gluegen-rt.jar"/>
  </resources>
  <resources os="Windows">
    <j2se href="http://java.sun.com/products/autodl/j2se" version="1.4+"/>
    <jar href="binaries.jar"/>
  </resources>
  <application-desc>
    <argument>localhost</argument>
  <application-desc>
</jnlp>

It launched, but crashed without any message (I just print stack trace on exception and exit the application). I guess it’s the same old problem, that OpenGL can’t be found.

I don’t really like this, first of all it doesn’t work second I need to distribute a lot of files (binaries.jar, gluegen-rt.jar, jogl.jar, test.jar, test.jnlp, testpic.gif - and that’s only with the win32 binaries!) and thirdly because I needed to sign the GLU and JOGL jar files which felt just wrong. Also the whole signing business was just a pain in the neck, and exporting a million jar files etc etc. And once I actually get this to work there’s no guarantee that ProGuard would not screw stuff up so it stops working.

Is there really just no way of just exporting a executable jar file like I used to and then tell ProGuard to skip everything that is GLU or JOGL related? There would be so much sweeter to just have to do three things before I can distribute instead of having to do thirty things.

First Android is a phone OS that just happens to not like that proguard option, disregard.

That website is fairly generic. The jogl - User’s guide, https://jogl.dev.java.net/source/browse/checkout/jogl/trunk/doc/userguide/index.html, has a section called ‘Java Web Start integration’. Quickly looking at your jnlp file, get rid of the security, and signing will not be required. Get rid of jar references to jogl & gluegen-rt, and do not put them in your distribution. I would get rid of section. You don’t have a binaries.jar, do you? Get rid of the localhost arg in application-desc, unless you are actually are reading argv[].

Now the stuff missing that you actually need:

  • The entry for JOGL in resources

  • Your application-desc does not have an entry point. This should be kept by Proguard with something like:
    -keep public class* {
    public static void main(java.lang.String[]);
    }
    How this is specified in the GUI version is your job.

  • I would rather be dead than require anything less than j2se version less than 1.5. I show what I am using, but am thinking about adding the nodraw arg as well, based on the Windows section of JOGL User Guide. You can probably default on heap size, I just need a lot myself.

The offline-allowed means the use network only required the first time. Nothing is perfect, but the ace up Webstart’s sleeve is: when the computer starts the program, it checks for updates automatically, and brings down any newer files. That means you can do enhancements and bug fixes, and they just show up! FYI, that localhost codebase thing is usually just used for testing.

Finally, there is a manual somewhere on Webstart, & a console you can turn on for Webstart for testing. Goto the advanced Tab of the Java Control Panel for the console.

Hope that is enough help to get you thru. Your on your own. Good luck.

<?xml version="1.0" encoding="utf-8"?>
<!-- Test for Astro Prime Web Start Deployment -->
<jnlp spec="1.0+" codebase="file://localhost/C:/Users/Me/Desktop/test/" href="test.jnlp">
  <information>
    <title>Testing</title>
    <vendor>Me</vendor>
    <homepage href="http://www.webcrawler.com/"/>
    <description>This is a test.</description>
    <description kind="short">A test.</description>
    <icon href="testpic.gif"/>
    <icon kind="splash" href="testpic.gif"/>
    <offline-allowed/>
  </information>
  
  <resources>
    <j2se version="1.5" max-heap-size="200m" java-vm-args="-Dapple.awt.graphics.UseQuartz=true"/>
    <jar href="test.jar"/>
    <extension name="jogl" href="http://download.java.net/media/jogl/builds/archive/jsr-231-webstart-current/jogl.jnlp" />
  </resources>
  <application-desc main-class="mypackage.My_Main">
  <application-desc>
</jnlp>

Thanks for the info, appreciate it.

My binaries.jar was created from the Windows 32 JOGL binaries “jogl.dll” and “jogl_awt.dll”. The “localhost” argument was for connecting to a server that was on my own computer.

About the permissions, I will need files to be written to disk (player profile data for example) and for that I guess I need special permissions. Where would these files even be saved if someone ran the jnlp file from a home page?

Can the application be run from a computer with no Internet? Like if somebody download the game RAR archive from my site, then put it on a disk and takes the disk over to a isolated computer without a network cord, then extracts the game there and tries to play it in single player. If the DLL files needs to be downloaded I don’t think a jnlp file is the right solution for me. It can be noted that I don’t really need the game web startable, I want the players to always run it from a game folder on their computer.

Which I guess brings us back to the original purpose of the thread, how do I use ProGuard to shrink and obfuscate my exported game’s runnable JAR file? How can I tell ProGuard to not touch the classes that I haven’t written myself - if only my code gets shrunk and obfuscated the JOGL part of the application shouldn’t break, right?

I’m very much willing to learn how to use ProGuard “properly” (without the GUI), but I’d like to have somewhere to start. Does anyone have a example ProGuard script that shrinks/obfuscates a selected class inside a jar file?