从类路径中不包含的外部jar读取xml



我使用Netbeans创建了一个javafx项目,项目本身运行良好。

我现在正在尝试实现一个自定义轻量级插件系统,插件是位于主项目plugins/目录内的外部jar。我使用javax.security包沙盒插件。

下面是主项目的结构:

MainProject
  |
  |---plugins/
  |   |---MyPlugin.jar
  |
  |---src/
  |   |---main.app.plugin
  |       |---Plugin.java
  |       |---PluginSecurityPolicy.java
  |       |---PluginClassLoader.java
  |       |---PluginContainer.java
  ....

和插件:

Plugin
  |
  |---src/
  |   |---my.plugin
  |   |   |---MyPlugin.java
  |   |--settings.xml
  |
  |---dist/
      |---MyPlugin.jar
          |---META-INF/
          |   |---MANIFEST.MF
          |---my.plugin
          |   |---MyPlugin.class
          |---settings.xml

将插件加载到程序中,我已经制作了一个PluginContainer类,从插件目录中获取所有jar文件,列出jar中的所有文件,并查找插件文件和设置文件。

我可以加载并创建插件类的实例,但是当涉及到XML时,我甚至无法在jar内容中列出它。

下面是代码,也许有人能看出我哪里做错了。

PluginSecurityPolicy.java

import java.security.AllPermission;
import java.security.PermissionCollection;
import java.security.Permissions;
import java.security.Policy;
import java.security.ProtectionDomain;
public class PluginSecurityPolicy extends Policy {
    @Override
    public PermissionCollection getPermissions(ProtectionDomain domain) {
        if (isPlugin(domain)) {
            return pluginPermissions();
        } else {
            return applicationPermissions();
        }        
    }
    private boolean isPlugin(ProtectionDomain domain) {
        return domain.getClassLoader() instanceof PluginClassLoader;
    }
    private PermissionCollection pluginPermissions() {
        Permissions permissions = new Permissions();
        //
        return permissions;
    }
    private PermissionCollection applicationPermissions() {
        Permissions permissions = new Permissions();
        permissions.add(new AllPermission());
        return permissions;
    }
}

PluginClassLoader.java

import java.net.URL;
import java.net.URLClassLoader;
public class PluginClassLoader extends URLClassLoader {
    public PluginClassLoader(URL jarFileUrl) {
        super(new URL[] {jarFileUrl});
    }
}

PluginContainer.java, #load方法是一个

import main.app.plugin.PluginClassLoader;
import main.app.plugin.PluginSecurityPolicy;
import java.io.File;
import java.net.URL;
import java.security.Policy;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
public class PluginContainer {
    private ArrayList<Plugin> plugins;
    private ManifestParser parser;
    public PluginContainer() {
        Policy.setPolicy(new PluginSecurityPolicy());
        System.setSecurityManager(new SecurityManager());
        plugins = new ArrayList<>();
        parser = new ManifestParser();
    }
    public void init() {
        File[] dir = new File(System.getProperty("user.dir") + "/plugins").listFiles();
        for (File pluginJarFile : dir) {
            try {
                Plugin plugin = load(pluginJarFile.getCanonicalPath());
                plugins.add(plugin);
            } catch (Exception e) {
                throw new RuntimeException(e.getMessage(), e);
            }
        }
    }
    public <T extends Plugin> T getPlugin(Class<T> plugin) {
        for (Plugin p : plugins) {
            if (p.getClass().equals(plugin)) {
                return (T)p;
            }
        }
        return null;
    }
    private Plugin load(String pluginJarFile) throws Exception { 
        PluginManifest manifest = null;
        Plugin plugin = null;
        // Load the jar file
        ZipFile jarFile = new ZipFile(pluginJarFile);            
        // Get all jar entries
        Enumeration allEntries = jarFile.entries();
        String pluginClassName = null;
        while (allEntries.hasMoreElements()) {
            // Get single file
            ZipEntry entry = (ZipEntry) allEntries.nextElement();
            String file = entry.getName();
            // Look for classfiles
            if (file.endsWith(".class")) {
                // Set class name
                String classname = file.replace('/', '.').substring(0, file.length() - 6);
                // Look for plugin class
                if (classname.endsWith("Plugin")) {
                    // Set the class name and exit loop
                    pluginClassName = classname;
                    break;
                }
            }
        }
        // Load the class
        ClassLoader pluginLoader = new PluginClassLoader(new URL("file:///" + pluginJarFile));
        Class<?> pluginClass = pluginLoader.loadClass(pluginClassName);
        // Edit as suggested by KDM, still null
        URL settingsUrl = pluginClass.getResource("/settings.xml");
        manifest = parser.load(settingsUrl);
        // Check if manifest has been created
        if (null == manifest) {
            throw new RuntimeException("Manifest file not found in " + pluginJarFile);
        }
        // Create the plugin
        plugin = (Plugin) pluginClass.newInstance();
        plugin.load(manifest);
        return plugin;
    }
}

