RMI的简单例子

来源:互联网 发布:c3p0连接池配置 mysql 编辑:程序博客网 时间:2024/05/17 07:41

出自:http://abruzzi.iteye.com/blog/285034


Java的远程方法调用(Remote Method Invocation) 是为了分布式计算而提出来的,最近做一个项目需要用到,所以学习了一番,现在将一个简单的demo贴出来,以便想要学习RMI的同志可以快速上手。

 

RMI的调用是基于接口的,这个接口的定语需要客户知道,客户程序运行时需要一个实现该接口的类的存根(stub)。RMI的内部使用了TCP/IP连接方式,因此需要一个安全机制,且需要对客户机的权限进行一定的设置。

 

想要被远程调用的接口需要扩展Remote类,接口中定义的方法需要对RemoteException异常进行处理,当然,也可以只是抛出异常,将对异常的处理延迟到别的类。

 

RMI的安全处理机制使用的是RMISecurityManager,这个类提供必要的安全验证等处理,而权限控制是通过在客户端和服务端分别设置policy文件来做到的。

 

下面给出一个比较简单,但是能说明问题的例子,略去了不必要的代码,而且这个程序可以通过扩展达到你的某些特殊要求。

 

这个例子是这样,设计一个接口TaskContainer,这个接口对LinkedList进行了一个简单的封装,用途是作为Task的容器,其中可以加入多个Task,这些Task又分别可以独立运行(运行的逻辑可以自行扩展)。TaskContainer扩展了Remote类,因此是可以通过远程调用的。

 

Java代码  收藏代码
  1. package rmidemo;  
  2.   
  3. import java.rmi.Remote;  
  4. import java.rmi.RemoteException;  
  5.   
  6. public interface TaskContainer extends Remote{  
  7.     int getTaskCount() throws RemoteException;  
  8.     Task getTask() throws RemoteException;  
  9.     void addTask(Task t) throws RemoteException;  
  10. }  

 下面是这个接口的实现

Java代码  收藏代码
  1. package rmidemo;  
  2.   
  3. import java.rmi.RemoteException;  
  4. import java.rmi.server.UnicastRemoteObject;  
  5. import java.util.LinkedList;  
  6.   
  7. public class TaskContainerImpl extends UnicastRemoteObject   
  8. implements TaskContainer{  
  9.   
  10.     private static final long   
  11.             serialVersionUID = 4656230894967394376L;  
  12.     LinkedList<Task> taskList;  
  13.       
  14.     public TaskContainerImpl() throws RemoteException{  
  15.         taskList = new LinkedList<Task>();  
  16.     }  
  17.       
  18.     public Task getTask() throws RemoteException{  
  19.         return taskList.size() > 0 ?   
  20.                 taskList.removeFirst() : null;  
  21.     }  
  22.   
  23.     public int getTaskCount() throws RemoteException{  
  24.         return taskList.size();  
  25.     }  
  26.   
  27.     public void addTask(Task task) throws RemoteException{  
  28.         taskList.add(task);  
  29.     }  
  30. }  

 代码非常简单,就不用多说了,需要注意的是这个类需要扩展UnicastRemoteObject类,而且每个方法都要对RemoteException异常进行处理,这里只是简单的抛出。

 

下面是引用到的Task接口和其实现

Java代码  收藏代码
  1. package rmidemo;  
  2.   
  3. import java.rmi.Remote;  
  4. import java.rmi.RemoteException;  
  5.   
  6. public interface Task extends Remote{  
  7.     void execute() throws RemoteException;  
  8.     int getTaskID() throws RemoteException;  
  9. }  

 Task也很简单,一个执行方法,一个获得Task的ID的方法,实现:

Java代码  收藏代码
  1. package rmidemo;  
  2.   
  3. import java.rmi.RemoteException;  
  4. import java.rmi.server.UnicastRemoteObject;  
  5.   
  6. public class TaskImpl extends UnicastRemoteObject implements Task{  
  7.     private static final long serialVersionUID = -4520397655735981164L;  
  8.     private int taskID;  
  9.       
  10.     protected TaskImpl(int i) throws RemoteException {  
  11.         taskID = i;  
  12.     }  
  13.   
  14.     public void execute() throws RemoteException{  
  15.         System.out.println("execute task = "+taskID);//这里只是一个简单的打印,你可以任意扩展之  
  16.     }  
  17.       
  18.     public int getTaskID() throws RemoteException{  
  19.         return taskID;  
  20.     }  
  21. }  

 定义完这些具体的逻辑,就可以在server端进行RMI的配置了,下面是RMI的主入口TaskServer

