类加载从包中生成NoClassDefFoundError。

huangapple go评论81阅读模式
英文:

Classloading from package spawns NoClassDefFoundError

问题

以下是您提供的内容的翻译:

我有一个小程序,试图通过将类文件复制到特定的 ext 目录来实现插件功能。

该程序派生自 https://javaranch.com/journal/200607/Plugins.html,我尝试简化它并添加了一个目录扫描功能,以扫描原始代码缺少的包和目录。

在运行原始代码时,它可以正常工作。但是当我添加了目录和包扫描功能,并在演示包上进行测试时,它失败了。以下是示例。

系统接受动态加载的类文件作为插件的目录布局:

testpack-+
         |
         +---PluginDemo.java
         |
         +---PluginFunction.java

测试插件的目录布局:

b-+
  |
  +---Fibonacci.java

testpack-+
         |
         +---PluginFunction.java

带有自定义类加载器的 PluginDemo 代码:

package testpack;

import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.*;

public class PluginDemo extends ClassLoader {

    static String pluginsDir = "ext";
    static File basePluginDir = null;
    File directory;

    static List plugins;

    public static void main(String args[]) {
        PluginDemo demo = new PluginDemo();
        basePluginDir = new File(System.getProperty("user.dir") + File.separator + pluginsDir);
        demo.getPlugins(pluginsDir, "");
    }

    PluginDemo() {
        plugins = new ArrayList();
    }

    protected void getPlugins(String directory, String parent) {
        File dir = new File(System.getProperty("user.dir") + File.separator + directory);
        if (dir.exists() && dir.isDirectory()) {
            String[] files = dir.list();
            for (int i = 0; i < files.length; i++) {
                try {
                    // 允许递归地定位嵌套目录
                    String newTargetFile = System.getProperty("user.dir") + File.separator + directory + File.separator
                            + files[i];
                    System.out.println("目标: " + newTargetFile);

                    File currentTarget = new File(newTargetFile);
                    if (currentTarget.isDirectory()) {
                        String newDirectoryTarget = directory + File.separator + files[i];
                        getPlugins(newDirectoryTarget, files[i]);
                    }

                    if (!files[i].endsWith(".class"))
                        continue;

                    String childFile = parent + File.separator + files[i].substring(0, files[i].indexOf("."));
                    Class c = loadClass(childFile);
                    Class[] intf = c.getInterfaces();
                    for (int j = 0; j < intf.length; j++) {
                        if (intf[j].getName().equals("PluginFunction")) {
                            PluginFunction pf = (PluginFunction) c.newInstance();
                            plugins.add(pf);
                            continue;
                        }
                    }
                } catch (Exception ex) {
                    System.err.println(files[i] + " 文件不包含有效的 PluginFunction 类。");
                    ex.printStackTrace();
                }
            }
        }
    }

    public Class loadClass(String name) throws ClassNotFoundException {
        return loadClass(name, true);
    }

    public Class loadClass(String classname, boolean resolve) throws ClassNotFoundException {
        try {
            Class c = findLoadedClass(classname);
            if (c == null) {
                try {
                    c = findSystemClass(classname);
                } catch (Exception ex) {
                }
            }
            if (c == null) {
                String filename = classname.replace('.', File.separatorChar) + ".class";

                // 创建一个文件对象。将文件名相对于为此类加载器指定的目录进行解释。
                File baseDir = new File(System.getProperty("user.dir"));
                File f = new File(baseDir, PluginDemo.pluginsDir + File.separator + filename);
                int length = (int) f.length();
                byte[] classbytes = new byte[length];
                DataInputStream in = new DataInputStream(new FileInputStream(f));
                in.readFully(classbytes);
                in.close();
                c = defineClass(classname, classbytes, 0, length);
            }

            if (resolve)
                resolveClass(c);

            return c;
        } catch (Exception ex) {
            throw new ClassNotFoundException(ex.toString());
        }
    }
}

PluginFunction 接口代码:

package testpack;

public interface PluginFunction {

