Using the java.rmi.server.codebase Property

本文介绍了如何使用 Java RMI 进行动态代码下载,包括 stub 和其他类的下载过程,以及如何配置 codebase 属性来指定远程类的位置。

Dynamic code downloading using Java RMI

Dynamic code downloading using JavaTM RMI
(Using the java.rmi.server.codebase Property)


This tutorial is organized as follows:

  1. Starting out
  2. What is a codebase?
  3. How does it work?
  4. Using codebase in Java RMI for more than just stub downloading
  5. Command-line examples
  6. Troubleshooting tips

1.0 Starting out

One of the most significant capabilities of the JavaTM platform is the ability to dynamically download Java software from any Uniform Resource Locator (URL) to a virtual machine (VM) running in a separate process, usually on a different physical system. The result is that a remote system can run a program, for example an applet, which has never been installed on its disk. For the first few sections of this document, codebase with regard to applets will be discussed in order to help describe codebase with regard to Java Remote Method Invocation (Java RMI).

For example, a VM running from within a web browser can download the bytecodes for subclasses of java.applet.Applet and any other classes needed by that applet. The system on which the browser is running has most likely never run this applet before, nor installed it on its disk. Once all the necessary classes have been downloaded from the server, the browser can start the execution of the applet program using the local resources of the system on which the client browser is running.

Java RMI takes advantage of this capability to download and execute classes and on systems where those classes have never been installed on disk. Using the Java RMI API any VM, not only those in browsers, can download any Java class file including specialized Java RMI stub classes, which enable the execution of method calls on a remote server using the server system's resources.

The notion of a codebase originates from the use of ClassLoader s in the Java programming language. When a Java program uses a ClassLoader , that class loader needs to know the location(s) from which it should be allowed to load classes. Usually, a class loader is used in conjunction with an HTTP server that is serving up compiled classes for the Java platform. Most likely, the first ClassLoader /codebase pairing that you came into contact with was the AppletClassLoader , and the "codebase" part of the <applet> HTML tag, so this tutorial will assume that you have some experience with Java RMI programming, as well as writing HTML files that contain applet tags. For example, the HTML source will contain something like:

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

2.0 What is a codebase?

A codebase can be defined as a source, or a place, from which to load classes into a virtual machine. For example, if you invited a new friend over for dinner, you would need to give that friend directions to the place where you lived, so that he or she could locate your house. Similarly, you can think of a codebase as the directions that you give to a VM, so it can find your [potentially remote] classes.

You can think of your CLASSPATH as a "local codebase", because it is the list of places on disk from which you load local classes. When loading classes from a local disk-based source, your CLASSPATH variable is consulted. Your CLASSPATH can be set to take either relative or absolute path names to directories and/or archives of class files. So just as CLASSPATH is a kind of "local codebase", the codebase used by applets and remote objects can be thought of as a "remote codebase".

3.0 How does it work?

3.1 How codebase is used in applets

To interact with an applet, that applet and any classes that it needs to run must be accessible by remote clients. While applets can be accessed from "ftp:// " or local "file:/// " URLs, they are usually accessed from a remote HTTP server.

  1. The client browser requests an applet class that is not found in the client's CLASSPATH
  2. The class definition of the applet (and any other class(es) that it needs) is downloaded from the server to the client using HTTP
  3. The applet executes on the client
illustrates three steps above

Figure 1: Downloading applets

The applet's codebase is always relative to the URL of the HTML page in which the <applet> tag is contained.

3.2 How codebase is used in Java RMI

Using Java RMI, applications can create remote objects that accept method calls from clients in other VMs. In order for a client to call methods on a remote object, the client must have a way to communicate with the remote object. Rather than having to program the client to speak the remote object's protocol, Java RMI uses special classes called stubs that can be downloaded to the client that are used to communicate with (make method calls on) the remote object. The java.rmi.server.codebase property value represents one or more URL locations from which these stubs (and any classes needed by the stubs) can be downloaded.

Like applets, the classes needed to execute remote method calls can be downloaded from "file:/// " URLs, but like applets, a "file:/// " URL generally requires that the client and the server reside on the same physical host, unless the file system referred to by the URL is made available using some other protocol, such as NFS.

