To load protected resources through a special ClassLoader
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:
- Program could not load the bytes of a protected ".class" file, such as by a 'ClassLoader.getResourceAsStream("a/b/c.class");'.
- 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.
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.
// 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();
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:
- At step 6, click the "Config Internal" button.
Then at the "Custom" tab of popup "Config Assistant", add this line:
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.
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.
The default value is 'true', so if you want to let 'getSystemClassLoader()' not to be the 'Jar2ExeClassLoader', please use 'false' explicitly.