使用SPI加载时消失的jar条目

时间:2009-09-03 16:17:29

标签: java

所以在我的应用程序中我有一个插件系统,我用SPI(服务提供程序接口)编写,所以我有一个名为scripts.jar的jar文件,我保留在某处(注意:它不在类路径上),然后我使用这种方法从中加载脚本:

/**
 * Loads a script from the given name, regardless of case.
 * @param name script name
 * @return the loaded Script
 */
public static Script loadScript(Client c, String name) {
    Script script = null;
    try {
        URLClassLoader loader = new URLClassLoader(new URL[]{new File(Config.CONF_DIR, "scripts.jar").toURI().toURL()});
        ServiceLoader serviceLoader = ServiceLoader.load(Script.class, loader);
        serviceLoader.reload();
        Iterator<Script> scripts = serviceLoader.iterator();
        while(scripts.hasNext()) {
            Script cur = scripts.next();
            if(cur.getClass().getSimpleName().equalsIgnoreCase(name)) {
                script = cur;
                break;
            }
        }
    } catch(Exception e) {
        log.warn("Error loading script "+name, e);
    }
    return script;
}

现在,当我开始我的程序时,这非常有效。我可以轻松地在jar中加载任何脚本。

当我重新加载脚本时出现问题 - 即更改文件,并覆盖scripts.jar,然后尝试加载任何脚本。我收到这个错误:

    Exception in thread "anjin_san:test" java.util.ServiceConfigurationError: org.stork.script.Script: Error reading configuration file
        at java.util.ServiceLoader.fail(ServiceLoader.java:207)
        at java.util.ServiceLoader.parse(ServiceLoader.java:284)
        at java.util.ServiceLoader.access$200(ServiceLoader.java:164)
        at java.util.ServiceLoader$LazyIterator.hasNext(ServiceLoader.java:332)
        at java.util.ServiceLoader$1.hasNext(ServiceLoader.java:415)
        at org.stork.script.Script.loadScript(Script.java:408)
        at org.stork.Client$2.run(Client.java:95)
        at java.lang.Thread.run(Thread.java:619)
Caused by: java.io.FileNotFoundException: JAR entry META-INF/services/org.stork.script.Script not found in C:\Users\stork\Documents\bot-sama\etc\scripts.jar
        at sun.net.www.protocol.jar.JarURLConnection.connect(JarURLConnection.java:122)
        at sun.net.www.protocol.jar.JarURLConnection.getInputStream(JarURLConnection.java:132)
        at java.net.URL.openStream(URL.java:1010)
        at java.util.ServiceLoader.parse(ServiceLoader.java:279)
        ... 6 more

所以它说无法找到jar条目,但我可以很容易地检查它:

    C:\Users\stork\Documents\bot-sama\etc>jar tvf scripts.jar
     0 Thu Sep 03 16:58:00 BST 2009 META-INF/
   102 Thu Sep 03 16:57:58 BST 2009 META-INF/MANIFEST.MF
     0 Thu Sep 03 16:35:20 BST 2009 META-INF/services/
   482 Thu Sep 03 16:57:52 BST 2009 META-INF/services/org.stork.script.Script
   ...

它也是合适的尺寸。当我尝试加载任何脚本时,问题就发生了,而不仅仅是那个被修改过的脚本。

我认为这可能是一个错误,任何输入?

2 个答案:

答案 0 :(得分:4)

检查文件权限。当原始JAR与应用程序的其余部分放在一起时,程序可以读取它。也许它被不同的用户更新 - 而不向其他用户授予读取权限 - 并且程序无法再打开该文件。

我无法分辨您正在运行的Windows版本,但是安全属性对话框,或者在XP Home上,cacls命令应详细说明之前和之后的权限。


更新:重新阅读ServiceLoader文档,我注意到了一个实例方法reload。虽然代码每次需要时都会创建一个新的ServiceLoader,但可能是ServiceLoader类保留了所有实例使用的缓存。不要创建新的加载器,而是尝试保留一个,并使用reload方法检查新脚本。

答案 1 :(得分:0)

正如comment in another answer所说,禁用URL连接中的缓存对我有用,但在我的解决方案中,我没有调用openConnection()方法。相反,我称之为以下方法:

private void disableURLConnectionCache() {

    new URLConnection(null) {
        @Override
        public void connect() throws IOException {}
    }.setDefaultUseCaches(false);
}