Generally, the classes needed to execute remote method calls should be made accessible from a network resource, such as an HTTP or FTP server.

 

illustrates the first 5 steps of the stub downloadling process, as listed below

Figure 2: Downloading Java RMI stubs

  1. The remote object's codebase is specified by the remote object's server by setting the java.rmi.server.codebase property. The Java RMI server registers a remote object, bound to a name, with the Java RMI registry. The codebase set on the server VM is annotated to the remote object reference in the Java RMI registry.

     

  2. The Java RMI client requests a reference to a named remote object. The reference (the remote object's stub instance) is what the client will use to make remote method calls to the remote object.

     

  3. The Java RMI registry returns a reference (the stub instance) to the requested class. If the class definition for the stub instance can be found locally in the client's CLASSPATH , which is always searched before the codebase, the client will load the class locally. However, if the definition for the stub is not found in the client's CLASSPATH , the client will attempt to retrieve the class definition from the remote object's codebase.

     

  4. The client requests the class definition from the codebase. The codebase the client uses is the URL that was annotated to the stub instance when the stub class was loaded by the registry. Back in step 1, the annotated stub for the exported object was then registered with the Java RMI registry bound to a name.

     

  5. The class definition for the stub (and any other class(es) that it needs) is downloaded to the client.
    Note: Steps 4 and 5 are the sames steps that the registry took to load the remote object class, when the remote object was bound to a name in (registered with) the Java RMI registry. When the registry attempted to load the remote object's stub class, it requested the class definition from the codebase associated with that remote object.
  6. Now the client has all the information that it needs to invoke remote methods on the remote object. The stub instance acts as a proxy to the remote object that exists on the server; so unlike the applet which uses a codebase to execute code in its local VM, the Java RMI client uses the remote object's codebase to execute code in another, potentially remote VM, as illustrated in Figure 3:
illustrates the final step of the above procedure

Figure 3: Java RMI client making a remote method call

4.0 Using codebase in Java RMI for more than just stub downloading

In addition to downloading stubs and their associated classes to clients, the java.rmi.server.codebase property can be used to specify a location from which any class, not only stubs, can be downloaded.

When a client makes a method call to a remote object, the method that it calls could be written to accept no arguments or a number of arguments. There are three distinct cases that may occur, based on the data type(s) of the method argument(s).

In the first case, all of the method parameters (and return value) are primitive data types, so the remote object knows how to interpret them as method parameters, and there is no need to check its CLASSPATH or any codebase.

In the second case, at least one remote method parameter or the return value is an object, for which the remote object can find the class definition locally in its CLASSPATH .

In the third case (shown as Step 6, in Figure 4), the remote method receives an object instance, for which the remote object cannot find the class definition locally in its CLASSPATH . This type of remote method call is illustrated in Figure 4. The class of the object sent by the client will be a subtype of the declared parameter type. A subtype is either:

  • An implementation of the interface that is declared as the method parameter (or return) type

     

  • A subclass of the class that is declared as the method parameter (or return) type
Illustrates passing an unknown
 subtype as a method parameter, as described above and below.

Figure 4: Java RMI client making a remote method call, passing an unknown subtype as a method parameter

7. Like the applet's codebase, the client-specified codebase is used to download Remote classes, non-remote classes, and interfaces to other VMs. If the codebase property is set on the client application, then that codebase is annotated to the subtype instance when the subtype class is loaded by the client. If the codebase is not set on the client, the remote object will mistakenly use its own codebase.

5.0 Command-line examples

In the case of an applet, the applet codebase value is embedded in an HTML page, as we saw in the HTML example in the first section of this tutorial.

In the case of Java RMI codebase, rather than having a reference to the class embedded in an HTML page, the client first contacts the Java RMI registry for a reference to the remote object. Because the remote object's codebase can refer to any URL, not just one that is relative to a known URL, the value of the Java RMI codebase must be an absolute URL to the location of the stub class and any other classes needed by the stub class. This value of the codebase property can refer to:

  • The URL of a directory in which the classes are organized in package-named sub-directories

     

  • The URL of a JAR file in which the classes are organized in package-named directories

     

  • A space-delimited string containing multiple instances of JAR files and/or directories that meet the criteria above

Note: When the codebase property value is set to the URL of a directory , the value must be terminated by a "/".

Examples

If the location of your downloadable classes is on an HTTP server named "webvector", in the directory "export" (under the web root), your codebase property setting might look like this:

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

If the location of your downloadable classes is on an HTTP server named "webline", in a JAR file named "mystuff.jar", in the directory "public" (under the web root), your codebase property setting might look like this:

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

Now let's suppose that the location of your downloadable classes has been split between two JAR files, "myStuff.jar" and "myOtherStuff.jar". If these JAR files are located on different servers (named "webfront" and "webwave"), your codebase property setting might look like this:

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

6.0 Troubleshooting tips

Any serializable class, including Java RMI stubs, can be downloaded if your Java RMI programs are configured properly. Here are the conditions under which dynamic stub downloading will work:

  1. The stub class and any of the classes that the stub relies on are served up from a URL reachable from the client.

     

  2. The java.rmi.server.codebase property has been set on the server program (or in the case of activation, the "setup" program) that makes the call to bind or rebind , such that:

     

    • The value of the codebase property is the URL in step A

      and

       

    • If the URL specified as the value of the codebase property is a directory, it must end in a trailing "/"

  3. The rmiregistry cannot find the stub class or any of the classes that the stub relies on in its CLASSPATH . This is so the codebase gets annotated to the stub when the registry does its class load of the stub, as a result of calls to bind or rebind in the server or setup code.

     

  4. The client has installed a SecurityManager that allows the stub to be downloaded. In the Java 2 SDK, Standard Edition, v1.2 and later, this means that the client must also have a properly configured security policy file.

There are two common problems associated with the java.rmi.server.codebase property, which are discussed next.

6.1 If you encounter a problem running your Java RMI server

The first problem you might encounter is the receipt of a ClassNotFoundException when attempting to bind or rebind a remote object to a name in the registry. This exception is usually due to a malformed codebase property, resulting in the registry not being able to locate the remote object's stubs or other classes needed by the stub.

It is important to note that the remote object's stub implements all the same interfaces as the remote object itself, so those interfaces, as well as any other custom classes declared as method parameters or return values, must also be available for download from the specified codebase.

Most frequently, this exception is thrown as a result of omitting the trailing slash from the URL value of the property. Other reasons would include: the value of the property is not a URL; the path to the classes specified in the URL is incorrect or misspelled; the stub class or any other necessary classes are not all available from the specified URL.

The exception that you may encounter in such a case would look like this:

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 If you encounter a problem running your Java RMI client

The second problem you could encounter is the receipt of a ClassNotFoundException when attempting to lookup a remote object in the registry. If you receive this exception in a stacktrace resulting from an attempt to run your Java RMI client code, then your problem is the CLASSPATH with which your Java RMI registry was started. See requirement C in section 6.0 . Here is what the exception will look like:

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)

