Java RMI——在学校只做过一个例子,现在都忘的差不多了。好记性不如烂笔头,还是在从官网上抄写一遍Sample吧!
RMI in JDK1.5
创建
Step 1. 创建服务接口,并定义需要公开的方法
Step 2. 实现服务接口。
Step 2.1. 创建并Export一个远程对象
Step 2.2. 向Java RMI Registry注册
Step 3. 创建客户端代码
Step 3.1. 获取服务器的Registry的Stub
Step 3.2. 从Registry中查询并获取相关远程对象的stub
Step 3.3. 调用该远程对象的方法
运行
Step 1. 编译
CMD/> javac -d XXX/Dir *.java
Step 2. 启动 RMI registry
CMD/> start rmiregistry 1099
Step 3. 启动Server
CMD/> start java me.test.rmi.MyServer
Step 4. 启动Client
CMD/> start java me.test.rmi.MyClient
JDK1.5 相对应 JDK1.4 的改进
> 动态生成 stub 类,不需要在运行rmic命令(除非要支持1.5以前版本)
> 标准的 SSL/TLS Socket 工厂类
> 可从inetd/xinetd中运行 rmid或 Java RMI Server
> Serialization(序列化) 改进
IMyMath.java
package me.test.rmi;
import java.rmi.Remote; import java.rmi.RemoteException;
public interface IMyMath extends Remote { public int add(int a, int b) throws RemoteException; }
|
MyServer.java
package me.test.rmi;
import java.rmi.RemoteException; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; import java.rmi.server.UnicastRemoteObject;
public class MyServer implements IMyMath { private static final long serialVersionUID = 4788969735276921818L;
public MyServer() { } public int add(int a, int b) throws RemoteException { return a + b; } public static void main(String[] args) { try { IMyMath myMath = new MyServer(); // 可以不像JDK1.4中那样继承UnicastRemoteObject // 但需要自己手动的 Export 一个处理客户端请求的远程对象 IMyMath stub = (IMyMath) UnicastRemoteObject .exportObject(myMath, 0); // 注册 Registry registry = LocateRegistry.getRegistry(); registry.bind("MyMathServer", stub); System.out.println("MyMathServer bound in registry!"); } catch (Exception e) { System.out.println("MyMath ERROR : " + e.getMessage()); e.printStackTrace(); } } }
|
MyClient.java
package me.test.rmi; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry;
public class MyClient { public static void main(String[] args) { IMyMath myMath = null; try { Registry registry = LocateRegistry.getRegistry("localhost"); myMath = (IMyMath) registry.lookup("MyMathServer"); int a = 1; int b = 2; int sum = myMath.add(a, b); System.out.println(a + " + " + b + " = " + sum); } catch (Exception e) { System.out.println("MyMathClient ERROR : " + e.getMessage()); e.printStackTrace(); } } } |
参见Sun官方的RMI文档,可以看到JDK 1.4及以后版本的例子:
RMI in JDK1.4
创建
Step 1. 创建服务接口,并定义需要公开的方法(如官方示例的Hello接口)。
Step 2. 实现服务接口。
Step 2.1. 实现一个Remote接口
Step 2.2. 输出(Export)一个能够相应客户端请求的对象
Step 2.3. 创建并安装一个SecurityManager
Step 2.4. 创建一个或多个远程对象的实例,并注册
Step 3. 创建客户端代码
Step 3.1. 从registry中查询并获得注册相应地址的远程对象的实例(数据+class代码+反序列化)
Step 3.2. 调用该远程对象上的方法,并获取返回值(序列号+反序列化)。
运行
Step 1. 编译
CMD/> javac -d XXX/Dir *.java
Step 2. 使用rmic生成 skeleton 和 stub
CMD/> rmic -d XXX/Dir examples.hello.HelloImpl
※ 会生成 HelloImpl_Stub.class(远程对象在本地的代理) 和 HelloImpl_Skel.class
Step 3. 启动 RMI registry
CMD/> start rmiregistry 1099
Step 4. 启动Server
CMD/> start java -Djava.rmi.server.codebase=file:/C:\XXX\
-Djava.security.policy=policy examples.hello.HelloImpl
说明: 这里的 policy 文件内容如下,仅仅是为了说明例子
grant {
permission java.security.ALLPermission;
};
Step 5. 启动Client
CMD/> java examples.hello.HelloClient
Hello.java
package examples.hello;
import java.rmi.Remote; import java.rmi.RemoteException;
// 注意: 需要继承 java.rmi.Remote。 public interface Hello extends Remote { // 注意:公开方法要声明抛出 java.RemoteException 异常。 String sayHello() throws RemoteException; } |
HelloImpl.java
package examples.hello;
import java.rmi.Naming; import java.rmi.RemoteException; import java.rmi.RMISecurityManager; import java.rmi.server.UnicastRemoteObject;
// 实现类需要: // 1) 实现Remote接口; // 2) 输出(Export)一个能够相应客户端请求的对象 // 3) 声明构造函数,并至少throw一个java.rmi.RemoteException // 注意: 继承自UnicastRemoteObject,可以使用RMI默认的、基于socket的通讯方式。并一直保持运行。 public class HelloImpl extends UnicastRemoteObject implements Hello {
private static final long serialVersionUID = -233771670669755972L;
// 父类的构造函数中会Export出这个对象,以相应客户端请求。 public HelloImpl() throws RemoteException { super(); }
// 实现每个远程方法 public String sayHello() { return "Hello World!"; }
public static void main(String args[]) {
// Create and install a security manager if (System.getSecurityManager() == null) { System.setSecurityManager(new RMISecurityManager()); } try { HelloImpl obj = new HelloImpl(); // Bind this object instance to the name "HelloServer" Naming.rebind("HelloServer", obj); System.out.println("HelloServer bound in registry"); } catch (Exception e) { System.out.println("HelloImpl err: " + e.getMessage()); e.printStackTrace(); } } }
|
HelloClient.java
package examples.hello; import java.rmi.Naming;
public class HelloClient {
public static void main(String[] args){ String message = "blank"; Hello obj = null; try { // 该语句将从服务器上获取远程对象(HelloImpl)stub的实例, // 然后从CLASSPATH或applet的codebase中下载到stub的class(HelloImpl_Stub) obj = (Hello)Naming.lookup("//localhost/HelloServer"); message = obj.sayHello(); System.out.println(message); } catch (Exception e) { System.out.println("HelloApplet exception: " + e.getMessage()); e.printStackTrace(); } } }
|
policy
grant {
// Allow everything for now
permission java.security.ALLPermission;
};
//grant {
// permission java.net.SocketPermission "*:1024-65535", "connect,resolve";
//};
PS: 补充——关于CallBack
Spring 中使用 RmiProxyFactoryBean 时,仅仅使用默认配置时,如果RMI服务器没有启动,则该服务在启动时会因为连接不上RMI服务器而造成 Spring 上下文创建失败。不过,可以参考以下例子经进行配置后可以仅在需要时才连接RMI服务器。
- <?xml version="1.0" encoding="UTF-8" ?>
- <beans xmlns=""
- xmlns:xsi=""
- xmlns:util=""
- xmlns:aop=""
- xmlns:tx=""
- xsi:schemaLocation="
- /spring-beans-2.5.xsd
- /spring-util-2.5.xsd
- /spring-aop-2.5.xsd
- /spring-tx-2.5.xsd">
- <bean id="testService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean" lazy-init="true">
- <property name="serviceUrl" value="rmi://localhost:1099/testService" />
- <property name="serviceInterface" value="java.lang.Runnable" />
- <property name="refreshStubOnConnectFailure" value="true" />
- <property name="lookupStubOnStartup" value="false" />
- <property name="cacheStub" value="true" />
- </bean>
- </beans>
PPS: 关于使用Spring+POJO+CallBack的例子,请参考:
说明:
默认:使用Spring,默认,只有服务器端的实现代码是在服务端运行的,而 Callback(引用型对象) 则都是经过序列化之后在客户端JVM上运行的。
1. traditional :
缺点1:回调接口需要继承自 Remote。
缺点2:回调接口实现类需要继承自 UnicastRemoteObject, 并提供抛出 RemoteException 的无参构造函数,
且每个要暴露的方法都要声明抛出 RemoteException。
2. modify :
可以避免缺陷2。但仍无法避免缺陷1。
缺点1:回调接口需要继承自 Remote。
3. pojo : 修正上述两个缺点,但需要使用一个额外的工具类 : RMIUtil
------------------------------------------ Linux 部署时的几个错误处理
1. ObjID already in use
错误日志相关提示:
[2012/05/24 23:57:00][INFO][RmiServiceExporter] Binding service 'xxxxxxx' to RMI registry: RegistryImpl[UnicastServerRef [liveRef: [endpoint:[
60.191.124.236:1253](local),objID:[0:0:0, 0]]]]
[2012/05/24 23:57:22][INFO][RmiServiceExporter] Unbinding RMI service 'xxxxxxx' from registry at port '1253'
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'yyyyyyyy' defined in class path resource [AAAA.xml]: Invocation of init method failed; nested exception is java.rmi.server.ExportException: internal error: ObjID already in use
原因:(I Guess),Java 程序是通过主机名查询其IP地址的,没有合理配置的化,会因为解析不了主机名而无法找到对应的IP地址。
以下是在CentOS 6.2 上配置正确后的相关内容:
- [root@s01 ~]# cat /etc/sysconfig/network
- NETWORKING=yes
- HOSTNAME=s01 # 主机名
- [root@s01 ~]# cat /etc/hosts
- 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4
- ::1 localhost localhost.localdomain localhost6 localhost6.localdomain6
- 192.168.0.253 s01 # 为解决该问题而追加的,将主机名本地解析为相应的IP地址
2. Caused by: java.net.NoRouteToHostException: No route to host: connect
虽然将1099等端口开放了,仍然抛错。
原因:防火墙开放的端口还不够,但是实在不知道还要再开放哪些端口,所以,只能把防火墙关了
。
FIXED!参考:《》
阅读(1721) | 评论(0) | 转发(0) |