- Overview
- RMI Transport Protocol
- RMI's Use of Object Serialization
- RMI's Use of HTTP POST Protocol
- Application-Specific Values for RMI
10.1 Overview
The RMI protocol makes use of two other protocols for its on-the-wire format: Java Object Serialization and HTTP. The Object Serialization protocol is used to marshal call and return data. The HTTP protocol is used to "POST" a remote method invocation and obtain return data when circumstances warrant. Each protocol is documented as a separate grammar. Nonterminal symbols in production rules may refer to rules governed by another protocol (either Object Serialization or HTTP). When a protocol boundary is crossed, subsequent productions use that embedded protocol.
Notes about Grammar Notation
- We use a similar notation to that used in The Java Language Specification.
- Control codes in the stream are represented by literal values expressed in hexadecimal.
- Some nonterminal symbols in the grammar represent application specific values supplied in a method invocation. The definition of such a nonterminal consists of its Java programming language type. A table mapping each of these nonterminals to its respective type follows the grammar.
10.2 RMI Transport Protocol
The wire format for RMI is represented by a Stream
. The
terminology adopted here reflects a client perspective. Out
refers to output messages and In
refers to input messages.
The contents of the transport header are not formatted using
object serialization.
Stream:
Out
In
The input and output streams used by RMI are paired. Each
Out
stream has a corresponding In
stream. An
Out
stream in the grammar maps to the output stream of a
socket (from the client's perspective). An In
stream (in
the grammar) is paired with the corresponding socket's input stream.
Since output and input streams are paired, the only header information
needed on an input stream is an acknowledgment as to whether the
protocol is understood; other header information (such as the magic
number and version number) can be implied by the context of stream
pairing.
10.2.1 Format of an Output Stream
An output stream in RMI consists of transport Header
information followed by a sequence of Messages
.
Alternatively, an output stream can contain an invocation embedded in
the HTTP protocol.
Out:
Header Messages
HttpMessage
Header:
0x4a 0x52 0x4d 0x49 Version Protocol
Version:
0x00 0x01
Protocol:
StreamProtocol
SingleOpProtocol
StreamProtocol:
0x4b
SingleOpProtocol:
0x4c
Messages:
Message
Messages Message
The Messages
are wrapped within a particular protocol as
specified by Protocol
. For the
SingleOpProtocol
, there may only be one
Message
after the Header
, and there is no
additional data that the Message
is wrapped in. The
SingleOpProtocol
is used for invocation embedded in HTTP
requests, where interaction beyond a single request and response is not
possible.
For the StreamProtocol
the server must respond with a
byte 0x4e
acknowledging support for the protocol, and an
EndpointIdentifier
that contains the host name and port
number that the server can see is being used by the client. The client
can use this information to determine its host name if it is otherwise
unable to do that for security reasons. The client must then respond
with another EndpointIdentifier
that contains the client's
default endpoint for accepting connections.
For the StreamProtocol
, after this endpoint negotiation,
the Messages
are sent over the output stream without any
additional wrapping of the data.
There are three types of output messages: Call
,
Ping
and DgcAck
. A Call
encodes a
method invocation. A Ping
is a transport-level message for
testing liveness of a remote virtual machine. A DgcAck
is
an acknowledgment directed to a server's distributed garbage collector
that indicates that remote objects in a return value from a server have
been received by the client.
Message:
Call
Ping
DgcAck
Call:
0x50 CallData
Ping:
0x52
DgcAck:
0x54 UniqueIdentifier
Note: The obsolete Protocol
MultiplexProtocol (0x4d)
has been removed as of Java SE
9.
10.2.2 Format of an Input Stream
There are currently three types of input messages:
ReturnData
, HttpReturn
and
PingAck
. ReturnData
is the result of a
"normal" RMI call. An HttpReturn
is a return result from an
invocation embedded in the HTTP protocol. A PingAck
is the
acknowledgment for a Ping
message.
In:
ProtocolAck Returns
ProtocolNotSupported
HttpReturn
ProtocolAck:
0x4e
ProtocolNotSupported:
0x4f
Returns:
Return
Returns Return
Return:
ReturnData
PingAck
ReturnData:
0x51 ReturnValue
PingAck:
0x53
10.3 RMI's Use of Object Serialization
Call and return data in RMI calls are formatted using the Java Object
Serialization protocol. Each method invocation's CallData
is written to a Java object output stream that contains the
ObjectIdentifier
(the target of the call), an
Operation
(a number representing the method to be invoked),
a Hash
(a number that verifies that client stub and remote
object skeleton use the same stub protocol), followed by a list of zero
or more Arguments
for the call.
In the JDK1.1 stub protocol the Operation
represents the
method number as assigned by rmic,
and the
Hash
was the stub/skeleton hash which is the stub's
interface hash. As of the Java 2 stub protocol (Java 2 stubs are
generated using the -v1.2
option with rmic
),
Operation
has the value -1 and the Hash
is a
hash representing the method to call. The hash is described in the
section "The
RemoteRef
Interface".
CallData:
ObjectIdentifier Operation Hash Arguments[opt]
ObjectIdentifier:
ObjectNumber UniqueIdentifier
UniqueIdentifier:
Number Time Count
Arguments:
Value
Arguments Value
Value:
Object
Primitive
A ReturnValue
of an RMI call consists of a return code
to indicate either a normal or exceptional return, a
UniqueIdentifier
to tag the return value (used to send a
DGCAck
if necessary) followed by the return result: either
the Value
returned or the Exception
thrown.
ReturnValue:
0x01 UniqueIdentifier Value[opt]
0x02 UniqueIdentifier Exception
Note: ObjectIdentifier
,
UniqueIdentifier,
and EndpointIdentifier
are
not written out using default serialization, but each uses its own
special write
method (this is not the
writeObject
method used by object serialization); the
write
method for each type of identifier adds its component
data consecutively to the output stream.
10.3.1 Class Annotation and Class Loading
RMI overrides the annotateClass
and
resolveClass
methods of ObjectOutputStream
and
ObjectInputStream
respectively. Each class is annotated
with the codebase URL (the location from which the class can be loaded).
In the annotateClass
method, the classloader that loaded
the class is queried for its codebase URL. If the classloader is
non-null
and the classloader has a non-null
codebase, then the codebase is written to the stream using the
ObjectOutputStream.writeObject
method; otherwise a
null
is written to the stream using the
writeObject
method. Note: as an optimization, classes in
the "java
" package are not annotated, since they are always
available to the receiver.
The class annotation is resolved during deserialization using the
ObjectInputStream.resolveClass
method. The
resolveClass
method first reads the annotation via the
ObjectInputStream.readObject
method. If the annotation, a
codebase URL, is non-null
, then it obtains the classloader
for that URL and attempts to load the class. The class is loaded by
using a java.net.URLConnection
to fetch the class bytes
(the same mechanism used by a web browser's applet classloader).
10.4 RMI's Use of HTTP POST Protocol
The implementation of RMI calls through firewalls via proxies has been removed as of JDK 9.
10.5 Application-Specific Values for RMI
This table lists the nonterminal symbols that represent application-specific values used by RMI. The table maps each symbol to its respective type. Each is formatted using the protocol in which it is embedded.
Symbol | type |
---|---|
Count |
short |
Exception |
java.lang.Exception |
Hash |
long |
Hostname |
UTF |
Number |
int |
Object |
java.lang.Object |
ObjectNumber |
long |
Operation |
int |
PortNumber |
int |
Primitive |
byte , int ,
short , long ... |
Time |
long |