I ran across an issue when attempting to port an application over to JApplet
so it can run on a browser.
Program Contents:
CustomClassLoader
implementation. Stored on website.Issue:
I am getting a NoClassDefFoundError
when attempting to load .class files in the content directory with my CustomClassLoader
.
The error, although unattainable, relates back to a class inside the jar. The class is abstract. All the .class files in the content directory extend this class and fill all the required methods. Upon loading these classes, the error is thrown. The program, when ran normally java -jar file.jar
, works perfectly fine.
This makes me believe it has to do with the classpath.
Security Setup:
I am running the applet through the appletviewer
command like so:
appletviewer -J-Djava.security.policy=policy file.html
In the same directory is my 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 being thrown. The applet is signed.
HTML File Used To Load 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 towards fixing this problem 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;
}
}
Example Runner: 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[] {};
}
}
}
There are several things wrong with the class loader. The first is that the loadClass
method uses an argument of a String
rather than a File
, the string being the name of the class to load. This is because the class to load might not be in a file, it might be on a network connection, and anyway the JVM doesn't know how to find the file. The second is that it is bad practice to override loadClass
, because if you do, it interferes with the default behavior, which first tries to load classes the normal way, and only resorts to calling the findClass
method if that doesn't work. So, you should 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 somehow and use that to initialize contentDir
.
The reason this works when run as a jar is cause it is then capable of loading the classes without needing a custom class loader.