繁体   English   中英

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

[英]Read xml from an external jar not included in classpath

我使用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类,该类从plugins目录中获取所有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文件)也不起作用。

这就是我能想到的,在此先感谢您的帮助

[ 编辑 ]我已经创建了一个images/monitor-16.png到插件罐子根,加入#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)正在从Clean and Build操作上添加的Jar / Folder Libraries参考中删除plugin目录。 我已经下载并安装了Netbeans 8.0.2,问题已解决。 不过,在其跟踪器上找不到该版本的任何相关错误。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM