Protected Resource

To load protected resources through a special ClassLoader


Introduction

To care about the ClassLoader is to resolve load problems of protected resources.

Usually a protected program should run normally. The problem may occur when it is to load protected resources within a un-protected class.

To resolve this type of problems, you can try to select Thread .currentThread .getContextClassLoader() instead of ClassLoader of current class.


Effects to Your Program

The protection by Jar2Exe will bring 2 changes to your java program:

  1. Program could not load the bytes of a protected ".class" file, such as by a 'ClassLoader.getResourceAsStream("a/b/c.class");'.
  2. Program could not enumerate the filenames within a folder in a protected jar file. If some framework try to open a protected jar by ZipFile class, it will fail.

If your spring is configured like "classpath:a/b/*.xml", it will fail and you need to configure them one by one explicitly.


Why Load Failed

Every class has a ClassLoader which has loaded the class.

The ClassLoader of protected classes is a special ClassLoader, while the ClassLoader of un-protected classes is another ClassLoader. When the program is to load a class or resource, it will use the ClassLoader of current class by default, such as "Class.forName()".

So if the program is to load a protected resource within un-protected class, the ClassLoader of the un-protected class cannot load the protected resources.


Resolve

When a generated exe runs, the context ClassLoader of current thread is the Special ClassLoader.

So when you meet load failure of protected resources, if the current class is un-protected, try to use the Thread .currentThread() .getContextClassLoader() instead of ClassLoader of current class.

For example:

// To load a protected class by Class.forName()
Class.forName("hello.HelloWorld", true, Thread.currentThread().getContextClassLoader());

// Or by ClassLoader.loadClass()
Thread.currentThread().getContextClassLoader().loadClass("hello.HelloWorld");

// Load a resource
Thread.currentThread().getContextClassLoader().getResource("hello/yes.gif");

// Or construct a URL directly
URL yes = new URL("j2e:" + "hello/yes.gif");
InputStream in = yes.openStream();

Compatible Mode[since 2.1.3]

In certain cases, if your program cannot run after encryption, you can try to enable 'compatible' mode to encrypt your program in another more compatible style. Some third-party libraries may need this mode if they will control the ClassLoaders much.

The known issue which brings compatible mode is that, the EclipseLink JPA library needs the 'compatible' mode, or it will fail when finding model classes.

To enable 'compatible' mode, you need to add “compatible true” in the “Internal config” of generated exe. That is:

  1. At step 6, click the "Config Internal" button.
  2. Then at the "Custom" tab of popup "Config Assistant", add this line:
    compatible true
    

Please note that, this special line can only be added to “Internal config”, it will not work when it is outside in the xxx.config file.


To control the 'getSystemClassLoader()'[since 2.1.4]

By default, when an encrypted binary file runs, the 'getSystemClassLoader()' is an instance of the special classloader 'Jar2ExeClassLoader'. In certain cases, if your program needs to let the 'getSystemClassLoader()' to be the original ClassLoader from JRE, you can use a configure line to turn this off.

To turn this off, you can use “assystemclassloader false” in the config file.

assystemclassloader false

The default value is 'true', so if you want to let 'getSystemClassLoader()' not to be the 'Jar2ExeClassLoader', please use 'false' explicitly.


See Also

Comments

Issue with protected resources.

I'm trying to do this command (Thread.currentThread().getContextClassLoader().getResource("/test.tep"); and using it with runtime.exec that runs a cmd command and then it just reads the bytes. Is there a way to fix? the file is in resources.

Add new comment