    // 允许应用程序传入参数
    public void setParameter(int param);

    // 从插件检索结果
    public int getResult();

    // 返回此插件的名称
    public String getPluginName();

    // 可以调用以确定插件是否由于错误条件中止执行
    public boolean hasError();
}

Fibonacci.java 代码:

package b;

import testpack.PluginFunction;

public class Fibonacci implements PluginFunction {

    int parameter = 0;
    boolean hasError = false;

    public boolean hasError() {
        return hasError;
    }

    public void setParameter(int param) {
        parameter = param;
    }

    public int getResult() {
        hasError = false;
        return fib(parameter);
    }

    protected int fib(int n) {
        if (n < 0) {
            hasError = true;
            return 0;
        }

        if (n == 0)
            return 0;
        else if (n == 1)
            return 1;
        else
            return fib(n - 1) + fib(n - 2);
    }

    public String getPluginName() {
        return "Fibonacci";
    }
}

带有错误输出的输出:

目标: C:\Users\Administrator\eclipse-workspace\TestPluginSystem\ext\b\Fibonacci.class
Exception in thread "main" java.lang.NoClassDefFoundError: b\Fibonacci (wrong name: b/Fibonacci)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at testpack.PluginDemo.loadClass(PluginDemo.java:89)
at testpack.PluginDemo.loadClass(PluginDemo.java:65)
at testpack.PluginDemo.getPlugins(PluginDemo.java:47)
at testpack.PluginDemo.getPlugins(PluginDemo.java:40)
at testpack.PluginDemo.main(PluginDemo.java:19)

我需要帮助让这个带有包和目录扫描功能的类加载器正常工作。谢谢。

英文:

I have a small program that attempts to allow plugins via class files copied to a specific ext directory.

The program is derived from https://javaranch.com/journal/200607/Plugins.html and I have attempted to simplify it and add on a directory scanning ability to scan packages and directories that the original code lacks.

When running the original code, it works. When I add on my directory and package scanning capability and test it on a demo package, it fails. Below are the samples.

The directory layout of the system accepting dynamically loaded class files as plugins:

 testpack-+
|
+---PluginDemo.java
|
+---PluginFunction.java

The test plugin's directory layout:

b-+
  |
  +---Fibonacci.java

testpack-+
         |
         +---PluginFunction.java

The PluginDemo code with custom class loader:

