Inability to load class definition from jar

I ran into a problem while trying to port an application to JApplet so that it could run in a browser.

Program Content:

  • Jar file. Contains an implementation of CustomClassLoader . Saved on the website.
  • Content Directory Filled with compiled classes. It is saved on the user's computer.

Question:

I get a NoClassDefFoundError when I try to load .class files into the content directory using CustomClassLoader .

The error, although unattainable, belongs to the class inside the jar. The class is abstract. All .class files in the content directory extend this class and populate all the necessary methods. An error occurs while loading these classes. A program that works fine java -jar file.jar works fine.

It makes me think this is related to the classpath.

Security Setting:

I run the applet through the appletviewer command as follows:

  appletviewer -J-Djava.security.policy=policy file.html 

In the same directory is the policy file:

 grant { permission java.lang.RuntimePermission "getenv.APPDATA"; permission java.io.FilePermission "<<ALL FILES>>", "read, write, delete, execute"; permission java.lang.RuntimePermission "exitVM"; permission java.util.PropertyPermission "user.name", "read"; permission java.lang.RuntimePermission "createClassLoader"; }; 

As far as I know, no other security exceptions are thrown. The applet is signed.

HTML file used to download the applet:

 <!DOCTYPE html> <html> <body> <object width="1000" height="600" classid="clsid:8AD9C840-044E-11D1-B3E9-00805F499D93" codebase="http://java.sun.com/products/plugin/autodl/jinstall-1_4-windows-i586.cab#Version=1,4,0,0"> <param name="archive" value="file.jar"/> <param name="code" value="package.to.Boot"/> </object> </body> </html> 

Any help in fixing this issue is greatly appreciated.

CustomClassLoader.java:

 package org.obicere.cc.methods; import java.io.File; public class CustomClassLoader extends ClassLoader { //... private Class<?> loadClass(final File file) { try { final byte[] data = IOUtils.readData(file); return super.defineClass(file.getName().substring(0, file.getName().length() - 6), data, 0, data.length); } catch (Exception e) { e.printStackTrace(); } return null; } } 

Runner example: CanReachRunner.java

 import java.lang.reflect.Method; import java.util.Random; import org.obicere.cc.executor.Result; import org.obicere.cc.tasks.projects.Runner; public class CanReachRunner extends Runner { @Override public Result[] getResults(Class<?> clazz) { try { final Method method = clazz.getMethod("canReach", int.class, int.class, int.class); final Random ran = new Random(); final Result[] results = new Result[10]; for (int i = 0; i < 10; i++) { final int small = ran.nextInt(5) + 5; final int large = ran.nextInt(5); final int goal = (small + large * 5) + 5 + ran.nextInt(6); results[i] = new Result(method.invoke(clazz.newInstance(), small, large, goal), (goal <= small + large * 5) && goal % 5 <= small, small, large, goal); } return results; } catch (Exception e) { return new Result[] {}; } } } 
+6
source share
1 answer

There are several errors in the class loader. First, the loadClass method uses the String argument, not a File , with the string being the name of the loaded class. This is because the class for loading may not be in the file, it may be connected to the network, and in any case, the JVM does not know how to find the file. Secondly, the wrong practice redefines loadClass , because if you do this, it interferes with the default behavior, which first tries to load classes normally and only findClass to calling the findClass method, if that is not the case, that works. Thus, you must override findClass instead of defineClass . Here's the updated code:

 public class CustomClassLoader extends ClassLoader { private Class<?> findClass(String class) { try { File contentDir = ...; // You have to fill this in with the location of the content dir final byte[] data = IOUtils.readData(new File(contentDir, class + ".class"); return defineClass(class, data, 0, data.length); } catch (Exception e) { e.printStackTrace(); return null; } } } 

You must find the content directory in some way and use it to initialize the contentDir .

The reason this works when run as a jar is because it is able to load classes without the need for a special class loader.

+4
source

Source: https://habr.com/ru/post/950614/


All Articles