Other resources

If you your codebase questions are still unanswered, please take a look through the archives of the rmi-users email list first.

You may wish to subscribe to the rmi-users email list .


Copyright © 2006 Sun Microsystems, Inc. All Rights Reserved.

Please send comments to: rmi-comments@java.sun.com
Sun
Java Software

<think>我们正在讨论Java RMI中的`java.rmi.server.hostname`系统属性。根据引用,这个属性用于指定RMI服务器在注册RMI注册表时使用的主机名或IP地址。当RMI服务器在具有多个网络接口(例如,多个IP地址)的机器上运行时,或者当服务器位于NAT或防火墙后面时,这个属性尤为重要。如果不设置,RMI可能会选择一个不正确的网络接口(如127.0.0.1或私有IP),导致远程客户端无法连接。 配置方法有两种: 1. 在启动JVM时通过命令行参数设置:`-Djava.rmi.server.hostname=你的公网IP或域名` 2. 在代码中设置系统属性:`System.setProperty("java.rmi.server.hostname", "你的公网IP或域名");` 注意:这个属性必须在创建RMI注册表或绑定远程对象之前设置,因为RMI在导出远程对象时会使用这个主机名。 例如,在代码中设置: ```java public void start() throws Exception { System.setProperty("java.rmi.server.hostname", "118.31.168.198"); LocateRegistry.createRegistry(30101); RMIServer h = new RMIServer(); Naming.rebind("rmi://127.0.0.1:30101/Feng", h); System.out.println("RMI服务在30101端口启动"); } ``` 或者,在启动命令中设置: ``` java -Djava.rmi.server.hostname=118.31.168.198 -jar your_application.jar ``` 在分布式测试工具如JMeter中,这个设置同样重要,因为它确保从机(slave)能够正确地连接到主机(master)的RMI服务。 相关问题: 1. 为什么在分布式JMeter测试中必须设置`java.rmi.server.hostname`? 2. 如何诊断和解决Java RMI连接被拒绝的问题? 3. 除了设置`java.rmi.server.hostname`,还有哪些配置可以确保RMI服务在复杂网络环境中正常工作? 4. RMI注册表(Registry)和远程对象绑定(bind/rebind)之间有什么关联? 5. 在Docker容器中运行RMI服务时,如何配置`java.rmi.server.hostname`?</think>### Java RMI 服务器主机名参数详解 `java.rmi.server.hostname` 是 Java RMI(远程方法调用)框架中的关键系统属性,用于指定 RMI 服务器对外暴露的主机名或 IP 地址。当服务器有多个网络接口或位于 NAT/防火墙后时,必须正确配置该参数才能确保客户端正常连接。 #### 核心作用 1. **地址绑定控制** 指定 RMI 服务器在注册表中发布的网络地址(而非默认的本地地址) ```java // 示例:设置服务器公网IP System.setProperty("java.rmi.server.hostname", "118.31.168.198"); ``` 2. **解决连接问题** 当客户端收到 "Connection refused" 错误时(如引用[2]所述),通常是因为服务器注册了不可达的地址(如 127.0.0.1) 3. **分布式系统支持** 在 JMeter 分布式测试等场景中(引用[3][5]),确保从机能解析到主机的正确地址 #### 配置方法 **方式1:代码中动态设置(推荐)** 在创建 RMI 注册表前设置系统属性: ```java public void startRMIServer() throws Exception { // 关键配置:设置主机名/IP System.setProperty("java.rmi.server.hostname", "118.31.168.198"); // 创建注册表并绑定服务 LocateRegistry.createRegistry(30101); Naming.rebind("rmi://118.31.168.198:30101/ServiceName", new ServiceImpl()); System.out.println("RMI服务在30101端口启动"); } ``` **方式2:JVM 启动参数** 通过 `-D` 参数指定: ```bash java -Djava.rmi.server.hostname=118.31.168.198 -jar your_app.jar ``` **方式3:环境变量配置** 适用于容器化部署(引用[4]): ```bash # Tomcat示例 export CATALINA_OPTS="$CATALINA_OPTS -Djava.rmi.server.hostname=192.168.40.103" ``` #### 注意事项 1. **时机要求** 必须在 `LocateRegistry.createRegistry()` 或 `Naming.rebind()` 前设置,否则无效[^1] 2. **网络环境** - 公网部署需使用公网 IP 或域名 - 云环境需配合安全组开放端口 3. **客户端匹配** 客户端连接地址需与服务器配置一致: ```java // 客户端连接示例 String rmiUrl = "rmi://118.31.168.198:30101/ServiceName"; Service service = (Service) Naming.lookup(rmiUrl); ``` #### 常见问题解决 - **错误:ObjID already in use** 检查端口冲突(引用[4]),确保每次重启前注销服务 - **分布式测试失败** JMeter 需在所有节点设置一致的主机名(引用[3][5]) - **NAT 穿透问题** 在防火墙后部署时,需同时配置端口转发规则 ### 相关问题 1. 如何诊断 Java RMI 的 "Connection refused" 错误? 2. 在 Docker 容器中部署 RMI 服务需要哪些特殊配置? 3. RMI 与 Web Service 在分布式通信中的优劣比较? 4. 如何实现 RMI 服务的动态负载均衡? 5. JMeter 分布式测试中除了主机名还需要哪些关键配置?[^3][^5] [^1]: 引用自代码示例,说明配置时机的重要性 [^2]: 引用关于 RMI 连接问题的通用解决方案 [^3]: 引用 JMeter 分布式测试中的实际应用场景 [^4]: 引用容器环境配置示例 [^5]: 引用分布式测试的端口配置注意事项
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值