Java代码  收藏代码
  1. package rmidemo;  
  2.   
  3. import java.rmi.Naming;  
  4. import java.rmi.RMISecurityManager;  
  5. import java.rmi.registry.LocateRegistry;  
  6.   
  7. public class TaskServer {  
  8.     private static final String host = "localhost";  
  9.     private static final int port = 9527;  
  10.       
  11.     public static void main(String[] args){  
  12.         System.setProperty("java.security.policy",   
  13.         "TaskServer.policy");//权限控制  
  14.           
  15.         if(System.getSecurityManager() == null){  
  16.             System.setSecurityManager(new RMISecurityManager());  
  17.         }//安全管理器的安装  
  18.           
  19.         try{  
  20.             System.out.println("Constructing server");  
  21.             LocateRegistry.createRegistry(port);//注册  
  22.               
  23.             TaskContainer container = new TaskContainerImpl();  
  24.               
  25.             container.addTask(new TaskImpl(1));//想容器中添加几个Task  
  26.             container.addTask(new TaskImpl(2));  
  27.             container.addTask(new TaskImpl(3));  
  28.               
  29.             System.out.println("binding server impl to registry");  
  30.   
  31.             Naming.rebind("//"+host+":"+port+"/TaskContainer", container);  
  32.               
  33.         }catch(Exception e){  
  34.             e.printStackTrace();  
  35.         }  
  36.     }  
  37. }  

 先安装一个权限配置文件,再安装一个安全管理器,然后注册监听端口,并创建需要被远程调用的对象container,并通过Naming.rebind(String url, Object remote)方法对其绑定,以便远程对其按照URL进行访问。

 

权限的配置需要放在一个单独的文件中,这个例子中,配置放在TaskServer.policy文件中,其内容如下:

Java代码  收藏代码
  1. grant  
  2. {  
  3.  permission java.net.SocketPermission  
  4.       "*:1000-9999","accept,connect,listen,resolve";  
  5. };  

 以上为服务器端的全部配置,不过,现在还不能使用,不是因为没有客户端,而是RMI需要一个存根stub才能使远程的线程来调用。

 

JDK里带了一个工具rmic.exe,这个工具可以根据扩展了Remote的类的class文件生成一个存根类,如根据x.class,生成x_stub.class。如果你的类有包名的话,你需要提供给rmic一个完整的包路径。

 

通过下面的命令即可完成这个动作:

Java代码  收藏代码
  1. $ rmic rmidemo.TaskContainerImpl  
  2. $ rmic rmidemo.TaskImpl  

 现在在类的完整的路径下生成两个文件TaskContainerImpl_stub.class,TaskImpl_stub.class,服务端的所有配置到此结束。下面我们来看看客户端的代码。

 

相比服务端,客户端的代码就相当简单了,只需要一个用于测试的类即可:

Java代码  收藏代码
  1. package rmidemo;  
  2.   
  3. import java.rmi.Naming;  
  4. import java.rmi.RMISecurityManager;  
  5.   
  6. public class TaskClient {  
  7.     private static final String host = "localhost";  
  8.     private static final int port = 9527;  
  9.       
  10.     public static void main(String[] args){  
  11.         System.setProperty("java.security.policy",   
  12.         "TaskClient.policy");//install the policy file  
  13.           
  14.         //install RMI Security Manager  
  15.         System.setSecurityManager(new RMISecurityManager());  
  16.         String url = "rmi://"+host+":"+port+"/";  
  17.           
  18.         try{  
  19.             TaskContainer container =   
  20.                 (TaskContainer)Naming.lookup(url+"TaskContainer");  
  21.             while(container.getTaskCount() > 0){  
  22.                 container.getTask().execute();//invoke the remote method.  
  23.             }  
  24.         }catch(Exception e){  
  25.             e.printStackTrace();  
  26.         }  
  27.     }  
  28. }  

 同样,客户端也需要一个权限配置文件,TaskClient.policy

Java代码  收藏代码
  1. grant  
  2. {  
  3.  permission java.net.SocketPermission  
  4.       "*:1000-9999","connect";  
  5. };  

 好了,客户端的代码及配置到此结束。现在,我们可以运行这个典型的RMI了,为了清晰起见,我们在控制台上运行这两个程序(分别启两个cmd窗口,因为RMI的server端不会退出[CTRL+C]),先启动server端,再运行客户端,可以看到,server窗口的输出为:

Java代码  收藏代码
  1. Constructing server  
  2. binding server impl to registry  
  3. execute task = 1  
  4. execute task = 2  
  5. execute task = 3  

 而客户端没有人户输出就结束了,这正好说明了RMI的意义和流程,client调用的是远程的方法(即server端的对象的方法)

 


0 0
原创粉丝点击