如何使用RMI实现插件体系结构



我正在尝试在Java Swing客户端和Tomcat服务器之间实现RMI工作流。我遇到的困难是创建一个插件架构,以便购买我们产品的用户可以将新的jar文件放入服务器上的插件文件夹中,然后使这些类对客户端和服务器都可用。

现在,我有客户端部分工作:它要求服务器提供插件url列表,服务器扫描插件文件夹并返回与它在那里找到的jar文件对应的url,然后客户端使用网络类加载器与提供的url动态加载插件类。

当客户端通过使用插件类的RMI向服务器发送对象时,就会出现问题。服务器的类路径中没有这些类(因为它们只是放在硬盘驱动器上的一个文件夹中,而不是放在webapp或Tomcat文件夹中),因此会抛出错误。我需要找到一个解决办法。

有几个限制需要注意:

  • 我们不希望用户在将新插件放入服务器上的插件文件夹时必须重新启动Tomcat

  • 我们不想更改Tomcat安装本身的任何内容,例如启动参数或额外的jar文件,因为我们的解决方案需要由已经运行了自己的Tomcat的客户安装。他们只是想把我们的产品放到他们的webapp目录下,然后。

以下是我尝试过的一些可能的解决方案,但都没有奏效:

  • 我不能使用'java.rmi.server. net '。Codebase在服务器上的system属性来解决这个问题,因为插件jar列表在运行时更改,并且该属性在启动时缓存。我也希望不必安装SecurityManager,这是这种方法所必需的。

  • 定义我们自己的类加载器并设置'java.rmi.server. js 'RMIClassLoaderSpi的系统属性看起来正是我们需要的,但是当使用这种方法时,类加载器需要由系统类加载器加载。因为我们是在使用WebAppClassLoaders的Tomcat中运行的,所以这行不通。

  • 我想使用反射和暴力设置我们的自定义类加载器到'defaultProvider'私有静态变量在RMIClassLoader类,但它是最终的,所以我认为我不能设置一个新的值(还没有实际测试)。

[UPDATE - show stack trace from server]下面是当客户端试图在RMI调用中使用一个插件类时,服务器产生的堆栈跟踪:

Jun 19, 2013 7:20:58 AM sun.rmi.server.UnicastServerRef logCallException
FINE: RMI TCP Connection(4)-192.168.1.107: [192.168.1.107] exception: 
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
    java.lang.ClassNotFoundException: com.prosc.cps.CPSProperties
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:294)
    at sun.rmi.transport.Transport$1.run(Transport.java:159)
    at java.security.AccessController.doPrivileged(Native Method)
    at sun.rmi.transport.Transport.serviceCall(Transport.java:155)
    at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:535)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:790)
    at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:649)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:895)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:918)
    at java.lang.Thread.run(Thread.java:680)
Caused by: java.lang.ClassNotFoundException: com.prosc.cps.CPSProperties
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:306)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:247)
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:249)
    at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:432)
    at sun.rmi.server.LoaderHandler.loadClass(LoaderHandler.java:163)
    at java.rmi.server.RMIClassLoader$2.loadClass(RMIClassLoader.java:620)
    at java.rmi.server.RMIClassLoader.loadClass(RMIClassLoader.java:247)
    at sun.rmi.server.MarshalInputStream.resolveClass(MarshalInputStream.java:201)
    at java.io.ObjectInputStream.readNonProxyDesc(ObjectInputStream.java:1589)
    at java.io.ObjectInputStream.readClassDesc(ObjectInputStream.java:1494)
    at java.io.ObjectInputStream.readOrdinaryObject(ObjectInputStream.java:1748)
    at java.io.ObjectInputStream.readObject0(ObjectInputStream.java:1327)
    at java.io.ObjectInputStream.readObject(ObjectInputStream.java:349)
    at sun.rmi.server.UnicastRef.unmarshalValue(UnicastRef.java:306)
    at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:288)
    ... 9 more

在客户端使用RMIClassLoaderSPI方法这相当于在客户端动态设置代码库属性:类通过RMI发送到服务器时得到注释,以便服务器可以加载它们。

找到答案了!我的系统属性"java.rmi.server"。服务器上的useCodebaseOnly'被设置为true(这很奇怪,因为它在Java 6中被记录为false,这就是我使用的)。当为true时,服务器会忽略客户端嵌入在类注释中的类路径URL。我将其设置为false,现在服务器正在从客户端读取注释并解析类!

最新更新