/*************************************************************************
*
* $RCSfile: iiop.java,v $
*
* $Revision: 1.4 $
*
* last change: $Author: kr $ $Date: 2001/02/02 09:01:03 $
*
* The Contents of this file are made available subject to the terms of
* either of the following licenses
*
* - GNU Lesser General Public License Version 2.1
* - Sun Industry Standards Source License Version 1.1
*
* Sun Microsystems Inc., October, 2000
*
* GNU Lesser General Public License Version 2.1
* =============================================
* Copyright 2000 by Sun Microsystems, Inc.
* 901 San Antonio Road, Palo Alto, CA 94303, USA
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License version 2.1, as published by the Free Software Foundation.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
* MA 02111-1307 USA
*
*
* Sun Industry Standards Source License Version 1.1
* =================================================
* The contents of this file are subject to the Sun Industry Standards
* Source License Version 1.1 (the "License"); You may not use this file
* except in compliance with the License. You may obtain a copy of the
* License at http://www.openoffice.org/license.html.
*
* Software provided under this License is provided on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
* See the License for the specific provisions governing your rights and
* obligations concerning the Software.
*
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
*
* Copyright: 2000 by Sun Microsystems, Inc.
*
* All Rights Reserved.
*
* Contributor(s): _______________________________________
*
*
************************************************************************/
package com.sun.star.lib.uno.protocols.iiop;
import java.io.IOException;
import java.io.DataOutput;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Array;
import java.util.Enumeration;
import java.util.Vector;
import com.sun.star.container.NoSuchElementException;
import com.sun.star.corba.CorbaString8;
import com.sun.star.corba.LogicalThreadID;
import com.sun.star.corba.OneThreadID;
import com.sun.star.corba.ObjectKey;
import com.sun.star.corba.giop.IORAddressingInfo;
import com.sun.star.corba.giop.MessageHeader_1_1;
import com.sun.star.corba.giop.MsgType_1_1;
import com.sun.star.corba.giop.RequestHeader_1_2;
import com.sun.star.corba.giop.ReplyHeader_1_2;
import com.sun.star.corba.giop.ReplyStatusType_1_2;
import com.sun.star.corba.giop.TargetAddress;
import com.sun.star.corba.giop.Version;
import com.sun.star.corba.iop.IOR;
import com.sun.star.corba.iop.ServiceContext;
import com.sun.star.corba.iop.TaggedProfile;
import com.sun.star.uno.IBridge;
import com.sun.star.uno.Type;
import com.sun.star.uno.Enum;
import com.sun.star.lib.uno.environments.remote.IMarshal;
import com.sun.star.lib.uno.environments.remote.IMessage;
import com.sun.star.lib.uno.environments.remote.IUnmarshal;
import com.sun.star.lib.uno.environments.remote.IProtocol;
import com.sun.star.lib.uno.environments.remote.Job;
import com.sun.star.lib.uno.environments.remote.Protocol;
import com.sun.star.lib.uno.environments.remote.ThreadID;
import com.sun.star.lib.uno.typedesc.MethodDescription;
import com.sun.star.lib.uno.typedesc.TypeDescription;
/**
* This class implements the complete IIOP protocol
* from corba. The functionality is reachable through
* the <code>IProtocol</code> interface.
* <p>
* @version $Revision: 1.4 $ $ $Date: 2001/02/02 09:01:03 $
* @author Kay Ramme
* @see com.sun.star.lib.uno.environments.remote.IProtocol
* @since UDK1.0
*/
public class iiop extends Protocol {
/**
* When set to true, enables various debugging output.
*/
static public final boolean DEBUG = false;
static public final TypeDescription __ObjectKeyTypeDescription = TypeDescription.getTypeDescription(ObjectKey.class);
static public final TypeDescription __LogicalThreadIDTypeDescription = TypeDescription.getTypeDescription(LogicalThreadID.class);
static public final TypeDescription __RequestHeader_1_2TypeDescription = TypeDescription.getTypeDescription(RequestHeader_1_2.class);
static public final TypeDescription __ReplyHeader_1_2TypeDescription = TypeDescription.getTypeDescription(ReplyHeader_1_2.class);
static private int int_convertEndianness(int value) {
int b4 = value & 0xFF;
int b3 = (value >> 8) & 0xFF;
int b2 = (value >> 16) & 0xFF;
int b1 = (value >> 24) & 0xFF;
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
}
static private byte magic[] = new byte[] {(byte)'G', (byte)'I', (byte)'O', (byte)'P'};
static private MessageHeader_1_1 readMessageHeader(InputStream inputStream) throws IOException {
DataInputStream dataInputStream = new DataInputStream(inputStream);
MessageHeader_1_1 messageHeader = new MessageHeader_1_1(dataInputStream.readByte(),
dataInputStream.readByte(),
dataInputStream.readByte(),
dataInputStream.readByte(),
new Version(dataInputStream.readByte(), // version major
dataInputStream.readByte()), // version minor
dataInputStream.readByte(), // flags
dataInputStream.readByte(),
dataInputStream.readInt());
if(messageHeader.flags != 0)
messageHeader.message_size = int_convertEndianness(messageHeader.message_size);
if(DEBUG) System.err.println("##### " + iiop.class.getName() + ".readMessageHeader:" + messageHeader.message_type + " " + messageHeader.message_size);
if((messageHeader.magic_1 != magic[0])
|| (messageHeader.magic_2 != magic[1])
|| (messageHeader.magic_3 != magic[2])
|| (messageHeader.magic_4 != magic[3]))
throw new IOException("invalid magic:"
+ " " + (char)messageHeader.magic_1
+ " " + (char)messageHeader.magic_2
+ " " + (char)messageHeader.magic_3
+ " " + (char)messageHeader.magic_4);
return messageHeader;
}
static private void writeMessageHeader(int type, int size, DataOutput dataOutput) throws IOException {
if(DEBUG) System.err.println("##### " + iiop.class.getName() + ".writeMessageHeader:" + type + " " + size);
dataOutput.writeByte(magic[0]);
dataOutput.writeByte(magic[1]);
dataOutput.writeByte(magic[2]);
dataOutput.writeByte(magic[3]);
dataOutput.writeByte(1); // version major
dataOutput.writeByte(2); // version minor
dataOutput.writeByte(0); // flags
dataOutput.writeByte(type);
dataOutput.writeInt(size);
}
static private ObjectKey unmarshalObjectKey(boolean littleEndian, byte bytes[]) throws Exception {
Unmarshal unmarshal = new Unmarshal(bytes, bytes.length, littleEndian, null);
return (ObjectKey)unmarshal.readObject(__ObjectKeyTypeDescription);
}
static private LogicalThreadID unmarshalLogicalThreadID(boolean littleEndian, byte bytes[]) throws Exception {
Unmarshal unmarshal = new Unmarshal(bytes, bytes.length, littleEndian, null);
return (LogicalThreadID)unmarshal.readObject(__LogicalThreadIDTypeDescription);
}
protected int _requestId;
protected IBridge _iBridge;
protected boolean _bIgnoreNextCloseConnection = false;
protected Vector _flushList;
class Message implements IMessage {
String _oid;
Object _object;
TypeDescription _typeDescription;
MethodDescription _methodDescription;
String _operation;
ThreadID _threadId;
Unmarshal _unmarshal;
boolean _synchron;
boolean _exception;
Object _params[];
Message(String oid,
Object object,
TypeDescription typeDescription,
MethodDescription methodDescription,
String operation,
ThreadID threadId,
Unmarshal unmarshal,
boolean synchron,
boolean exception,
Object params[])
{
_oid = oid;
_object = object;
_typeDescription = typeDescription;
_methodDescription = methodDescription;
_operation = operation;
_threadId = threadId;
_unmarshal = unmarshal;
_synchron = synchron;
_exception = exception;
_params = params;
}
public String getOperation() {
return _operation;
}
public ThreadID getThreadID() {
return _threadId;
}
public TypeDescription getInterface() {
return _typeDescription;
}
public boolean isSynchron() {
return _synchron;
}
public boolean mustReply() {
return _synchron;
}
public boolean isException() {
return _exception;
}
public String getOid() {
return _oid;
}
public Object getData(Object params[][]) throws Exception {
Object result = _object;
if(_exception) {
// String exceptionName = _unmarshal.read_asciistring();
result = _unmarshal.readThrowable();
// result = _unmarshal.readObject(Class.forName(exceptionName));
}
else {
params[0] = _params;
if(_operation == null) { // is it a reply?
result = _unmarshal.readObject((TypeDescription)_methodDescription.getReturnSig());
for(int i = 0; i < _params.length; ++ i)
if(_methodDescription.getOutSignature()[i] != null) // is it an out parameter
Array.set(_params[i], 0, _unmarshal.readObject(_methodDescription.getOutSignature()[i].getComponentType()));
}
else { // it is a request
for(int i = 0; i < _params.length; ++ i) {
if(_methodDescription.getInSignature()[i] != null) // is it an in parameter?
if(_methodDescription.getOutSignature()[i] != null) {// is it also an out -> inout?
Object inout[] = (Object[])Array.newInstance(_methodDescription.getOutSignature()[i].getComponentType().getZClass(), 1);
inout[0] = _unmarshal.readObject(_methodDescription.getOutSignature()[i].getComponentType());
_params[i] = inout;
}
else // it is only an in parameter
_params[i] = _unmarshal.readObject(_methodDescription.getInSignature()[i]);
else // it is only an out parameter, so provide the holder
_params[i] = Array.newInstance(_methodDescription.getOutSignature()[i].getComponentType().getZClass(), 1);
}
}
}
return result;
}
}
public iiop(IBridge iBridge) {
_iBridge = iBridge;
_flushList = new Vector();
}
static private byte[] marshalObjectKey(String oId, String sType) throws Exception {
Marshal marshal = new Marshal(false, null);
marshal.writeObject(__ObjectKeyTypeDescription, new ObjectKey(new CorbaString8(oId), new CorbaString8(sType)));
byte bytes[] = marshal.reset();
return bytes;
}
static private byte[] marshalLogicalThreadID(LogicalThreadID logicalThreadID) throws Exception {
Marshal marshal = new Marshal(false, null);
marshal.writeObject(__LogicalThreadIDTypeDescription, logicalThreadID);
return marshal.reset();
}
/**
* Returns a string representation of this object.
* <p>
* @return the string representation
* @see java.lang.Object#toString
*/
public String toString() {
return "iiop: statistics - messages created:" + _requestsSend + " requests recieved:" + _requestsRecieved;
}
/**
* Gets the name of the protocol.
* <p>
* @result the name of the protocol (iiop)
* @see com.sun.star.lib.uno.environments.remote.IProtocol#getName
*/
public String getName() {
return "iiop";
}
/**
* Tells the protocol to ignore the next <code>closeConnection</code>
* meta request.
* <p>
* @see com.sun.star.lib.uno.environments.remote.IProtocol#ignore_next_closeConnection
*/
public void ignore_next_closeConnection() {
_bIgnoreNextCloseConnection = true;
}
/**
* Tells the protocol to send a <code>closeConnection</code>
* meta request.
* <p>
* @param outputStream the output stream
* @see com.sun.star.lib.uno.environments.remote.IProtocol#send_closeConnection
*/
public void send_closeConnection(OutputStream outputStream) throws IOException {
writeMessageHeader(MsgType_1_1.CloseConnection_value, 0, new DataOutputStream(outputStream));
}
private IMessage readRequest(Unmarshal unmarshal) throws Exception {
++ _requestsRecieved;
RequestHeader_1_2 requestHeader = (RequestHeader_1_2)unmarshal.readObject(__RequestHeader_1_2TypeDescription);
LogicalThreadID logicalThreadID = unmarshalLogicalThreadID(unmarshal.isLittleEndian(), requestHeader.service_context[0].context_data);
ObjectKey objectKey = unmarshalObjectKey(unmarshal.isLittleEndian(), requestHeader.target.object_key);
boolean synchron = (requestHeader.response_flags != 0); // if != 0, then it should be 3
TypeDescription typeDescription = TypeDescription.getTypeDescription(objectKey.sType.theString);
ThreadID threadId = new ThreadID(logicalThreadID.IDs[0].threadID);
MethodDescription methodDescription = typeDescription.getMethodDescription(requestHeader.operation.theString);
Object params[] = new Object[methodDescription.getInSignature().length];
if(synchron) // if the request is synchron, it is pending
putPendingReply(threadId, new Object[]{params, methodDescription.getOutSignature(), methodDescription.getReturnSig(), new Integer(requestHeader.request_id)});
IMessage iMessage = new Message(objectKey.sOid.theString,
null,
typeDescription,
methodDescription,
requestHeader.operation.theString,
threadId,
unmarshal,
synchron,
false,
params);
if(DEBUG) System.err.println("##### " + getClass().getName() + ".readRequest:" + iMessage);
return iMessage;
}
private IMessage readReply(Unmarshal unmarshal) throws Exception {
ReplyHeader_1_2 replyHeader = (ReplyHeader_1_2)unmarshal.readObject(__ReplyHeader_1_2TypeDescription);
LogicalThreadID logicalThreadID = unmarshalLogicalThreadID(unmarshal.isLittleEndian(), replyHeader.service_context[0].context_data);
ThreadID threadId = new ThreadID(logicalThreadID.IDs[0].threadID);
Object data[] = (Object[])removePendingRequest(threadId);
IMessage iMessage = new Message(null, // oid
null, // object
null, // interface
(MethodDescription)data[1], // MMDesc
null, // operation
threadId,
unmarshal,
false, // synchron
replyHeader.reply_status != ReplyStatusType_1_2.NO_EXCEPTION,
(Object[])data[0]); // params
if(DEBUG) System.err.println("##### " + getClass().getName() + ".readReply:" + iMessage);
return iMessage;
}
/**
* Reads a job from the given stream.
* <p>
* @return thread read job.
* @see com.sun.star.lib.uno.environments.remote.Job
* @see com.sun.star.lib.uno.environments.remote.IProtocol#readJob
*/
public IMessage readMessage(InputStream inputStream) throws Exception {
MessageHeader_1_1 messageHeader = readMessageHeader(inputStream);
byte bytes[] = new byte[messageHeader.message_size];
int read = 0;
do {
int act_read = inputStream.read(bytes, read, bytes.length - read);
if(act_read < 0)
throw new com.sun.star.uno.RuntimeException("iiop - got EOF");
read += act_read;
}
while(read < bytes.length);
boolean littleEndian = messageHeader.flags != 0;
Unmarshal unmarshal = new Unmarshal(bytes, messageHeader.message_size, littleEndian, _iBridge);
IMessage iMessage = null;
switch(messageHeader.message_type) {
case MsgType_1_1.Request_value: iMessage = readRequest(unmarshal); break;
case MsgType_1_1.Reply_value: iMessage = readReply(unmarshal); break;
case MsgType_1_1.MessageError_value: throw new RuntimeException("WARNING: iiop - recieved a message error!!!");
case MsgType_1_1.CancelRequest_value:
case MsgType_1_1.LocateRequest_value:
case MsgType_1_1.LocateReply_value: throw new RuntimeException("WARNING: iiop - received unsupported message of type:" + messageHeader.message_type);
case MsgType_1_1.CloseConnection_value:
if(!_bIgnoreNextCloseConnection) {
inputStream.close();
}
else
_bIgnoreNextCloseConnection = false;
break;
default: throw new RuntimeException("ERROR: iiop - received message of unknown type!!!");
}
return iMessage;
}
public void writeRequest(String oid, TypeDescription typeDescription, String operation, ThreadID threadId, Object params[], Boolean synchron[], Boolean mustReply[]) throws Exception {
if(DEBUG) System.err.println("##### " + getClass().getName() + ".writeRequest:" + oid + " " + typeDescription + " " + operation);
++ _requestsSend;
MethodDescription methodDescription = typeDescription.getMethodDescription(operation);
Marshal marshal = new Marshal(false, _iBridge);
TargetAddress targetAddress = new TargetAddress(-7,
(short)0,
marshalObjectKey(oid, typeDescription.getTypeName()),
new TaggedProfile(2, new byte[]{(byte)7, (byte)6, (byte)5}),
new IORAddressingInfo(3,
new IOR(new CorbaString8(""),
new TaggedProfile[0])
)
);
LogicalThreadID logicalThreadID = new LogicalThreadID(new OneThreadID[]{new OneThreadID(0, threadId.getBytes())});
ServiceContext serviceContexts[] = new ServiceContext[]{new ServiceContext(4, marshalLogicalThreadID(logicalThreadID))};
// if the type of request is not provided, use methodDescription if available, otherwise fall back to synchron
if(synchron[0] == null) {
if(methodDescription == null)
synchron[0] = new Boolean(true);
else
synchron[0] = new Boolean(!methodDescription.isOneway());
}
int messageType = MsgType_1_1.Request_value;
marshal.writeObject(__RequestHeader_1_2TypeDescription,
new RequestHeader_1_2(++ _requestId,
(byte)(synchron[0].booleanValue() ? 3 : 0),
(byte)0, (byte)0, (byte)0,
targetAddress,
new CorbaString8(operation),
serviceContexts)
);
// write the in parameters
for(int i = 0; i < methodDescription.getInSignature().length; ++ i) {
if(methodDescription.getInSignature()[i] != null) { // is it an in parameter
if(methodDescription.getOutSignature()[i] != null) // is it also an out parameter
marshal.writeObject(methodDescription.getOutSignature()[i].getComponentType(), ((Object [])params[i])[0]);
else // in only
marshal.writeObject(methodDescription.getInSignature()[i], params[i]);
}
}
// add the marshal to the flush list
_flushList.addElement(new Object[]{MsgType_1_1.Request, marshal});
if(synchron[0].booleanValue()) // if we are waiting for a reply, the reply is pending
putPendingRequest(threadId, new Object[]{params, methodDescription});
}
public void writeReply(boolean exception, ThreadID threadId, Object result) throws Exception {
if(DEBUG) System.err.println("##### " + getClass().getName() + ".writeReply:" + exception + " " + threadId + " " + result);
Object data[] = (Object[])removePendingReply(threadId);
Object params[] = (Object[])data[0];
TypeDescription signature[] = (TypeDescription[])data[1];
TypeDescription resType = (TypeDescription)data[2];
int requestId = ((Integer)data[3]).intValue();
Marshal marshal = new Marshal(false, _iBridge);
LogicalThreadID logicalThreadID = new LogicalThreadID(new OneThreadID[]{new OneThreadID(0, threadId.getBytes())});
ServiceContext serviceContexts[] = new ServiceContext[]{new ServiceContext(4, marshalLogicalThreadID(logicalThreadID))};
int messageType = MsgType_1_1.Reply_value;
marshal.writeObject(__ReplyHeader_1_2TypeDescription,
new ReplyHeader_1_2(requestId,
exception ? ReplyStatusType_1_2.USER_EXCEPTION : ReplyStatusType_1_2.NO_EXCEPTION,
serviceContexts)
);
if(exception) {
resType = TypeDescription.getTypeDescription(result.getClass());
// resType = TypeDescription.getType(com.sun.star.uno.Exception.class);
signature = new TypeDescription[0];
}
marshal.writeObject(resType, result);
for(int i = 0; i < signature.length; ++ i)
if(signature[i] != null)
marshal.writeObject(signature[i].getComponentType(), Array.get(params[i], 0));
// add the marshal to the flush list
_flushList.addElement(new Object[]{MsgType_1_1.Reply, marshal});
}
public synchronized void flush(DataOutput dataOutput) throws Exception {
if(DEBUG) System.err.println("##### " + getClass().getName() + ".flush:" + dataOutput);
Enumeration elements = _flushList.elements();
while(elements.hasMoreElements()) {
Object objects[] = (Object[])elements.nextElement();
Enum enum = (Enum)objects[0];
Marshal marshal = (Marshal)objects[1];
int size = marshal.getSize();
byte bytes[] = marshal.reset();
writeMessageHeader(enum.getValue(), size, dataOutput);
if(DEBUG) System.err.println("##### " + getClass().getName() + ".flush - bytes:" + size);
dataOutput.write(bytes, 0, size);
}
_flushList = new Vector();
}
public IMarshal createMarshal() {
return new Marshal(true, _iBridge);
}
public IUnmarshal createUnmarshal(byte bytes[]) {
return new Unmarshal(bytes, bytes.length, true, _iBridge);
}
}