无法从jar加载类定义

时间:2013-07-30 10:27:48

标签: java applet securitymanager appletviewer

我在尝试将应用程序移植到JApplet时遇到了一个问题,因此它可以在浏览器上运行。

计划内容:

  1. Jar文件。包含我的CustomClassLoader实现。存储在网站上。
  2. 内容目录。填写编译的类。存储在用户计算机上。
  3. 问题:

    尝试使用NoClassDefFoundError在内容目录中加载.class文件时,我收到CustomClassLoader

    错误虽然无法实现,但却与jar内的一个类有关。这门课是抽象的。内容目录中的所有.class文件都扩展此类并填充所有必需的方法。加载这些类后,将引发错误。该程序在正常运行java -jar file.jar时运行正常。

    这让我相信它与类路径有关。

    安全设置:

    我正在通过appletviewer命令运行applet,如下所示:

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

    在同一目录中是我的政策文件:

    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";
    };
    

    据我所知,没有其他安全例外被抛出。小程序已签名。

    用于加载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>
    

    非常感谢任何有关解决此问题的帮助。

    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;
        }
    }
    

    示例亚军: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[] {};
            }
        }
    }
    

1 个答案:

答案 0 :(得分:4)

类加载器有几个问题。第一个是loadClass方法使用String而不是File的参数,该字符串是要加载的类的名称。这是因为要加载的类可能不在文件中,它可能在网络连接上,并且无论如何JVM都不知道如何查找文件。第二个是覆盖loadClass是不好的做法,因为如果这样做,它会干扰默认行为,它首先尝试以正常方式加载类,并且仅调用findClass方法如果这不起作用。因此,您应该覆盖findClass而不是defineClass。这是更新后的代码:

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;
        }
    }
}

您必须以某种方式找到内容目录并使用它来初始化contentDir

当作为jar运行时,它的工作原理是因为它能够加载类而无需自定义类加载器。