package testpack;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.util.*;
public class PluginDemo extends ClassLoader {
static String pluginsDir = &quot;ext&quot;;
static File basePluginDir = null;
File directory;
static List plugins;
public static void main(String args[]) {
PluginDemo demo = new PluginDemo();
basePluginDir = new File(System.getProperty(&quot;user.dir&quot;) + File.separator + pluginsDir);
demo.getPlugins(pluginsDir, &quot;&quot;);
}
PluginDemo() {
plugins = new ArrayList();
}
protected void getPlugins(String directory, String parent) {
File dir = new File(System.getProperty(&quot;user.dir&quot;) + File.separator + directory);
if (dir.exists() &amp;&amp; dir.isDirectory()) {
String[] files = dir.list();
for (int i = 0; i &lt; files.length; i++) {
try {
// Allows recursive targetting of nested directories
String newTargetFile = System.getProperty(&quot;user.dir&quot;) + File.separator + directory + File.separator
+ files[i];
System.out.println(&quot;Targetting: &quot; + newTargetFile);
File currentTarget = new File(newTargetFile);
if (currentTarget.isDirectory()) {
String newDirectoryTarget = directory + File.separator + files[i];
getPlugins(newDirectoryTarget, files[i]);
}
if (!files[i].endsWith(&quot;.class&quot;))
continue;
String childFile = parent + File.separator + files[i].substring(0, files[i].indexOf(&quot;.&quot;));
Class c = loadClass(childFile);
Class[] intf = c.getInterfaces();
for (int j = 0; j &lt; intf.length; j++) {
if (intf[j].getName().equals(&quot;PluginFunction&quot;)) {
PluginFunction pf = (PluginFunction) c.newInstance();
plugins.add(pf);
continue;
}
}
} catch (Exception ex) {
System.err.println(&quot;File &quot; + files[i] + &quot; does not contain a valid PluginFunction class.&quot;);
ex.printStackTrace();
}
}
}
}
public Class loadClass(String name) throws ClassNotFoundException {
return loadClass(name, true);
}
public Class loadClass(String classname, boolean resolve) throws ClassNotFoundException {
try {
Class c = findLoadedClass(classname);
if (c == null) {
try {
c = findSystemClass(classname);
} catch (Exception ex) {
}
}
if (c == null) {
String filename = classname.replace(&#39;.&#39;, File.separatorChar) + &quot;.class&quot;;
// Create a File object. Interpret the filename relative to the
// directory specified for this ClassLoader.
File baseDir = new File(System.getProperty(&quot;user.dir&quot;));
File f = new File(baseDir, PluginDemo.pluginsDir + File.separator + filename);
int length = (int) f.length();
byte[] classbytes = new byte[length];
DataInputStream in = new DataInputStream(new FileInputStream(f));
in.readFully(classbytes);
in.close();
c = defineClass(classname, classbytes, 0, length);
}
if (resolve)
resolveClass(c);
return c;
} catch (Exception ex) {
throw new ClassNotFoundException(ex.toString());
}
}
}

The PluginFunction interface code:

package testpack;
public interface PluginFunction {
// let the application pass in a parameter
public void setParameter (int param);
// retrieve a result from the plugin
public int getResult();
// return the name of this plugin
public String getPluginName();
// can be called to determine whether the plugin
// aborted execution due to an error condition
public boolean hasError();
}

The Fibonacci.java code:

package b;
import testpack.PluginFunction;
public class Fibonacci implements PluginFunction {
int parameter = 0;
boolean hasError = false;
public boolean hasError() {
return hasError;
}
public void setParameter (int param) {
parameter = param;
}
public int getResult() {
hasError = false;
return fib(parameter);
}
protected int fib (int n) {
if (n &lt; 0) {
hasError = true;
return 0;
}
if (n == 0)
return 0;
else if (n == 1)
return 1;
else
return fib(n-1) + fib(n-2); 
}
public String getPluginName() {
return &quot;Fibonacci&quot;;
}
}

The output with errors:

Targetting: C:\Users\Administrator\eclipse-workspace\TestPluginSystem\ext\b\Fibonacci.class
Exception in thread &quot;main&quot; java.lang.NoClassDefFoundError: b\Fibonacci (wrong name: b/Fibonacci)
at java.lang.ClassLoader.defineClass1(Native Method)
at java.lang.ClassLoader.defineClass(ClassLoader.java:760)
at java.lang.ClassLoader.defineClass(ClassLoader.java:642)
at testpack.PluginDemo.loadClass(PluginDemo.java:89)
at testpack.PluginDemo.loadClass(PluginDemo.java:65)
at testpack.PluginDemo.getPlugins(PluginDemo.java:47)
at testpack.PluginDemo.getPlugins(PluginDemo.java:40)
at testpack.PluginDemo.main(PluginDemo.java:19)

I would need help to get this package and directory scanning capable classloader working. Thanks.

答案1

得分: 1

看到错误和方法[ClassLoader.defineClass] 1,我认为name参数必须使用作为包分隔符,而不是/\

在您的代码中,在getPlugins中,childFile是使用File.separator构造的。

String childFile = parent + File.separator + files[i].substring(0, files[i].indexOf(“。”));
Class c = loadClass(childFile);
英文:

Looking at the error and method ClassLoader.defineClass, I think the name parameter must have . as package separators, not / or \.

In your code in getPlugins the childFile is constructed using File.separator

String childFile = parent + File.separator + files[i].substring(0, files[i].indexOf(&quot;.&quot;));
Class c = loadClass(childFile);

huangapple
  • 本文由 发表于 2020年8月19日 19:50:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/63486363.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定