和自动生成的MANIFEST。MF

Manifest-Version: 1.0
Ant-Version: Apache Ant 1.9.4
Created-By: 1.8.0_25-b18 (Oracle Corporation)

Class-Path指令缺失,但如果我强制它为../settings.xmlsettings.xml(通过手动编辑MANIFEST.MF文件),它也不会工作。

这是我能想到的,提前感谢你的帮助

[EDIT]我已经创建了一个images/monitor-16.png到插件jar根,添加了#load2方法到PluginContainer

由于该方法是在循环中调用的,所以我将Policy.setPolicy(new PluginSecurityPolicy());System.setSecurityManager(new SecurityManager());留在构造函数中。

下面是新的插件jar结构:

TestPlugin.jar
    |
    |---META-INF/
    |   |---MANIFEST.MF
    |
    |---dev.jimbo
    |   |---TestPlugin.class
    |
    |---images
    |   |---monitor-16.png
    |
    |---settings.xml

新方法代码:

private Plugin load2(String pluginJarFile) throws MalformedURLException, ClassNotFoundException {        
    PluginClassLoader urlCL = new PluginClassLoader(new File(pluginJarFile).toURL());
    Class<?> loadClass = urlCL.loadClass("dev.jimbo.TestPlugin");
    System.out.println(loadClass);
    System.out.println("Loading the class using the class loader object. Resource = " + urlCL.getResource("images/monitor-16.png"));
    System.out.println("Loading the class using the class loader object with absolute path. Resource = " + urlCL.getResource("/images/monitor-16.png"));
    System.out.println("Loading the class using the class object. Resource = " + loadClass.getResource("images/monitor-16.png"));
    System.out.println();
    return null;
}

输出

class dev.jimbo.TestPlugin
Loading the class using the class loader object. Resource = null
Loading the class using the class loader object with absolute path. Resource = null
Loading the class using the class object. Resource = null

以下程序:

public static void main(String[] args) throws MalformedURLException, ClassNotFoundException {
    Policy.setPolicy(new PluginSecurityPolicy());
    System.setSecurityManager(new SecurityManager());
    PluginClassLoader urlCL = new PluginClassLoader(new File(
            "A Jar containing images/load.gif and SampleApp class").toURL());
    Class<?> loadClass = urlCL.loadClass("net.sourceforge.marathon.examples.SampleApp");
    System.out.println(loadClass);
    System.out.println("Loading the class using the class loader object. Resource = " + urlCL.getResource("images/load.gif"));
    System.out.println("Loading the class using the class loader object with absolute path. Resource = " + urlCL.getResource("/images/load.gif"));
    System.out.println("Loading the class using the class object. Resource = " + loadClass.getResource("images/load.gif"));
}

产生以下输出:

class net.sourceforge.marathon.examples.SampleApp
Loading the class using the class loader object. Resource = jar:file:/Users/dakshinamurthykarra/Projects/install/marathon/sampleapp.jar!/images/load.gif
Loading the class using the class loader object with absolute path. Resource = null
Loading the class using the class object. Resource = null

所以我不认为你的类装入器有任何问题。将此作为答案,以便可以正确格式化代码。

成功了!似乎我以前的Netbeans(8.0)正在从添加的Jar/Folder Libraries引用中删除plugin目录Clean和Build动作。我已经下载并安装了Netbeans 8.0.2,问题解决了。但在他们的跟踪器上找不到任何与该版本相关的bug…

无论如何,谢谢你的帮助:)

相关内容

  • 没有找到相关文章

最新更新