`
gyabooks
  • 浏览: 23796 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Java RMI 实现代码动态下载

阅读更多

博采众生摘要:本译文将向你介绍JavaTMRMI动态类文件下载的应用,学习完本文,你将会对JavaTMRMI有进一步的认识。希望你能参考我的上一篇译文:开始学习Java RMI,远程方法调用-基础篇

你可以点击如下链接访问原文:Dynamic code downloading using JavaTM RMI,codebase tutorial。

声明:未经允许请勿转载,谢谢!

本教程将分如下步骤向你介绍:

1.概要

Java平台一个重要的优点就是可以动态的从一个给定的URL下载Java软件到一个正在运行JVM的独立进程中,该进程通常位于一个不同物理系统中。这样可以让一个远程系统运行一个程序,例如一个Applet,它从来没有被安装到本地的存储介质上。在该文档的前几部分,我们先讨论Applet的codebase,以帮助我们更好的介绍有关Java RMI的codebase。

举例来说,一个运行在浏览器中的虚拟机,可以把java.applet.Applet的子类和其相关类的字节码下载下来(到本地)。运行该浏览器的系统以前从没有运行过该Applet,也没有在本地安装。一旦所有的类从服务端下载完成,浏览器借助本地资源就开始运行这个Applet程序。

Java RMI正是采用了这个优点,下载、运行这些从来没有在本地安装过的类。调用Java RMI的API的虚拟机,不仅仅像那些浏览器中,能够下载任意Java类文件,其中含有那些特定Java RMI存根类,它使借助服务器资源的远程调用的执行成为可能。

Codebase观点源于Java程序语言的ClassLoaders的应用。当一个Java程序使用一个ClassLoader时,那么它需要知道它被允许到那里调用类。通常,一个类调用者和HTTP Server一起使用,Server为Java平台应用提供编译过的类。很可能,你提到第一对有关ClassLoader/codebase就是AppletClassLoader和作为HTML标签<applet>的“codebase”属性。本文档假设你有些Java RMI编程经验,同时写过一些含有applet标签的HTML文件。例如,在HTML源文件中(applet标签)将含有一些类似下面的代码:

<applet height=100 width=100 codebase="myclasses/" code="My.class"> <param name="ticker"> </applet>

2.什么是codebase

类代码址可以为一个源文件,或是一个目录,虚拟机可以由此加载类。举个例子来说,如果你邀请一个朋友到家吃晚饭,你需要告诉你朋友你的居住方向,以便你的朋友能够确定你家的位置。同样,你可以把代码库址(Codebase)看作一个你指给JVM的方向,让JVM能够找到[可能是远程]它需要的类。

你可以把你的classpath看作为是“本地代码库址”,因为它是一系列调用本地代码类目录。当基于本地调用类时,你的classpath环境变量是(JVM的)参照。CLasspath变量可以设定为一个相对,绝对目录或是类文件压缩包。倘若CLASSPATH是一种“本地代码库址”,那么Applets和远程对象使用的codebase也可以认为是一种“远程代码库址”。

3.工作原理

3.1 Applets如何使用代码库址(codebase)

为了能和Applet交互,这个applet和其运行中需要的任何类必须能够被客户端访问。虽然applets也可以通过“ftp://”或是“file:///”地址访问,但是它经常是通过Web服务访问。

  • 1.客户端浏览器请求的一个applet的类在CLASSPATH中无法找到
  • 2.通过HTTP,applet(和它需要其他类)从服务端下载到客户端
  • 3.在客户端执行applet

图一:下载Applets

<applet>标签含有的代码库址(codebase)通常是HTML页面URL的相对地址。

3.2 Java RMI如何使用代码库址(codebase)

使用Java RMI,应用程序能够创建出远程对象,该对象接受从客户端中JVM方法调用。为了能让客户端调用远程对象中的方法,客户端必须采用一种机制来和远程对象交流。Java RMI 使用了一个叫做存根的特殊类,它能够被下载到客户端和远程对象的交流(进行方法的调用),而不是使用(专用)程序使客户端同远程对象的方法进行对话。java.rmi.server.codebase属性代表了一个或是多个URL地址,从该地址那些存根类(和存根需要的其他类)能够被下载。

像applet,那些要进行远程方法调用的类也要从“file:///”地址下载,但是也像applet,一个“file:///”地址通常要求客户端和服务端位于同样的物理主机上,除非URL使用其他的文件系统,像NFS,这样才能变得有效。

通常,那些被用来进行远程方法调用的类,都是通过网络资源访问的,像是HTTP或是FTP服务器。

图二:下载Java RMI 存根类

  • 1. 远程对象代码库址(codebase)是通过在远程对象服务端设定java.rmi.server.codebase属性指定的。在Java RMI 注册表的帮助下,Java RMI 服务端注册了一个远程对象,并绑定了一个名字。服务端JVM中代码库址(codebase)设定给在Java RMI注册表中的远程对象引用提供了注解。

  • 2. Java RMI客户端请求一个(已知)命名的远程对象的引用。客户端使用该引用(远程对象的存根实例)进行对远程对象的方法调用。

  • 3. Java RMI注册表向请求的类返回一个引用(存根实例)。客户端会优先于codebase在本地的classpath中寻找存根类,如果发现了,那么它就会在本地调用该类。然而,如果在本地的classpath找不到该stub的存根类,客户端就会试着从远程对象代码库址(codebase)中检索该类。

  • 4. 客户端从代码库址(codebase)请求类。客户端使用的代码库址(codebase)就是存根实例注解的URL,它是在存根类被注册表加载时注解的。回到第一步1,为导出对象的存根做注解,然后随着绑定的名字被注册到注册表中。

  • 5. 定义的存根类(和其需要的其他类)被下载到客户端。

    注意:第4和5是相同的步骤,当远程对象被一个名字绑定到注册表 中时,注册表就开始调用该远程对象类。当注册表试着调用远程对象的存根类时,从代码库址(codebase)中,它和远程对象一起请求该类的定义。

  • 6. 现在客户端已经具备了调用远程对象方法的所有条件。存根(在这过程中)像一个服务端的远程对象的代理一样;不像applet使用代码库址(codebase)运行代码在本地的虚拟机中,Java RMI 客户端使用远程的代码库址(codebase)运行代码在另一个,可能是远程JVM中。如图三所示:

图三:Java RMI 客户端远程方法调用

4.在Java RMI中利用codebase属性,实现非存根(sub)类的下载

除了下载存根类(stub)和其辅助类到客户端,java.rmi.server.codebase属性还被用来指定其他的,不仅仅是stub类的下载地址。

当客户端调用远程对象的方法时,该方法可能无参或是有许多的参数,根据方法参数类型,这样就可能有三种不同情况发生。

第一种情况,所有的(远程)方法参数(或是返回值)都是原始的数据类型,这样远程对象知道如何的解释他们作为方法的参数,同时也无需检查classpath和codebase属性。

第二种情况,至少有一个参数或是返回值是一个对象,然而远程对象可以在本地的classpath中可以找到该对象类的定义。

第三种情况(如图四,第六步所示),远程方法收到一个对象参数,然而远程对象在本地的classpath中没有找到对象的定义。这种远程方法的调用情况如图四所示。客户端发送的对象类可能是(远程方法)参数类的子类型,它可能是两者其中之一:

  • 一个接口的实现,该接口为方法的参数(或是返回值)类型
  • 一个类的子类,该类为方法的参数(或是返回值)类型

图四:Java RMI客户端远程方法调用,传递一个未知的参数类型的子类型

7. 类似applet的代码库址(codebase),客户端设定的代码库址(codebase),用于其他JVM下载远程类,非远程类和接口地址。如果在客户端的应用中设定了代码库址(codebase)属性,那么客户端在调用子类型时,代码库址(codebase)就被作为参数加到子类型的实例上。如果在客户端没有设定代码库址(codebase),那么远程对象就会错误的使用自己的代码库址(codebase)。

5.命令行例子

在applet情况下,代码库址(codebase)是嵌在网页中的,就如我们在本文的第一部分看到的HTML例子。

在Java RMI应用时,codebase不是依靠一个镶嵌在网页中类的引用实现的,客户端会和Java RMI的注册表沟通获得远程对象的应用。由于远程对象的代码库址(codebase)可以指向任意URL,不能仅是一个相对于已知的URL地址,必须是存根类(stub)和其相关类目录的绝对地址。代码库址(codebase)可以指向:

  • 一个目录地址,该目录中含有类包子目录
  • 一个Jar文件路径,含有类包的目录压缩文件
  • 满足以上条件的多个目录或是多个Jar文件,中间用空格间隔

注意:如果代码库址(codebase)设定为一目录地址,那么结尾一定要是“/”。

例子:

如果你把要下载类在“webvector”HTTP服务器的export目录下(在Web根目录下),那么的你的代码库址(codebase)就该这样设置:

-Djava.rmi.server.codebase=http://webvector/export/

如果你把要下载类放在“webline”HTTP服务器的public目录下(在Web根目录下),一个名字为“mystuff.jar”的Jar文件,你的代码库址(codebase)就该如此设置:

-Djava.rmi.server.codebase=http://webline/public/mystuff.jar

现在我们假设你把要下载的类分为两个文件“myStuff.jar”和“myOtherStuff.jar”,而且这两个文件放在不同的服务器上(名字是:“webfront”和“webwave”),你的代码库址(codebase)属性就该这样设定:

-Djava.rmi.server.codebase="http://webfront/myStuff.jar http://webwave/myOtherStuff.jar"

6.疑难问题解答

如果你的Java RMI 程序配置正确,任何一个可以序列化的类,包含Java RMI 存根类,都是可以被下载下来的。动态的存根(stub)能够正常的下载,需要满足几种状况:

  • A. 通过URL提供的存根类和存根类依赖的任何类能够被客户端可达。
  • B. 在服务程序中通过调用bind或是rebind设定(或是在程序安装的过程中激活)(译注:rebind(String url, Remote obj) 或是bind(String url, Remote obj)),如下情况:
    • 如步骤A中设定的URL同时
    • 如果设定的为一个目录,那么必须是“/”结尾。
  • C. rmiregistry在它相关的classpath中找不到存根类或是其依赖的其他类。这也是为什么我们在注册表调用存根时,给它加上代码库址(codebase)的参数的原因了,在服务端或是安装代码中,作为一个调用的结果。
  • D. 客户端安装的SecurityManager允许存根(stub)下载。在Java 2 SE或是高版本中,这将意味着客户端必须在策略文件中进行合适的配置。

在使用Java RMI的java.rmi.server.codebase系统变量时,有两种经常性的问题,我们将在下边讨论。

6.1 运行Java RMI服务端可能碰到的问题

你碰到的第一个问题可能是收到ClassNotFoundException的异常,当你向注册表绑定(bind或是rebind)一个远程对象和名字时。这种异常通常是由不合法的codebase属性引起的,导致了在注册表中不能定位远程对象的存根(stub)或是存根需要的其他类。

同远程对象本身相比,远程对象的存根(stub)实现了所有同样的接口,这需要特别的注意。因此这些接口,同其他定制的类作为方法参数或是返回值,也必须能够通过指定的代码库址(codebase)下载。

通常,由于忽略了属性设定时URL中末尾“/”,导致这个异常的抛出。其他的一些原因可能是:属性值不是一个URL;URL路径拼写错误或是不正确;设定的URL中存根类(stub)和其相关的类不存在。

这种情况下,你遇到的异常可能是这样:

java.rmi.ServerException: RemoteException occurred in server thread; nested exception is: 
	java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
	java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub
java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is:
	java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub
java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub
	at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(Compiled Code)
	at sun.rmi.transport.StreamRemoteCall.executeCall(Compiled Code)
	at sun.rmi.server.UnicastRef.invoke(Compiled Code)
	at sun.rmi.registry.RegistryImpl_Stub.rebind(Compiled Code)
	at java.rmi.Naming.rebind(Compiled Code)
	at examples.callback.MessageReceiverImpl.main(Compiled Code)
RemoteException occurred in server thread; nested exception is:
	java.rmi.UnmarshalException: error unmarshalling arguments; nested exception is: 
	java.lang.ClassNotFoundException: examples.callback.MessageReceiverImpl_Stub

6.2 运行Java RMI客户端可能碰到的问题

你可能遇到第二个问题,就是在注册表中查找远程对象时,输出ClassNotFoundException的异常。如果你在运行客户端代码时收到这个堆栈异常信息,那么问题可能是你的Java RMI注册表启动时classpath设定的问题。参考 requirement C in section 6.0。这里有一个这样异常例子:

java.rmi.UnmarshalException: Return value class not found; nested exception is:
	java.lang.ClassNotFoundException: MyImpl_Stub
	at sun.rmi.registry.RegistryImpl_Stub.lookup(RegistryImpl_Stub.java:109
	at java.rmi.Naming.lookup(Naming.java:60)
	at RmiClient.main(MyClient.java:28)

其他资源

如果你对代码库址(codebase)还有其他的没有解答的问题,请首先浏览RMI-USER组

你可能也想加入RMI-USER邮件列表中。

alimama_pid="mm_11642003_1480608_3725409"; alimama_titlecolor="0000FF"; alimama_descolor ="000000"; alimama_bgcolor="FFFFFF"; alimama_bordercolor="E6E6E6"; alimama_linkcolor="008000"; alimama_bottomcolor="FFFFFF"; alimama_anglesize="0"; alimama_bgpic="0"; alimama_icon="0"; alimama_sizecode="11"; alimama_width=760; alimama_height=90; alimama_type=2;

分享到:
评论

相关推荐

    java RMI实现代码

    本项目提供的“java RMI实现代码”包括客户端和服务器端的示例,通过清晰的代码注释帮助理解RMI的工作原理。 一、RMI的基本概念 1. 远程接口:远程接口定义了客户端和服务器之间通信的API,它是Java接口,标注了`@...

    RMI实现代码动态下载

    在"RMI实现代码动态下载"这个主题中,我们将会探讨如何利用RMI技术来动态地从服务器下载代码到客户端。 首先,让我们理解RMI的基本工作原理。RMI包含两个主要部分:远程接口(Remote Interface)和远程对象(Remote...

    JAVA RMI测试代码

    在"JAVA RMI测试代码"中,我们可以期待找到以下核心知识点: 1. **RMI基本概念**:理解RMI的核心在于远程接口(Remote Interface)和远程对象(Remote Object)。远程接口定义了可以在远程主机上执行的方法,而远程...

    java rmi java rmi

    根据提供的文件信息,我们...通过以上步骤,我们成功地实现了一个简单的RMI应用程序,其中包括了远程接口的定义、远程对象的实现、服务器端和客户端的代码实现。这个例子展示了如何利用RMI技术来构建分布式应用程序。

    java_rmi.rar_RMI java_java.rmi

    2. **实现远程接口**:然后,创建一个实现远程接口的类,该类通常需要继承自`java.rmi.Remote`接口,并且抛出`java.rmi.RemoteException`。在这个实现类中,你需要实现远程接口中的所有方法。 3. **创建并注册远程...

    通过Java RMI实现远程调用的一个简单例子

    这个简单的例子将引导我们了解如何利用Java RMI实现远程调用。 首先,我们要创建远程接口。在Java RMI中,远程接口是一个实现了`java.rmi.Remote`接口的Java接口。接口中声明的方法将在客户端调用,实际上会在...

    RMI不依赖其他http服务实现代码动态下载

    RMI在Java早期版本中,通常需要与HTTP服务器配合,以便在客户端和服务器之间传输类定义,从而实现代码的动态下载。然而,在Java 6中,引入了一种轻量级的HTTP实现,使得RMI可以直接处理类的传输,无需依赖外部HTTP...

    rmi.zip_Java RMI_java rmi网络_java源代码 RMI

    学习和理解Java RMI,你需要掌握如何创建远程接口和实现,如何启动RMI服务器,如何在客户端进行远程对象的查找和调用,以及如何处理可能出现的网络异常。此外,对于序列化和网络通信的基础知识也是必不可少的。通过...

    一个简单的RMI例子Java源代码,用于学习理解RMI

    远程方法调用(Remote Method Invocation,RMI)是Java平台中一种用于实现分布式计算的技术。它允许Java对象在不同的JVM之间进行交互,仿佛这些对象都在同一台机器上一样。这个"一个简单的RMI例子Java源代码"是为了...

    RMI动态下载类分析及代码

    总结,RMI动态下载类是Java RMI技术的重要组成部分,它实现了跨JVM的动态类加载,增强了系统的可扩展性和灵活性。理解并熟练掌握这一特性,对于开发高效的分布式Java应用至关重要。在实际操作中,通过分析和实践代码...

    Java RMI实现远程控制程序及源代码

    使用Java的RMI来实现远程控制的小程序,类似qq的远程协助。 本程序是根据网上看到的源代码改写而成,主要在用户界面和服务端与客户端的交互上增加了许多功能,并对代码结构进行了重新组织。里面附有原作者的链接,...

    java RMI简单Demo

    `java.rmi.registry.Registry`是RMI提供的标准注册表实现。 4. ** Stub 和 Skeleton**:Stub是远程对象的代理,它位于客户端,负责将客户端的调用转换为网络消息发送给服务器;Skeleton是服务器端的代理,它接收...

    JavaRMI超棒书

    - **生成存根和骨架**:使用Java RMI工具生成客户端和服务端的存根和骨架代码,以实现远程调用。 - **测试与部署**:编写测试应用程序验证远程对象的功能,最后部署应用程序并确保其正常运行。 #### 五、案例分析...

    JAVA RMI简单例子

    这个"JAVA RMI简单例子"旨在帮助我们深入理解RMI的基本原理和实现步骤。 RMI的核心概念包括远程接口、远程对象和RMIC编译器。首先,我们需要定义一个远程接口,该接口声明了可以在远程服务器上执行的方法。这些方法...

    Java RMI中文规范

    - **分布式垃圾收集**:RMI实现了分布式垃圾收集,当远程对象不再被引用时,会自动删除,类似于本地Java虚拟机的垃圾收集。 - **并行计算**:RMI支持多线程,让服务器能高效处理来自多个客户端的请求。 1.3 其他...

    JAVA RMI

    ### RMI实现步骤 1. **定义远程接口** 编写一个接口,继承自`java.rmi.Remote`,并在接口中声明要暴露的远程方法。 2. **实现远程接口** 创建实现远程接口的类,实现接口中的所有方法。 3. **创建并注册远程...

    JAVA RMI.rar_Java RMI_ME_RMI java_rmi

    Java Remote Method Invocation (RMI) 是Java平台中用于构建...而"JAVA RMI"文件可能是一个包含RMI实现的Java类库,或者是演示RMI功能的可执行程序。通过阅读"read me"文件,可以更深入地理解这个项目是如何实现RMI的。

    JAVA RMI远程调用方法代码

    - 创建并实现一个远程接口,该接口需要继承`java.rmi.Remote`接口,并声明可能抛出`RemoteException`的方法。 - 实现该远程接口,创建一个具体的类来完成实际的业务逻辑。 - 使用`UnicastRemoteObject`将远程对象...

Global site tag (gtag.js) - Google Analytics