从头学Java EE(二)- 用RMI实现一个简单的例子

上一篇中,简单的介绍了RMI的运行机制,本篇将使用RMI实现一个简单的例子。在Server端实现一个CarFactory,Client端通过远程调用CarFactory的createCar()方法来获得Car对象,Client端的classpath中不需要CarFactory和Car,而是通过配置Server端的codebase来使Client端自动下载这些类,codebase通过class-server提供HTTP下载。Server端和Client端均通过JNDI来分别绑定和获取对象。Server和Client均部署在同一台物理机上,但运行在不同的JVM里面。 

为了模拟Server和Client的独立性,分别为Server和Client程序建立两个不同的工程。

Server端工程目录如下:

 

 

在Server端首先定义服务接口CarFactory:

 package com.thoughtworks.davenkin.rmi.car.server;

 import java.rmi.Remote;

 import java.rmi.RemoteException;

 public interface CarFactory extends Remote

 {

    public Car createCar(int speed) throws RemoteException;

 }

 

该接口应该继承自Remote接口。然后定义实现接口的CarFactoryImpl类:

package com.thoughtworks.davenkin.rmi.car.server;

import java.rmi.RemoteException;

import java.rmi.server.UnicastRemoteObject;

public class CarFactoryImpl extends UnicastRemoteObject implements CarFactory

{

    protected CarFactoryImpl() throws RemoteException

    {

        super();

    }

    @Override

    public Car createCar(int speed) throws RemoteException

    {

        return new Car(speed);

    }

}

 

CarFactoryImpl类实现了工厂方法creatCar(),用来制造Car对象: 

package com.thoughtworks.davenkin.rmi.car.server;

import java.io.Serializable;

public class Car implements Serializable

{

    private int speed;

 

    public Car(int speed)

    {

        this.speed = speed;

    }

    public void run()

    {

        System.out.println("I am running at a speed of "+ speed + " per hour!");

    }

}

 

由于Car对象需要从Server端传到Client端,故Car应该实现Serializable接口,另外定义了run()方法。CarServer类用于将CarFactoryImpl对象绑定到对应的JNDI名字上:

package com.thoughtworks.davenkin.rmi.car.server;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.naming.NamingException;

import java.rmi.RMISecurityManager;

import java.rmi.RemoteException;

import java.util.Hashtable;

public class CarServer

{

    private final InitialContext context;

    public CarServer() throws NamingException, RemoteException

    {

        if (System.getSecurityManager() == null)

        {

            System.setSecurityManager(new RMISecurityManager());

        }

        Hashtable<String, String> env = new Hashtable<String, String>();

        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");

        context = new InitialContext(env);

    }

    public void createBinding() throws RemoteException, NamingException

    {

        context.bind("/CarFactory", new CarFactoryImpl());

    }

    public static void main(String args[]) throws NamingException, RemoteException, InterruptedException

    {

        CarServer carServer = new CarServer();

        carServer.createBinding();

    }

}

 

在src目录下编译所有java类:

javac -d ../build/ com/thoughtworks/davenkin/rmi/car/server/*.java

 

为了在build目录下能够启动server端程序,在build目录下建立sercuriy.policy文件:

grant {  

    permission java.security.AllPermission "", "";  

}; 

 

在启动Server之前需要启动rmiregistry,rmiregistry可以在任意目录下启动。

在启动Server时,我们提供codebase,以使rmiregistry和Client能够自动下载需要的类文件。

 

我们用class-server来做codebase的http服务器,启动该http服务器:

java examples.classServer.ClassFileServer 2001 /path/to/server/build/dir

其中2001表示监听端口号。

 

启动Server:

java -cp path/to/server/build/dir -Djava.rmi.server.codebase="http://localhost:2001/" -Djava.security.policy=/path/to/security.policy com.thoughtworks.davenkin.rmi.car.server.CarServer

 

此时在class-server的终端中可以看到有类文件被下载:

reading: com.thoughtworks.davenkin.rmi.car.server.CarFactory

reading: com.thoughtworks.davenkin.rmi.car.server.Car

 

到此,Server端开发配置完成,接下来是Client端,建立另一个Client端工程,目录如下:

 

Client端只需要定义一个CarClient类:

 

package com.thoughtworks.davenkin.rmi.car.client;

import com.thoughtworks.davenkin.rmi.car.server.CarFactory;

import javax.naming.Context;

import javax.naming.InitialContext;

import javax.naming.NamingException;

import java.rmi.RMISecurityManager;

import java.rmi.RemoteException;

import java.util.Hashtable;

public class CarClient

{

    public static void main(String[] args) throws NamingException, RemoteException

    {

        if (System.getSecurityManager() == null)

        {

            System.setSecurityManager(new RMISecurityManager());

        }

        Hashtable env = new Hashtable();

        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.rmi.registry.RegistryContextFactory");

        InitialContext context = new InitialContext(env);

        CarFactory carFactory = (CarFactory) context.lookup("/CarFactory");

        carFactory.createCar(123).run();

    }

}

 

在编译CarClient类时,由于需要用到Server端的CarFactory等类,故应将CarFactory类加入到在classpath中,在Client端的src文件夹下运行:

javac -cp /path/to/server/build/dir -d ../build com/thoughtworks/davenkin/rmi/car/client/CarClient.java

 

由于Client程序需要下载Server端的类文件,同样在Client端的build目录下创建security.policy文件:

grant {  

    permission java.security.AllPermission "", "";  

};

 

最后运行CarClient:

java -cp /path/to/client/build/dir -Djava.security.policy=/path/to/security.policy com.thoughtworks.davenkin.rmi.car.client.CarClient

 

搞了一整天,一直出现一下错误:

 

Exception in thread "main" java.lang.NoClassDefFoundError: com/thoughtworks/davenkin/rmi/car/server/CarFactory

at com.thoughtworks.davenkin.rmi.car.client.CarClient.main(CarClient.java:24)

Caused by: java.lang.ClassNotFoundException: com.thoughtworks.davenkin.rmi.car.server.CarFactory

at java.net.URLClassLoader$1.run(URLClassLoader.java:202)

at java.security.AccessController.doPrivileged(Native Method)

at java.net.URLClassLoader.findClass(URLClassLoader.java:190)

at java.lang.ClassLoader.loadClass(ClassLoader.java:306)

at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)

at java.lang.ClassLoader.loadClass(ClassLoader.java:247)

... 1 more

 

在class-server下显示Car类和CarFactory都被下载了,但是还是说CarFactory找不到,不知道为什么。

你可能感兴趣的:(java,ee)