`
自己知道,生活的艰辛
  • 浏览: 5227 次
  • 性别: Icon_minigender_1
  • 来自: 上海
文章分类
社区版块
存档分类
最新评论

Understanding Java RMI Internals

阅读更多
Motivation for this Article
The motivation to write this article is my own experience. I came to know about Java RMI only recently. Instantly, I liked the concept very much, especially that of stubs and skeletons. It seemed very interesting to me how RMI designers have designed the whole framework such that the RMI client feels as if the method is invoked locally while it is actually executed in a remote object. As I learned more, I came to know about the RMIRegistry and more. There began the train of problems. I had many doubts, such as what is the actual role of RMIRegistry and is it absolutely necessary? Where is the object of the stub created (in the server, client, or RMIRegistry)? How will the client know to which port the server is listening? Which port is the server listening to? Are all the servers using port 1099 for listening to clients and so on.

This article is really an attempt to answer my own questions that I had previously. I referred to a lot of books and articles on RMI on the net, but I was unable to get any clue on the matter. Nobody told me how things are happening. Everyone was telling me how to program—only that, and nothing about how things really work in the lower levels. I got answers to my questions after a long time after doing a lot of research and experimenting. So, I decided I should share my knowledge to others because many guys might be having the same doubts.

Introduction
This article tries to answer the questions about RMI Internals, such as the following:

Who actually creates an object of stubs? The server, Registry, or client?
Which port is the server listening to?
Does the server listens to port 1099 (default port of RMI Registry)?
How will the client know to which port the server is listening
Is a Registry necessary to run the RMI system?
Can you use RMI without rmiregistry?
I will answer all these questions in detail in the following sections. If you want quick answers to these questions, you can directly go to the last part. Here, I will go in a step-by-step manner to give you an idea about what is happening in RMI.

Forget About RMIRegistry (for now)!!!
Yes, just forget about the RMIRegistry for now. Just assume such a thing does not exist as of now.

The initial scenario is like this: We have a server and a client. The server extends from java.rmi.server.UnicastRemoteObject. The client and server are running on different machines.

Now, our requirement is this: The client wants to execute a function of the server that is on a remote machine.

How that can be done? The Java RMI framework deals with this question. The solution certainly involves network programming using sockets because the server is running on a remote machine. The point is that solution to this problem should focus on a framework in which the client is decoupled from the networking programming code. Also, you should build the framework in such a way that the client should not be aware that the the function it is calling is actually run in a remote machine. It should appear as if the function is run locally. So, the RMI framework developers introduced the Stub and skeleton model (I assume you already know about stubs and skeletons).

What happens is that all the network-related code is put in the stub and skeleton so that the client and server won't have to deal with the network and sockets in their code. The stub implements the same Remote interface (as in an interface that extends java.rmi.Remote) that the server implements. So, the client can call the same methods in the stub that it wants to call in this server. But, the functions in the stub are filled with network-related code, not the actual implementation of the required function. For example, if a server implements an add(int,int) function, the stub also will have an add(int,int) function, but it won't contain the actual implementation of the addition function; instead, it will contain the code to connect to the remote skeleton, to send details about the function to be invoked, to send the parameters, and to get the results back.

So, this will be the situation:

Client<--->stub<--->[NETWORK]<--->skeleton<--->Server
The client speaks to the stub, the stub speaks to the skeleton through the network, the skeleton speaks to the server, the server executes the required function, and the results are also passed in the same way. So, you will have four separate programs corresponding to each of these entities (this means four class files).

Later, in JDK 1.2, the skeletons were incorporated into the server itself so that there are no separate entities as skeletons. In other words, the skeletons's functionality was incorporated into the server itself. So, the scenario became like this:

Client<--->stub<--->[NETWORK]<--->Server_with_skeleton_functionality
Socket-Level Details
Now, you can examine how the communication is achieved in the socket level. This is very important. This is where the concept usually gets twisted. So, take special note about this section.

The server listens to a port on the server machine. This port is usually an anonymous port that is chosen at runtime by the jvm or the underlying operating system. Or, you can say that the server exports itself to a port on the server machine.
The client has NO idea on which machine and to which port the server is listening. But, it has a stub object that knows all these. So, the client can invoke the desired method of the stub.
The client invokes the function of the stub.
The stub connects to the server's listening port and sends the parameters. The details of this are given below. You can skip it if you don't want to know about the intricate details about TCP/IP connection semantics. For those who are interested, this is how it is done.
The client connects to the server's listening port.
The server accepts the incoming connection and creates a new socket just to handle this single connection.
The old listening port is still there; it waits for the incoming requests from the clients.
The communication between client and server takes place using the newly created socket on the server.
With an agreed-upon protocol, they communicate and exchange parameters and results.
The protocol can be JRMP (Java Remote Method protocol) or CORBA-compatible RMI-IIOP (Internet Inter-ORB Protocol).
The method is executed on the server and the result is sent back to the stub.
The stub returns the results back to the client as if the stub had executed the function locally.
So, that is how it is done. Wait a second. Take a look at Point 2. The stub knows which host the server is running and to which port the server is listening. How is that possible?? If the client does not know about the server host and port, how can it create an object of the stub that knows all these? Especially when the server port is chosen arbitrarily or randomly when the server is instantiated, it will change for each object of the same server class. Once the client knows these details, it can proceed. Also, it should be noted that even if the client has CalcImpl_Stub.class in its machine, it cannot simply create an object of the stub because its constructor takes a RemoteRef reference as a parameter and you can get that only from an object of the remote server—exactly what we are trying to access! This is called a Bootstrap Problem.

Bootstrap Problem!!!
This is one of the biggest problems in many real-life systems. The solution depends on how we can inform the client about the details of the server. RMI designers have a work-around for the bootstrap problem. That is where the RMIRegistry comes in. The Registry can be thought of as a service that keeps (public_name, Stub_object) pairs in a hashmap. For example, if I have a Remote Server object called Scientific_Calculator, I can make it available by a public name, "calc". For this, I will create a Stub Object at the server machine and register it with the RMIRegistry so that clients who want to access the services of the Remote Server object can get the Stub Object from the Registry. For doing these things, you use a class called java.rmi.Naming.

Let me illustrate this with an example.

Consider a calculator application. It has an add(int,int) function. You want to make this function available to remote clients on other machines. We have the Remote interface called Calc.

Source code of Calc.java:
public interface Calc extends Remote
{
   public int add(int i,int j)throws RemoteException;
}

We have the server class CalcImpl.

Source code of CalcImpl.java:
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;

public class CalcImpl extends UnicastRemoteObject implements Calc
{
   public CalcImpl()throws RemoteException
   {
      super();
   }

   public int add(int i,int j)throws RemoteException
   {
      return i+j;
   }

   public static void main(String[] args)throws Exception
   {
      CalcImpl c=new CalcImpl();
      Naming.rebind("rmi://localhost:1099/calc",c);
   }
}

We have the client class CalcClient.

Source code of CalcClient.java:
import java.rmi.Naming;

public class CalcClient
{
   public static void main(String[] args)throws Exception
   {
      Calc c=(Calc)Naming.lookup("rmi://remotehost:1099/calc");
      System.out.println(c.add(3,4));
   }
}

Note: You have to replace remotehost with the host name or IP address of the server machine.
These are the classes. Compile all the java files to get the class files. Now, use the RMI compiler to generate the stub. Use the .keep option if you need the source file of the stub.

C:\test>rmic .keep CalcImpl
This will create two files: CalcImpl_Stub.class and CalcImpl_Stub.java.

Source code for CalcImpl_Stub.java:
public final class CalcImpl_Stub
   extends java.rmi.server.RemoteStub
   implements Calc, java.rmi.Remote
{
   private static final long serialVersionUID = 2;
   private static java.lang.reflect.Method $method_add_0;
   static {
      try {
         $method_add_0 = Calc.class.getMethod("add",
            new java.lang.Class[] {int.class, int.class});
      } catch (java.lang.NoSuchMethodException e) {
         throw new java.lang.NoSuchMethodError(
            "stub class initialization failed");
      }
   }

   // constructors
   public CalcImpl_Stub(java.rmi.server.RemoteRef ref) {
      super(ref);
   }

   // methods from remote interfaces

   // implementation of add(int, int)
   public int add(int $param_int_1, int $param_int_2)
      throws java.rmi.RemoteException

      try {
         Object $result = ref.invoke(this, $method_add_0,
                          new java.lang.Object[]
                          {new java.lang.Integer($param_int_1),
                          new java.lang.Integer($param_int_2)},
                          -7734458262622125146L);
      return ((java.lang.Integer) $result).intValue();
      } catch (java.lang.RuntimeException e) {
         throw e;
      } catch (java.rmi.RemoteException e) {
         throw e;
      } catch (java.lang.Exception e) {
         throw new java.rmi.UnexpectedException("undeclared checked
                                                 exception", e);
      }
   }
}

Right now, just don't worry much about what it does; just have a look at the constructor, which you might need later.

Now to run this code on the server machine, we need two consoles.

On console 1, run

C:\test>rmiregistry
On console 2, run

C:\test>java CalcImpl
On the client machine, run the following on a console,

C:\test>java CalcClient
You will get an output of 7 on your screen.

What Is Happening Here
I'll show you what is really happening behind the scenes.

First, RMIRegistry is run on the server machine. RMIRegistry itself is a remote object. The point to note here is this: All remote objects (in other words, all objects that extend UnicastRemoteObject) export themselves to an arbitrary port on the server machine. Because RMIRegistry is also a remote object, it exports itself into a port. A difference is that the the port is a well-known port. By default, it is 1099. The term well-known means that the port is known to all the clients.
Now, the server is run on the server machine. In UnicastRemoteObject's constructor, it exports itself to an anonymous port on the server machine. This port is unknown to the clients; only the server knows about this port.
When you call Naming.rebind(), you are passing a reference of CalcImpl (here c) as the second parameter to the Naming class. The Naming class constructs an object of the Stub and binds it past the stub object (not the actual object) to the Registry remote object. How is the stub object created? Here it is:
The naming class uses the getClass() method to get the name of the class (here, CalcImpl).
Then, it adds _Stub to the name of the class to get the stub's name (here, CalcImpl_Stub).
It loads the CalcImpl_Stub class to the JVM.
It gets a RemoteRef obj from c.
RemoteRef ref=c.getRef();
It is this ref that encapsulate all the details about the server such as server hostname, server's listening port, address, and so on.
It uses this RemoteRef object to construct the stub:
CalcImpl_Stub stub=new CalcImpl_Stub(ref);
If you look at CalcImpl_Stub.java, you will find that the stub's constructor takes RemoteRef reference as the parameter.

It passes this stub object to RMIRegistry for binding, along with the public name as (calc,stub).
RMIRegistry stores this public name and stub object in a hashmap internally.
When the client executes Naming.lookup(), it passes the public name as the parameter. The RMIRegistry (which itself is a remote object) returns the stored stub object back to the client. Now, the client gets a stub object that knows about the server host name and port to which the server listens. The client can invoke the stub's method to call the remote object's method.
Is RMIRegistry Absolutely Necessary?
Can I Do Away With RMIRegistry and Bind/Rebind/Lookup Methods?
That is how it works. Is RMIRegistry an absolute necessity? From the above discussion, it is evident that an RMIRegistry is not a must. RMIRegistry is used just for the bootstrapping purpose, nothing else. Once the client gets the stub object from the Registry, the Registry is not used anymore. The communication takes place directly between client and server by using sockets. So, if you can provide the bootstrapping in some other way, you can eliminate the use of Registry itself. In other words, if the client can get an object of the stub from the server somehow, that is just enough. That means if you have some facility to transport the stub object created in the server to the client machine, you don't have to use the RMIRegistry or bin/rebind/lookup methods. That sounds great. eh? Also, it should be noted that even if the client has CalcImpl_Stub.class on its machine, it cannot simply create an object of the stub because its constructor takes a RemoteRef reference as a parameter; you can get only from an object of the remote server, which is exactly what we are trying to access! To facilitate the transportation of the stub object from server to client, you can build a transport facility of your own that is based on sockets that serialize the Stub object and sends it to the clients. That will eliminate the need of the Registry at all!

I will give you an example that does not use the RMIRegistry. But, in this example, the client and server run on the same machine. You can use socket programming as I stated earlier if the client and server are on different machines, but the program will make the basic concepts clear.

Source code of CalcImpl.java:
import java.rmi.server.UnicastRemoteObject;
import java.rmi.RemoteException;

public class CalcImpl extends UnicastRemoteObject implements Calc
{
   public CalcImpl()throws RemoteException
   {
      super();
   }

   public int add(int i,int j)throws RemoteException
   {
      return i+j;
   }

   public static void main(String[] args)throws Exception
   {
      CalcImpl ci=new CalcImpl();
      RemoteRef ref=ci.getRef();
      CalcClient cc=new CalcClient(ref);
   }
}

Source code of CalcClient.java:
import java.rmi.server.*;
import java.rmi.*;

public class CalcClient
{
   public CalcClient(RemoteRef ref)throws RemoteException
   {
      CalcImpl_Stub stub=new CalcImpl_Stub(ref);
      System.out.println(stub.add(3,4));
   }
}

Both Calc.java and CalcImpl_Stub.java are the same as in the previous example. Now, compile the classes and run CalcImpl and CalcClient on two different consoles.

On console 1:

C:\test2>java CalcImpl
On console 2:

C:\test2>java CalcClient
You will get an output of 7 on your screen.

That is it!!!

http://www.developer.com/java/ent/article.php/10933_3455311_1
分享到:
评论

相关推荐

    基于JAVA RMI的聊天室

    **基于JAVA RMI的聊天室** Java Remote Method Invocation(RMI)是Java平台提供的一种用于在分布式环境中调用远程对象的方法。在这个“基于JAVA RMI的聊天室”项目中,开发者利用RMI技术构建了一个简单的多用户...

    java RMI技术实现的网络聊天室

    Java RMI(Remote Method Invocation)技术是Java平台中用于分布式计算的一种机制,它允许一个Java对象调用远程计算机上的另一个Java对象的方法。在本案例中,“java RMI技术实现的网络聊天室”是一个使用RMI构建的...

    javaRMI反序列化漏洞验证工具

    Java RMI(Remote Method Invocation,远程方法调用)是一种Java技术,允许在分布式环境中执行远程对象的方法。这个技术的核心是序列化和反序列化过程,它使得对象可以在网络上进行传输。然而,这个特性也可能引入...

    java RMI实现代码

    Java RMI(Remote Method Invocation,远程方法调用)是Java平台提供的一种分布式计算技术,它允许在不同的Java虚拟机之间进行远程对象的调用。RMI使得开发者可以像调用本地对象一样调用网络上的对象,极大地简化了...

    java rmi java rmi

    根据提供的文件信息,我们可以深入探讨Java RMI(Java Remote Method Invocation)的相关知识点,包括其概念、原理、体系结构以及一个具体的示例。 ### RMI的概念 RMI是一种Java技术,它允许开发者创建分布式应用...

    java_rmi.rar_RMI java_java.rmi

    Java RMI(Remote Method Invocation,远程方法调用)是Java平台提供的一种分布式计算技术,它允许Java对象在不同的网络环境中进行交互,就像它们在同一个进程内一样。RMI是Java在分布式系统领域的核心特性,极大地...

    JavaRMI快速入门

    Java Remote Method Invocation(Java RMI)是Java编程语言中用于在网络间进行远程对象调用的技术。它是Java平台的标准部分,允许程序员在分布式环境中调用对象的方法,就像它们在同一台计算机上一样。Java RMI对于...

    java rmi 参考文档

    ### Java RMI (Remote Method Invocation) 概念与实践 #### 一、Java RMI简介 Java RMI(Remote Method Invocation)是一种允许调用不同Java虚拟机(JVM)上方法的机制。这些JVM可能位于不同的机器上,也可能在同一...

    java RMI简单Demo

    Java RMI(Remote Method Invocation,远程方法调用)是Java平台提供的一种分布式计算技术,它允许在不同网络节点上的Java对象之间进行透明的交互。在Java RMI中,一个对象可以调用另一个位于不同JVM(Java虚拟机)...

    JavaRMI超棒书

    ### Java远程方法调用(Java RMI)核心概念与应用详解 #### 一、Java RMI简介 Java远程方法调用(Java Remote Method Invocation,简称Java RMI)是一种用于实现远程对象之间通信的技术,它是Java平台的一个核心...

    Java RMI 简单示例

    Java RMI(Remote Method Invocation,远程方法调用)是Java平台提供的一种用于分布式计算的技术,它允许一个Java对象调用另一个在不同 JVM(Java虚拟机)上的对象的方法。这个简单的示例展示了如何创建一个基本的...

    RMI.rar_Java RMI_java.rmi_java.rmi.Remot_remote

    Java RMI(远程方法调用)是Java编程语言中的一项核心技术,自JDK 1.1版本起就被引入,用于构建分布式系统。RMI允许Java对象在不同的Java虚拟机(JVMs)之间进行交互,仿佛这些对象是在同一台机器上一样。这种技术的...

    JavaRMI.pdf

    Java RMI(Remote Method Invocation)是Java编程语言中用于实现远程过程调用的一种技术。它允许运行在客户机上的程序调用位于远程服务器上的对象的方法,从而实现分布式计算。RMI的核心思想是通过接口隐藏底层网络...

    JAVA RMI.rar_Java RMI_ME_RMI java_rmi

    Java Remote Method Invocation (RMI) 是Java平台中用于构建分布式应用程序的一种重要技术。RMI允许Java对象在不同的Java虚拟机(JVM)之间调用方法,从而实现了远程对象的透明访问。这个RAR文件"JAVA RMI.rar"包含...

    javaRMI完整版.pdf

    Java RMI 完整版 Java Remote Method Invocation(RMI)是一种分布式对象技术,允许使用 Java 编写分布式对象,不同的 Java 虚拟机(JVM)之间进行对象间的通讯。这使得应用程序(Application)可以远程调用方法,...

    JAVA RMI简单例子

    Java RMI(Remote Method Invocation,远程方法调用)是Java平台提供的一种分布式计算技术,它允许在不同的Java虚拟机之间进行方法调用,仿佛这些方法都在本地对象上执行一样。这个"JAVA RMI简单例子"旨在帮助我们...

    Java RMI中文规范

    Java RMI(Remote Method Invocation,远程方法调用)是Java平台中用于构建分布式对象系统的关键技术。它允许Java应用程序在不同Java虚拟机(JVM)之间进行远程方法调用,这些虚拟机可能位于同一台计算机或网络上的...

    java rmi HelloWorld版(源码)

    Java RMI,全称为Remote Method Invocation,是Java平台上的一个标准API,用于实现分布式计算,使得在不同Java虚拟机(JVM)上的对象能够互相调用方法。这个"java rmi HelloWorld版(源码)"的压缩包文件提供了一个...

    JAVA RMI

    **JAVA RMI(远程方法调用)详解** Java RMI(Remote Method Invocation)是Java平台上的一个核心特性,它允许Java程序在不同的JVM(Java虚拟机)之间进行分布式计算,实现了对象间的远程调用。RMI使得开发者可以像...

    JAVA RMI 传输 SSL加密

    Java RMI (Remote Method Invocation) 是一种用于在Java应用程序之间进行远程通信的技术。为了提高RMI通信的安全性,我们可以使用SSL (Secure Sockets Layer) 或其后继者TLS (Transport Layer Security) 进行加密。...

Global site tag (gtag.js) - Google Analytics