【问题标题】:Hot loading .jar files: random FileNotFoundException热加载 .jar 文件:随机 FileNotFoundException
【发布时间】:2016-03-05 15:06:04
【问题描述】:

我有一个PluginManager 类,它使用WatchService 监视./plugins 目录以创建文件,然后利用PluginLoader 类中的静态方法loadPlugin(File) 在运行时加载新添加的jar。文件夹中的所有 jars 也会在应用程序启动时加载和启动,在这种情况下一切顺利,即使有一堆插件。
但是当我将插件一个一个放到文件夹中时,我得到了一个奇怪的行为:

  • 第一个 jar 在 98% 的时间内都可以正常加载
  • 仅占 5% 左右的第二位
  • 第三次仅在极少数情况下发生,但它会发生

我得到的是这样的:

java.io.FileNotFoundException: .\plugins\test2.jar (Der Prozess kann nicht auf die Datei zugreifen, da sie von einem anderen Prozess verwendet wird)
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(ZipFile.java:220)
at java.util.zip.ZipFile.<init>(ZipFile.java:150)
at java.util.jar.JarFile.<init>(JarFile.java:166)
at java.util.jar.JarFile.<init>(JarFile.java:130)
at PluginLoader.loadPlugin(PluginLoader.java:34)
at PluginManager$WatchQueueReader.run(PluginManager.java:118)
at java.lang.Thread.run(Thread.java:745)
Exception in thread "FileWatcher" java.lang.NullPointerException
at PluginLoader.loadPlugin(PluginLoader.java:41)
at PluginManager$WatchQueueReader.run(PluginManager.java:118)
at java.lang.Thread.run(Thread.java:745)

表示该文件正在另一个进程中使用。

插件加载器:

public static BasePlugin loadPlugin(File pluginJar) {
    Attributes attrib = null;
    JarFile file = null;
    try {
        file = new JarFile(pluginJar);
        Manifest manifest = file.getManifest();
        attrib = manifest.getMainAttributes();
    } catch (IOException e) {
        e.printStackTrace();
    }

    String main = attrib.getValue(Attributes.Name.MAIN_CLASS);
    String name = attrib.getValue("Plugin-Name");

    if (main == null || name == null) {
        System.out.println("Not a valid manifest: " + pluginJar.getName());
        return null;
    }

    URLClassLoader loader = null;
    try {
        loader = URLClassLoader.newInstance(new URL[] { pluginJar.toURI().toURL() }, PluginLoader.class.getClassLoader());
    } catch (MalformedURLException e2) {
        e2.printStackTrace();
    }
    Class<?> cl = null;
    try {
        cl = Class.forName(main, true, loader);
    } catch (ClassNotFoundException e1) {
        e1.printStackTrace();
    } finally {
        try {
            loader.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    try {
        Class<? extends BasePlugin> c = cl.asSubclass(BasePlugin.class);
        Constructor<? extends BasePlugin> ctr = c.getConstructor(String.class, SystemManager.class);
        return ctr.newInstance(name, this.sm);
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}

PluginManager 中的 WatchService:

    private static class WatchQueueReader implements Runnable {

    private WatchService watcher;
    private PluginManager pm;

    public WatchQueueReader(PluginManager pm, WatchService watcher) {
        this.pm = pm;
        this.watcher = watcher;
    }

    @Override
    public void run() {
        try {
            WatchKey key = watcher.take();
            while (key != null) {
                for (WatchEvent<?> event : key.pollEvents()) {
                    switch (event.kind().toString()) {
                    case "ENTRY_CREATE":
                        Path dir = (Path) key.watchable();
                        Path fullPath = dir.resolve(event.context().toString());
                        BasePlugin plugin = PluginLoader.loadPlugin(fullPath.toFile());
                        if (plugin != null) {
                            this.pm.startPlugin(plugin);
                        }
                        break;
                    case "ENTRY_MODIFY":
                        break;
                    case "ENTRY_DELETE":
                        this.pm.stopPlugin(event.context().toString()); // TODO wrong name (.jar)
                        break;
                    default:
                        break;
                    }
                }
                key.reset();
                key = watcher.take();
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

BasePlugin 是插件扩展的抽象类。

【问题讨论】:

    标签: java plugins reflection jar filenotfoundexception


    【解决方案1】:

    文件仍在复制中,WatchService 发现它太快了。

    事实上,它会在它['s inode] 创建后立即报告文件存在,但尚未填充信息(字节,你知道)。

    我的第一个提示是,如果您遇到此异常,请等待几秒钟,然后重试。就在 10-30 秒的徒劳尝试之后,您可以放弃,因为它可能已被删除。但这部分需要微调,因为复制操作可能很慢。

    【讨论】:

    • 谢谢,这就是问题所在。也许可以在 ENTRY_CREATE 之后等待 ENTRY_MODIFY?
    • 我不确定。文档说:“当报告事件表明监视目录中的文件已被修改时,不能保证修改该文件的程序(或多个程序)已完成。”
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-01-23
    • 1970-01-01
    • 1970-01-01
    • 2018-08-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多