Last Modified: May 17, 2004
Version: 1.6
<Ken Cavanaugh>We often need to examine problems in CORBA that are related to the details of marshalling messages for CDR encoded GIOP messages. This is currently a difficult and time-consuming problem due to the lack of tools for this task. Harsha Godugu is working on a GIOP analyzer to make this process easier. This note discusses some ORB changes needed to support the GIOP analyzer, and also to make other debugging tasks easier.
What we need is the following:
Probably the simplest possible representation for the data contained in a sequence of messages is simply byte[][]. Wherever I use this type to represent a sequence of messages, we will assume that:
If any of these conditions fail, getMessageData should throw an appropriate exception. getMessageData is a new method on the ORB SPI defined as follows:
/** Messages must be an array of GIOP messages. The first message must * be a GIOP request or response. Any subsequent messages must be * fragment messages with the same request ID as the first message. * This returns a MessageData interface, in which all of the Messages * have been unmarshalled, and the result of getStream is a stream * that is prepared to unmarshal the request or reply body. */ MessageData getMessageData( byte[][] messages ) ;
MessageData is an interface defined as follows:
public interface MessageData { /** An array GIOP Messages. The first message is a request or reply, * all subsequent messages are fragments with the same request ID. */ Message[] getMessages() ; /** A fully initialized input stream for the message data, positioned * at the first element of the body. */ CDRInputStream getStream() ; }
To support this, we need a Connection that is backed by a byte[][] or equivalent. I am currently testing this, which us called BufferedConnectionImpl.
The idea here is to provide an interface that allows the programmer to obtain the sequence of messages in the request and reply from a CORBA method invocation. The model I have designed for this is fairly simple and easy to use. First, we define a new interface called a MessageTraceManager:
public interface MessageTraceManager { /** Returns true if messages are to be captured on this thread, otherwise * false. */ boolean isEnabled() ; /** Called with flag=true to enable capture of messages */ void enable( boolean flag ) ; /** Return an array of messages (represented as byte[]) for the * message(s) sent for the request in the last invocation. In the * event of a location forward, this is the sequence for the last * invocation. */ byte[][] getRequestData() ; /** Return an array of messages (represented as byte[]) for the * message(s) received for the last invocation. In the event of * a location forward, this is the sequence received from the last * invocation. */ byte[][] getResponseData() ; }
This interface allows access to the messages sent and received in the last request. To obtain an instance of MessageTraceManager, we add the following API to the ORB SPI:
/** Return a MessageTraceManager for the current thread. Each thread that * calls this method gets its own MessageTraceManager. */ MessageTraceManager getMessageTraceManager()
This API allows message data to be collected from any synchronous CORBA invocation. This include RMI-IIOP, IDL, and DII (but not deferred synchronous invocations). A test could also simulate a stub invocation if desired.
Start with receipt of read event on a connection:
Note that we generally always need to call unmarshalRequestID before we can do much else, since the transport does not know where to put anything without knowing the request ID (it's obvious from the code that one of GIOP's minor blunders is not putting the request ID in the GIOP message header, since it is so frequently needed). Here is a sketch of the code for getMessageData( byte[][] data ):
public MessageData getMessageData( byte[][] data ) { connection = new BufferedConnectionImpl() ; for (int ctr=0; ctr<data.length; ctr++) // write data[ctr] to connection final Message[] messages = new Message[data.length] ; int requestID = 0 ; Message firstMessage = null ; Message msg = null ; CDRInputObject inobj = null ; BufferManagerRead buffman = null ; for (int ctr=0; ctr<data.length; ctr++) { msg = MessageBase.readGIOPMessage( orb, connectionCopy ) ; messages[ctr] = msg ; msg.unmarshalRequestID() ; // Do we always do this? // Check that moreFragments == (ctr < data.length) if (ctr==0) { // Check that we have a request or reply requestID = msg.getRequestID() ; firstMessage = msg ; requestID = msg.getRequestID() ; inobj = new CDRInputObject( orb, connection, msg.getByteBuffer(), msg ) ; buffman = inobj.getBufferManager() ; } else { // Check that the request ID is as expected buffman.processFragment( msg, msg.getByteBuffer() ) ; } } // Unmarshal all the data in the first message. This may // cause other fragments to be read. firstMessage.unmarshalHeader( stream ) ; final CDRInputObject resultObj = inobj ; return new MessageData() { Message[] getMessages() { return messages ; } CDRInputStream getInputStream() { return resultObj ; } } ; }