邱俊的空间

Simple is beautiful.
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

[导入]RMI的简单例子

Posted on 2008-12-30 21:08  abruzzi  阅读(503)  评论(1编辑  收藏  举报


作者: abruzzi  链接:http://abruzzi.javaeye.com/blog/285034  发表时间: 2008年12月02日

声明:本文系JavaEye网站发布的原创博客文章,未经作者书面许可,严禁任何网站转载本文,否则必将追究法律责任!

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

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

想要被远程调用的接口需要扩展Remote类,接口中定义的方法需要对RemoteException异常进行处理,当然,也可以只是抛出异常,将对异常的处理延迟到别的类。RMI的安全处理机制使用的是RMISecurityManager,这个类提供必要的安全验证等处理,而权限控制是通过在客户端和服务端分别设置policy文件来做到的。下面给出一个比较简单,但是能说明问题的例子,略去了不必要的代码,而且这个程序可以通过扩展达到你的某些特殊要求。

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

 

 下面是这个接口的实现

package rmidemo;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.LinkedList;

public class TaskContainerImpl extends UnicastRemoteObject 
implements TaskContainer{

    
private static final long 
            serialVersionUID 
= 4656230894967394376L;
    LinkedList
<Task> taskList;
    
    
public TaskContainerImpl() throws RemoteException{
        taskList 
= new LinkedList<Task>();
    }
    
    
public Task getTask() throws RemoteException{
        
return taskList.size() > 0 ? 
                taskList.removeFirst() : 
null;
    }

    
public int getTaskCount() throws RemoteException{
        
return taskList.size();
    }

    
public void addTask(Task task) throws RemoteException{
        taskList.add(task);
    }
}

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

 

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

package rmidemo;

import java.rmi.Remote;
import java.rmi.RemoteException;

public interface Task extends Remote{
    
void execute() throws RemoteException;
    
int getTaskID() throws RemoteException;
}

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

package rmidemo;

import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;

public class TaskImpl extends UnicastRemoteObject implements Task{
    
private static final long serialVersionUID = -4520397655735981164L;
    
private int taskID;
    
    
protected TaskImpl(int i) throws RemoteException {
        taskID 
= i;
    }

    
public void execute() throws RemoteException{
        System.out.println(
"execute task = "+taskID);//这里只是一个简单的打印,你可以任意扩展之
    }
    
    
public int getTaskID() throws RemoteException{
        
return taskID;
    }
}

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

package rmidemo;

import java.rmi.Naming;
import java.rmi.RMISecurityManager;
import java.rmi.registry.LocateRegistry;

public class TaskServer {
    
private static final String host = "localhost";
    
private static final int port = 9527;
    
    
public static void main(String[] args){
        System.setProperty(
"java.security.policy"
        
"TaskServer.policy");//权限控制
        
        
if(System.getSecurityManager() == null){
            System.setSecurityManager(
new RMISecurityManager());
        }
//安全管理器的安装
        
        
try{
            System.out.println(
"Constructing server");
            LocateRegistry.createRegistry(port);
//注册
            
            TaskContainer container 
= new TaskContainerImpl();
            
            container.addTask(
new TaskImpl(1));//想容器中添加几个Task
            container.addTask(new TaskImpl(2));
            container.addTask(
new TaskImpl(3));
            
            System.out.println(
"binding server impl to registry");

            Naming.rebind(
"//"+host+":"+port+"/TaskContainer", container);
            
        }
catch(Exception e){
            e.printStackTrace();
        }
    }
}

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

 

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

grant
{
 permission java.net.SocketPermission
      
"*:1000-9999","accept,connect,listen,resolve";
};

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

 

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

 

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

$ rmic rmidemo.TaskContainerImpl
$ rmic rmidemo.TaskImpl

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

 

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

package rmidemo;

import java.rmi.Naming;
import java.rmi.RMISecurityManager;

public class TaskClient {
    
private static final String host = "localhost";
    
private static final int port = 9527;
    
    
public static void main(String[] args){
        System.setProperty(
"java.security.policy"
        
"TaskClient.policy");//install the policy file
        
        
//install RMI Security Manager
        System.setSecurityManager(new RMISecurityManager());
        String url 
= "rmi://"+host+":"+port+"/";
        
        
try{
            TaskContainer container 
= 
                (TaskContainer)Naming.lookup(url
+"TaskContainer");
            
while(container.getTaskCount() > 0){
                container.getTask().execute();
//invoke the remote method.
            }
        }
catch(Exception e){
            e.printStackTrace();
        }
    }
}

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

grant
{
 permission java.net.SocketPermission
      
"*:1000-9999","connect";
};

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

Constructing server
binding server impl to registry
execute task 
= 1
execute task 
= 2
execute task 
= 3

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

 

好了,这个简单的例子就说到这里,程序很简单,我就不打包了。如果有任何问题,可以留言。