简体   繁体   English

无法从jar加载类定义

[英]Failing to load class definition from jar

I ran across an issue when attempting to port an application over to JApplet so it can run on a browser. 尝试将应用程序移植到JApplet时,我遇到了一个问题,因此它可以在浏览器上运行。

Program Contents: 课程内容:

  1. Jar file. Jar文件。 Contains my CustomClassLoader implementation. 包含我的CustomClassLoader实现。 Stored on website. 存储在网站上。
  2. Content directory. 内容目录。 Filled with compiled classes. 填写编译的类。 Stored on the users computer. 存储在用户计算机上。

Issue: 问题:

I am getting a NoClassDefFoundError when attempting to load .class files in the content directory with my CustomClassLoader . 尝试使用CustomClassLoader在内容目录中加载.class文件时,我得到NoClassDefFoundError

The error, although unattainable, relates back to a class inside the jar. 这个错误虽然无法实现,但却与jar中的一个类有关。 The class is abstract. 这门课是抽象的。 All the .class files in the content directory extend this class and fill all the required methods. 内容目录中的所有.class文件都扩展此类并填充所有必需的方法。 Upon loading these classes, the error is thrown. 加载这些类后,将引发错误。 The program, when ran normally java -jar file.jar , works perfectly fine. 程序,当正常运行java -jar file.jar ,工作得很好。

This makes me believe it has to do with the classpath. 这让我相信它与classpath有关。

Security Setup: 安全设置:

I am running the applet through the appletviewer command like so: 我通过appletviewer命令运行applet,如下所示:

 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: 用于加载Applet的HTML文件:

<!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: 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 示例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. 第一个是loadClass方法使用String的参数而不是File ,该字符串是要加载的类的名称。 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. 这是因为要加载的类可能不在文件中,它可能在网络连接上,并且无论如何JVM都不知道如何查找文件。 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. 第二个是覆盖loadClass是不好的做法,因为如果这样做,它会干扰默认行为,它首先尝试以正常方式加载类,并且只有在不起作用的情况下才会调用findClass方法。 So, you should override findClass instead of defineClass . 因此,您应该覆盖findClass而不是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 . 您必须以某种方式找到内容目录并使用它来初始化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. 这个作为jar运行时的原因是因为它能够加载类而无需自定义类加载器。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM