/*
 * @(#)MessageBase.java	1.19 04/06/21
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package com.sun.corba.se.impl.protocol.giopmsgheaders;

import java.io.IOException;
import java.lang.Class;
import java.lang.reflect.Constructor;
import java.nio.ByteBuffer;
import java.util.Iterator;

import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.MARSHAL;
import org.omg.CORBA.Principal;
import org.omg.CORBA.SystemException;
import org.omg.IOP.TaggedProfile;

import com.sun.corba.se.pept.transport.ByteBufferPool;

import com.sun.corba.se.spi.ior.ObjectKey;
import com.sun.corba.se.spi.ior.ObjectId;
import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.ior.ObjectKeyFactory;
import com.sun.corba.se.spi.ior.iiop.IIOPProfile;
import com.sun.corba.se.spi.ior.iiop.IIOPFactories;
import com.sun.corba.se.spi.ior.iiop.IIOPProfileTemplate ;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.ior.iiop.RequestPartitioningComponent;
import com.sun.corba.se.spi.logging.CORBALogDomains ;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.transport.CorbaConnection;
import com.sun.corba.se.spi.transport.ReadTimeouts;

import com.sun.corba.se.spi.servicecontext.ServiceContexts;
import com.sun.corba.se.impl.encoding.ByteBufferWithInfo;
import com.sun.corba.se.impl.encoding.CDRInputStream_1_0;
import com.sun.corba.se.impl.logging.ORBUtilSystemException ;
import com.sun.corba.se.impl.orbutil.ORBUtility;
import com.sun.corba.se.impl.orbutil.ORBConstants;
import com.sun.corba.se.impl.orbutil.ORBClassLoader;
import com.sun.corba.se.impl.protocol.AddressingDispositionException;

/**
 * This class acts as the base class for the various GIOP message types. This
 * also serves as a factory to create various message types. We currently
 * support GIOP 1.0, 1.1 and 1.2 message types.
 *
 * @author Ram Jeyaraman 05/14/2000
 * @version 1.0
 */

public abstract class MessageBase implements Message{

    // This is only used when the giopDebug flag is
    // turned on.
    public byte[] giopHeader;
    private ByteBuffer byteBuffer;
    private int threadPoolToUse;

    // (encodingVersion == 0x00) implies CDR encoding, 
    // (encodingVersion >  0x00) implies Java serialization version.
    byte encodingVersion = (byte) Message.CDR_ENC_VERSION;

    private static ORBUtilSystemException wrapper = 
	ORBUtilSystemException.get( CORBALogDomains.RPC_PROTOCOL ) ;

    // Static methods

    public static String typeToString(int type)
    {
	return typeToString((byte)type);
    }

    public static String typeToString(byte type)
    {
	String result = type + "/";
	switch (type) {
	case GIOPRequest         : result += "GIOPRequest";         break;
	case GIOPReply           : result += "GIOPReply";           break;
	case GIOPCancelRequest   : result += "GIOPCancelRequest";   break;
	case GIOPLocateRequest   : result += "GIOPLocateRequest";   break;
	case GIOPLocateReply     : result += "GIOPLocateReply";     break;
	case GIOPCloseConnection : result += "GIOPCloseConnection"; break;
	case GIOPMessageError    : result += "GIOPMessageError";    break;
	case GIOPFragment        : result += "GIOPFragment";        break;
	default                  : result += "Unknown";             break;
	}
	return result;
    }

    public static MessageBase readGIOPMessage(ORB orb, CorbaConnection connection)
    {
	MessageBase msg = readGIOPHeader(orb, connection);
	msg = (MessageBase)readGIOPBody(orb, connection, (Message)msg);
	return msg;
    }

    public static MessageBase readGIOPHeader(ORB orb, CorbaConnection connection)
    {
        MessageBase msg = null;
        ReadTimeouts readTimeouts =
	                   orb.getORBData().getTransportTCPReadTimeouts();

	ByteBuffer buf = null;

	try {
	    buf = connection.read(GIOPMessageHeaderLength,
			  0, GIOPMessageHeaderLength,
			  readTimeouts.get_max_giop_header_time_to_wait());
	} catch (IOException e) {
	    throw wrapper.ioexceptionWhenReadingConnection(e);
	}

        if (orb.giopDebugFlag) {
            // Since this is executed in debug mode only the overhead of
            // using a View Buffer is not an issue. We'll also use a
            // read-only View Buffer so we don't disturb the state of
            // byteBuffer. 
            dprint(".readGIOPHeader: " + typeToString(buf.get(7)));
	    dprint(".readGIOPHeader: GIOP header is: ");
            ByteBuffer viewBuffer = buf.asReadOnlyBuffer();
            viewBuffer.position(0).limit(GIOPMessageHeaderLength);
	    ByteBufferWithInfo bbwi = new ByteBufferWithInfo(orb,viewBuffer);
	    bbwi.buflen = GIOPMessageHeaderLength;
	    CDRInputStream_1_0.printBuffer(bbwi);
        }

        // Sanity checks

        /*
         * check for magic corruption
         * check for version incompatibility
         * check if fragmentation is allowed based on mesg type.
            . 1.0 fragmentation disallowed; FragmentMessage is non-existent.
            . 1.1 only {Request, Reply} msgs maybe fragmented.
            . 1.2 only {Request, Reply, LocateRequest, LocateReply} msgs
              maybe fragmented.
        */

        int b1, b2, b3, b4;

        b1 = (buf.get(0) << 24) & 0xFF000000;
        b2 = (buf.get(1) << 16) & 0x00FF0000;
        b3 = (buf.get(2) << 8)  & 0x0000FF00;
        b4 = (buf.get(3) << 0)  & 0x000000FF;
        int magic = (b1 | b2 | b3 | b4);

        if (magic != GIOPBigMagic) {
            // If Magic is incorrect, it is an error.
            // ACTION : send MessageError and close the connection.
	    throw wrapper.giopMagicError( CompletionStatus.COMPLETED_MAYBE);
        }

	// Extract the encoding version from the request GIOP Version,
	// if it contains an encoding, and set GIOP version appropriately.
	// For Java serialization, we use GIOP Version 1.2 message format.
	byte requestEncodingVersion = Message.CDR_ENC_VERSION;
	if ((buf.get(4) == 0x0D) &&
	    (buf.get(5) <= Message.JAVA_ENC_VERSION) &&
	    (buf.get(5) > Message.CDR_ENC_VERSION) &&
	    orb.getORBData().isJavaSerializationEnabled()) {
	    // Entering this block means the request is using Java encoding,
	    // and the encoding version is <= this ORB's Java encoding version.
	    requestEncodingVersion = buf.get(5);
	    buf.put(4, (byte) 0x01);
	    buf.put(5, (byte) 0x02);
	}

        GIOPVersion orbVersion = orb.getORBData().getGIOPVersion();

        if (orb.giopDebugFlag) {
            dprint(".readGIOPHeader: Message GIOP version: "
                              + buf.get(4) + '.' + buf.get(5));
            dprint(".readGIOPHeader: ORB Max GIOP Version: "
                              + orbVersion);
        }

        if ( (buf.get(4) > orbVersion.getMajor()) ||
             ( (buf.get(4) == orbVersion.getMajor()) && (buf.get(5) > orbVersion.getMinor()) )
            ) {
            // For requests, sending ORB should use the version info
            // published in the IOR or may choose to use a <= version
            // for requests. If the version is greater than published version,
            // it is an error.

            // For replies, the ORB should always receive a version it supports
            // or less, but never greater (except for MessageError)

            // ACTION : Send back a MessageError() with the the highest version
            // the server ORB supports, and close the connection.
            if ( buf.get(7) != GIOPMessageError ) {
		throw wrapper.giopVersionError( CompletionStatus.COMPLETED_MAYBE);
            }
        }

        AreFragmentsAllowed(buf.get(4), buf.get(5), buf.get(6), buf.get(7));

        // create appropriate messages types

        switch (buf.get(7)) {

        case GIOPRequest:
            if (orb.giopDebugFlag) {
                dprint(".readGIOPHeader: creating RequestMessage");
            }
            //msg = new RequestMessage(orb.giopDebugFlag);
            if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x00) ) { // 1.0
                msg = new RequestMessage_1_0(orb);
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x01) ) { // 1.1
                msg = new RequestMessage_1_1(orb);
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x02) ) { // 1.2
                msg = new RequestMessage_1_2(orb);
            } else {
		throw wrapper.giopVersionError(
		    CompletionStatus.COMPLETED_MAYBE);
            }
            break;

        case GIOPLocateRequest:
            if (orb.giopDebugFlag) {
                dprint(".readGIOPHeader: creating LocateRequestMessage");
            }
            //msg = new LocateRequestMessage(orb.giopDebugFlag);
            if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x00) ) { // 1.0
                msg = new LocateRequestMessage_1_0(orb);
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x01) ) { // 1.1
                msg = new LocateRequestMessage_1_1(orb);
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x02) ) { // 1.2
                msg = new LocateRequestMessage_1_2(orb);
            } else {
		throw wrapper.giopVersionError(
		    CompletionStatus.COMPLETED_MAYBE);
            }
            break;

        case GIOPCancelRequest:
            if (orb.giopDebugFlag) {
                dprint(".readGIOPHeader: creating CancelRequestMessage");
            }
            //msg = new CancelRequestMessage(orb.giopDebugFlag);
            if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x00) ) { // 1.0
                msg = new CancelRequestMessage_1_0();
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x01) ) { // 1.1
                msg = new CancelRequestMessage_1_1();
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x02) ) { // 1.2
                msg = new CancelRequestMessage_1_2();
            } else {
		throw wrapper.giopVersionError(
		    CompletionStatus.COMPLETED_MAYBE);
            }
            break;

        case GIOPReply:
            if (orb.giopDebugFlag) {
                dprint(".readGIOPHeader: creating ReplyMessage");
            }
            //msg = new ReplyMessage(orb.giopDebugFlag);
            if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x00) ) { // 1.0
                msg = new ReplyMessage_1_0(orb);
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x01) ) { // 1.1
                msg = new ReplyMessage_1_1(orb);
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x02) ) { // 1.2
                msg = new ReplyMessage_1_2(orb);
            } else {
		throw wrapper.giopVersionError(
		    CompletionStatus.COMPLETED_MAYBE);
            }
            break;

        case GIOPLocateReply:
            if (orb.giopDebugFlag) {
                dprint(".readGIOPHeader: creating LocateReplyMessage");
            }
            //msg = new LocateReplyMessage(orb.giopDebugFlag);
            if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x00) ) { // 1.0
                msg = new LocateReplyMessage_1_0(orb);
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x01) ) { // 1.1
                msg = new LocateReplyMessage_1_1(orb);
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x02) ) { // 1.2
                msg = new LocateReplyMessage_1_2(orb);
            } else {
		throw wrapper.giopVersionError(
		    CompletionStatus.COMPLETED_MAYBE);
            }
            break;

        case GIOPCloseConnection:
        case GIOPMessageError:
            if (orb.giopDebugFlag) {
                dprint(".readGIOPHeader: creating Message for CloseConnection or MessageError");
            }
            // REVISIT a MessageError  may contain the highest version server
            // can support. In such a case, a new request may be made with the
            // correct version or the connection be simply closed. Note the
            // connection may have been closed by the server.
            //msg = new Message(orb.giopDebugFlag);
            if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x00) ) { // 1.0
                msg = new Message_1_0();
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x01) ) { // 1.1
                msg = new Message_1_1();
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x02) ) { // 1.2
                msg = new Message_1_1();
            } else {
		throw wrapper.giopVersionError(
		    CompletionStatus.COMPLETED_MAYBE);
            }
            break;

        case GIOPFragment:
            if (orb.giopDebugFlag) {
                dprint(".readGIOPHeader: creating FragmentMessage");
            }
            //msg = new FragmentMessage(orb.giopDebugFlag);
            if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x00) ) { // 1.0
                // not possible (error checking done already)
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x01) ) { // 1.1
                msg = new FragmentMessage_1_1();
            } else if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x02) ) { // 1.2
                msg = new FragmentMessage_1_2();
            } else {
		throw wrapper.giopVersionError(
		    CompletionStatus.COMPLETED_MAYBE);
            }
            break;

        default:
            if (orb.giopDebugFlag)
                dprint(".readGIOPHeader: UNKNOWN MESSAGE TYPE: "
		       + buf.get(7));
            // unknown message type ?
            // ACTION : send MessageError and close the connection
	    throw wrapper.giopVersionError(
		CompletionStatus.COMPLETED_MAYBE);
        }

        //
        // Initialize the generic GIOP header instance variables.
        //

        if ( (buf.get(4) == 0x01) && (buf.get(5) == 0x00) ) { // 1.0
            Message_1_0 msg10 = (Message_1_0) msg;
            msg10.magic = magic;
            msg10.GIOP_version = new GIOPVersion(buf.get(4), buf.get(5));
            msg10.byte_order = (buf.get(6) == LITTLE_ENDIAN_BIT);
	    // 'request partitioning' not supported on GIOP version 1.0
	    // so just use the default thread pool, 0.
	    msg.threadPoolToUse = 0;
            msg10.message_type = buf.get(7);
            msg10.message_size = readSize(buf.get(8), buf.get(9), buf.get(10), buf.get(11),
                                          msg10.isLittleEndian()) +
                                 GIOPMessageHeaderLength;
        } else { // 1.1 & 1.2
            Message_1_1 msg11 = (Message_1_1) msg;
            msg11.magic = magic;
            msg11.GIOP_version = new GIOPVersion(buf.get(4), buf.get(5));
            msg11.flags = (byte)(buf.get(6) & TRAILING_TWO_BIT_BYTE_MASK);
	    // IMPORTANT: For 'request partitioning', the thread pool to use
	    //            information is stored in the leading 6 bits of byte 6.
	    //
	    // IMPORTANT: Request partitioning is a PROPRIETARY EXTENSION !!!
	    //
	    // NOTE: Bitwise operators will promote a byte to an int before 
	    //       performing a bitwise operation and bytes, ints, longs, etc
	    //       are signed types in Java. Thus, the need for the 
	    //       THREAD_POOL_TO_USE_MASK operation.
	    msg.threadPoolToUse = (buf.get(6) >>> 2) & THREAD_POOL_TO_USE_MASK;
            msg11.message_type = buf.get(7);
            msg11.message_size = 
                      readSize(buf.get(8), buf.get(9), buf.get(10), buf.get(11),
                              msg11.isLittleEndian()) + GIOPMessageHeaderLength;
        }


        if (orb.giopDebugFlag) {
            // Since this is executed in debug mode only the overhead of
            // using a View Buffer is not an issue. We'll also use a
            // read-only View Buffer so we don't disturb the state of
            // byteBuffer. 
            dprint(".readGIOPHeader: header construction complete.");

            // For debugging purposes, save the 12 bytes of the header
            ByteBuffer viewBuf = buf.asReadOnlyBuffer();
            byte[] msgBuf = new byte[GIOPMessageHeaderLength];
            viewBuf.position(0).limit(GIOPMessageHeaderLength);
            viewBuf.get(msgBuf,0,msgBuf.length);
	    // REVISIT: is giopHeader still used?
            ((MessageBase)msg).giopHeader = msgBuf;
        }

	msg.setByteBuffer(buf);
	msg.setEncodingVersion(requestEncodingVersion);

	return msg;
    }

    public static Message readGIOPBody(ORB orb,
			               CorbaConnection connection,
				       Message msg)
    {
        ReadTimeouts readTimeouts =
	                   orb.getORBData().getTransportTCPReadTimeouts();
	ByteBuffer buf = msg.getByteBuffer();

	buf.position(MessageBase.GIOPMessageHeaderLength);
	int msgSizeMinusHeader =
	    msg.getSize() - MessageBase.GIOPMessageHeaderLength;
	try {
	    buf = connection.read(buf, 
			  GIOPMessageHeaderLength, msgSizeMinusHeader,
			  readTimeouts.get_max_time_to_wait());
	} catch (IOException e) {
	    throw wrapper.ioexceptionWhenReadingConnection(e);
	}

	msg.setByteBuffer(buf);

	if (orb.giopDebugFlag) {
	    dprint(".readGIOPBody: received message:");
	    ByteBuffer viewBuffer = buf.asReadOnlyBuffer();
	    viewBuffer.position(0).limit(msg.getSize());
	    ByteBufferWithInfo bbwi = new ByteBufferWithInfo(orb, viewBuffer);
	    CDRInputStream_1_0.printBuffer(bbwi);
	}

        return msg;
    }

    private static RequestMessage createRequest(
            ORB orb, GIOPVersion gv, byte encodingVersion, int request_id,
            boolean response_expected, byte[] object_key, String operation,
            ServiceContexts service_contexts, Principal requesting_principal) {

        if (gv.equals(GIOPVersion.V1_0)) { // 1.0
            return new RequestMessage_1_0(orb, service_contexts, request_id,
					 response_expected, object_key,
					 operation, requesting_principal);
        } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
            return new RequestMessage_1_1(orb, service_contexts, request_id,
                response_expected, new byte[] { 0x00, 0x00, 0x00 },
                object_key, operation, requesting_principal);
        } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
            // Note: Currently we use response_expected flag to decide if the
            // call is oneway or not. Ideally, it is possible to expect a
            // response on a oneway call too, but we do not support it now.
            byte response_flags = 0x03;
            if (response_expected) {
                response_flags = 0x03;
            } else {
                response_flags = 0x00;
            }
            /*
            // REVISIT The following is the correct way to do it. This gives
            // more flexibility.
            if ((DII::INV_NO_RESPONSE == false) && response_expected) {
                response_flags = 0x03; // regular two-way
            } else if ((DII::INV_NO_RESPONSE == false) && !response_expected) {
                // this condition is not possible
            } else if ((DII::INV_NO_RESPONSE == true) && response_expected) {
                // oneway, but we need response for LocationForwards or
                // SystemExceptions.
                response_flags = 0x01;
            } else if ((DII::INV_NO_RESPONSE == true) && !response_expected) {
                // oneway, no response required
                response_flags = 0x00;
            }
            */
            TargetAddress target = new TargetAddress();
            target.object_key(object_key);
            RequestMessage msg = 
		new RequestMessage_1_2(orb, request_id, response_flags,
				       new byte[] { 0x00, 0x00, 0x00 },
				       target, operation, service_contexts);
	    msg.setEncodingVersion(encodingVersion);
	    return msg;
        } else {
	    throw wrapper.giopVersionError(
		CompletionStatus.COMPLETED_MAYBE);
        }
    }

    public static RequestMessage createRequest(
            ORB orb, GIOPVersion gv, byte encodingVersion, int request_id,
	    boolean response_expected, IOR ior,
	    short addrDisp, String operation,
            ServiceContexts service_contexts, Principal requesting_principal) {

	RequestMessage requestMessage = null;
        IIOPProfile profile = ior.getProfile();
            
        if (addrDisp == KeyAddr.value) {  
            // object key will be used for target addressing
            profile = ior.getProfile();
    	    ObjectKey objKey = profile.getObjectKey();
	    byte[] object_key = objKey.getBytes(orb);            
	    requestMessage = 
		   createRequest(orb, gv, encodingVersion, request_id,
				 response_expected, object_key,
				 operation, service_contexts,
                                 requesting_principal);            
        } else {
        
            if (!(gv.equals(GIOPVersion.V1_2))) {        
                // only object_key based target addressing is allowed for 
                // GIOP 1.0 & 1.1
	        throw wrapper.giopVersionError(
		    CompletionStatus.COMPLETED_MAYBE);
            }
    
            // Note: Currently we use response_expected flag to decide if the
            // call is oneway or not. Ideally, it is possible to expect a
            // response on a oneway call too, but we do not support it now.
            byte response_flags = 0x03;
            if (response_expected) {
                response_flags = 0x03;
            } else {
                response_flags = 0x00;
            }
            
            TargetAddress target = new TargetAddress();            
            if (addrDisp == ProfileAddr.value) { // iop profile will be used
                profile = ior.getProfile();
                target.profile(profile.getIOPProfile());
            } else if (addrDisp == ReferenceAddr.value) {  // ior will be used
                IORAddressingInfo iorInfo = 
                    new IORAddressingInfo( 0, // profile index
                        ior.getIOPIOR());
                target.ior(iorInfo);  
            } else { 
                // invalid target addressing disposition value
	        throw wrapper.illegalTargetAddressDisposition(
		    CompletionStatus.COMPLETED_NO);
            }
        
	    requestMessage =
                   new RequestMessage_1_2(orb, request_id, response_flags,
                                  new byte[] { 0x00, 0x00, 0x00 }, target,
                                  operation, service_contexts);
	    requestMessage.setEncodingVersion(encodingVersion);
	}

	if (gv.supportsIORIIOPProfileComponents()) {
	    // add request partitioning thread pool to use info
	    int poolToUse = 0; // default pool
	    IIOPProfileTemplate temp = 
		(IIOPProfileTemplate)profile.getTaggedProfileTemplate();
	    Iterator iter = 
		temp.iteratorById(ORBConstants.TAG_REQUEST_PARTITIONING_ID);
	    if (iter.hasNext()) {
		poolToUse = 
		    ((RequestPartitioningComponent)iter.next()).getRequestPartitioningId();
	    }

	    if (poolToUse < ORBConstants.REQUEST_PARTITIONING_MIN_THREAD_POOL_ID ||
		poolToUse > ORBConstants.REQUEST_PARTITIONING_MAX_THREAD_POOL_ID) {
		throw wrapper.invalidRequestPartitioningId(new Integer(poolToUse),
		      new Integer(ORBConstants.REQUEST_PARTITIONING_MIN_THREAD_POOL_ID),
	      	      new Integer(ORBConstants.REQUEST_PARTITIONING_MAX_THREAD_POOL_ID));
	    }
	    requestMessage.setThreadPoolToUse(poolToUse);
	}

	return requestMessage;
    }
                    
    public static ReplyMessage createReply(
            ORB orb, GIOPVersion gv, byte encodingVersion, int request_id,
            int reply_status, ServiceContexts service_contexts, IOR ior) {

        if (gv.equals(GIOPVersion.V1_0)) { // 1.0
            return new ReplyMessage_1_0(orb, service_contexts, request_id,
                                        reply_status, ior);
        } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
            return new ReplyMessage_1_1(orb, service_contexts, request_id,
                                        reply_status, ior);
        } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
            ReplyMessage msg = 
		new ReplyMessage_1_2(orb, request_id, reply_status,
				     service_contexts, ior);
	    msg.setEncodingVersion(encodingVersion);
	    return msg;
        } else {
	    throw wrapper.giopVersionError(
		CompletionStatus.COMPLETED_MAYBE);
        }
    }

    public static LocateRequestMessage createLocateRequest(
            ORB orb, GIOPVersion gv, byte encodingVersion,
            int request_id, byte[] object_key) {

        if (gv.equals(GIOPVersion.V1_0)) { // 1.0
            return new LocateRequestMessage_1_0(orb, request_id, object_key);
        } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
            return new LocateRequestMessage_1_1(orb, request_id, object_key);
        } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
            TargetAddress target = new TargetAddress();
            target.object_key(object_key);
            LocateRequestMessage msg =
		new LocateRequestMessage_1_2(orb, request_id, target);
	    msg.setEncodingVersion(encodingVersion);
	    return msg;
        } else {
	    throw wrapper.giopVersionError(
		CompletionStatus.COMPLETED_MAYBE);
        }
    }

    public static LocateReplyMessage createLocateReply(
	    ORB orb, GIOPVersion gv, byte encodingVersion,
            int request_id, int locate_status, IOR ior) {

        if (gv.equals(GIOPVersion.V1_0)) { // 1.0
            return new LocateReplyMessage_1_0(orb, request_id,
                                              locate_status, ior);
        } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
            return new LocateReplyMessage_1_1(orb, request_id,
                                              locate_status, ior);
        } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
            LocateReplyMessage msg = 
		new LocateReplyMessage_1_2(orb, request_id, 
					   locate_status, ior);
	    msg.setEncodingVersion(encodingVersion);
	    return msg;
        } else {
	    throw wrapper.giopVersionError(
		CompletionStatus.COMPLETED_MAYBE);
        }
    }

    public static CancelRequestMessage createCancelRequest(
            GIOPVersion gv, int request_id) {

        if (gv.equals(GIOPVersion.V1_0)) { // 1.0
            return new CancelRequestMessage_1_0(request_id);
        } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
            return new CancelRequestMessage_1_1(request_id);
        } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
            return new CancelRequestMessage_1_2(request_id);
        } else {
	    throw wrapper.giopVersionError(
		CompletionStatus.COMPLETED_MAYBE);
        }
    }

    public static Message createCloseConnection(GIOPVersion gv) {
        if (gv.equals(GIOPVersion.V1_0)) { // 1.0
            return new Message_1_0(Message.GIOPBigMagic, false,
                                   Message.GIOPCloseConnection, 0);
        } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
            return new Message_1_1(Message.GIOPBigMagic, GIOPVersion.V1_1,
                                   FLAG_NO_FRAG_BIG_ENDIAN,
                                   Message.GIOPCloseConnection, 0);
        } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
            return new Message_1_1(Message.GIOPBigMagic, GIOPVersion.V1_2,
                                   FLAG_NO_FRAG_BIG_ENDIAN,
                                   Message.GIOPCloseConnection, 0);
        } else {
	    throw wrapper.giopVersionError(
		CompletionStatus.COMPLETED_MAYBE);
        }
    }

    public static Message createMessageError(GIOPVersion gv) {
        if (gv.equals(GIOPVersion.V1_0)) { // 1.0
            return new Message_1_0(Message.GIOPBigMagic, false,
                                   Message.GIOPMessageError, 0);
        } else if (gv.equals(GIOPVersion.V1_1)) { // 1.1
            return new Message_1_1(Message.GIOPBigMagic, GIOPVersion.V1_1,
                                   FLAG_NO_FRAG_BIG_ENDIAN,
                                   Message.GIOPMessageError, 0);
        } else if (gv.equals(GIOPVersion.V1_2)) { // 1.2
            return new Message_1_1(Message.GIOPBigMagic, GIOPVersion.V1_2,
                                   FLAG_NO_FRAG_BIG_ENDIAN,
                                   Message.GIOPMessageError, 0);
        } else {
	    throw wrapper.giopVersionError(
		CompletionStatus.COMPLETED_MAYBE);
        }
    }

    public static FragmentMessage createFragmentMessage(GIOPVersion gv) {
        // This method is not currently used.
        // New fragment messages are always created from existing messages.
        // Creating a FragmentMessage from InputStream is done in
        // createFromStream(..)
        return null;
    }

    public static int getRequestId(Message msg) {
        switch (msg.getType()) {
        case GIOPRequest :
            return ((RequestMessage) msg).getRequestId();
        case GIOPReply :
            return ((ReplyMessage) msg).getRequestId();
        case GIOPLocateRequest :
            return ((LocateRequestMessage) msg).getRequestId();
        case GIOPLocateReply :
            return ((LocateReplyMessage) msg).getRequestId();
        case GIOPCancelRequest :
            return ((CancelRequestMessage) msg).getRequestId();
        case GIOPFragment :
            return ((FragmentMessage) msg).getRequestId();
        }

	throw wrapper.illegalGiopMsgType( 
	    CompletionStatus.COMPLETED_MAYBE);
    }

    /**
     * Set a flag in the given buffer (fragment bit, byte order bit, etc)
     */
    public static void setFlag(ByteBuffer byteBuffer, int flag) {
        byte b = byteBuffer.get(6);
        b |= flag;
        byteBuffer.put(6,b);
    }

    /**
     * Clears a flag in the given buffer
     */
    public static void clearFlag(byte[] buf, int flag) {
        buf[6] &= (0xFF ^ flag);
    }

    private static void AreFragmentsAllowed(byte major, byte minor, byte flag,
            byte msgType) {

        if ( (major == 0x01) && (minor == 0x00) ) { // 1.0
            if (msgType == GIOPFragment) {
		throw wrapper.fragmentationDisallowed( 
		    CompletionStatus.COMPLETED_MAYBE);
            }
        }

        if ( (flag & MORE_FRAGMENTS_BIT) == MORE_FRAGMENTS_BIT ) {
            switch (msgType) {
            case GIOPCancelRequest :
            case GIOPCloseConnection :
            case GIOPMessageError :
		throw wrapper.fragmentationDisallowed( 
		    CompletionStatus.COMPLETED_MAYBE);
            case GIOPLocateRequest :
            case GIOPLocateReply :
                if ( (major == 0x01) && (minor == 0x01) ) { // 1.1
		    throw wrapper.fragmentationDisallowed( 
			CompletionStatus.COMPLETED_MAYBE);
                }
                break;
            }
        }
    }

    /**
     * Construct an ObjectKey from a byte[].
     *
     * @return ObjectKey the object key.
     */
    static ObjectKey extractObjectKey(byte[] objKey, ORB orb) {

        try {
            if (objKey != null) {
                ObjectKey objectKey = 
		    orb.getObjectKeyFactory().create(objKey);
                if (objectKey != null) {
                    return objectKey;
                }
            }
        } catch (Exception e) {
	    // XXX log this exception
	}

	// This exception is thrown if any exceptions are raised while
	// extracting the object key or if the object key is empty.
	throw wrapper.invalidObjectKey();
    }

    /**
     * Extract the object key from TargetAddress.
     *
     * @return ObjectKey the object key.
     */
    static ObjectKey extractObjectKey(TargetAddress target, ORB orb) {
    
        short orbTargetAddrPref = orb.getORBData().getGIOPTargetAddressPreference();
        short reqAddrDisp = target.discriminator();
            
        switch (orbTargetAddrPref) {
        case ORBConstants.ADDR_DISP_OBJKEY :
            if (reqAddrDisp != KeyAddr.value) {
                throw new AddressingDispositionException(KeyAddr.value);
            }
            break;
        case ORBConstants.ADDR_DISP_PROFILE :
            if (reqAddrDisp != ProfileAddr.value) {
                throw new AddressingDispositionException(ProfileAddr.value);
            }
            break;
        case ORBConstants.ADDR_DISP_IOR :
            if (reqAddrDisp != ReferenceAddr.value) {
                throw new AddressingDispositionException(ReferenceAddr.value);
            }
            break;
        case ORBConstants.ADDR_DISP_HANDLE_ALL :
            break;
        default : 
	    throw wrapper.orbTargetAddrPreferenceInExtractObjectkeyInvalid() ;
        }    
        
        try {
            switch (reqAddrDisp) {
            case KeyAddr.value :
                byte[] objKey = target.object_key();
                if (objKey != null) { // AddressingDisposition::KeyAddr
                    ObjectKey objectKey = 
			orb.getObjectKeyFactory().create(objKey);
                    if (objectKey != null) {
                       return objectKey;
                   }
                }
                break;
            case ProfileAddr.value :
                IIOPProfile iiopProfile = null;
                TaggedProfile profile = target.profile();
                if (profile != null) { // AddressingDisposition::ProfileAddr
                   iiopProfile = IIOPFactories.makeIIOPProfile(orb, profile);
                   ObjectKey objectKey = iiopProfile.getObjectKey();
                   if (objectKey != null) {
                       return objectKey;
                   }
                }
                break;
            case ReferenceAddr.value :
                IORAddressingInfo iorInfo = target.ior();
                if (iorInfo != null) { // AddressingDisposition::IORAddr
                    profile = iorInfo.ior.profiles[iorInfo.selected_profile_index];
                    iiopProfile = IIOPFactories.makeIIOPProfile(orb, profile);
                    ObjectKey objectKey = iiopProfile.getObjectKey();
                    if (objectKey != null) {
                       return objectKey;
                   }
                }
                break;
            default : // this cannot happen
                // There is no need for a explicit exception, since the
                // TargetAddressHelper.read() would have raised a BAD_OPERATION
                // exception by now.
                break;
            }
        } catch (Exception e) {}

	// This exception is thrown if any exceptions are raised while
	// extracting the object key from the TargetAddress or if all the
	// the valid TargetAddress::AddressingDispositions are empty.
	throw wrapper.invalidObjectKey() ;
    }

    private static int readSize(byte b1, byte b2, byte b3, byte b4,
            boolean littleEndian) {

        int a1, a2, a3, a4;

        if (!littleEndian) {
            a1 = (b1 << 24) & 0xFF000000;
            a2 = (b2 << 16) & 0x00FF0000;
            a3 = (b3 << 8)  & 0x0000FF00;
            a4 = (b4 << 0)  & 0x000000FF;
        } else {
            a1 = (b4 << 24) & 0xFF000000;
            a2 = (b3 << 16) & 0x00FF0000;
            a3 = (b2 << 8)  & 0x0000FF00;
            a4 = (b1 << 0)  & 0x000000FF;
        }

        return (a1 | a2 | a3 | a4);
    }

    static void nullCheck(Object obj) {
        if (obj == null) {
	    throw wrapper.nullNotAllowed() ;
        }
    }

    static SystemException getSystemException(
        String exClassName, int minorCode, CompletionStatus completionStatus,
	String message, ORBUtilSystemException wrapper)
    {
	SystemException sysEx = null;

        try {
	    Class clazz = ORBClassLoader.loadClass(exClassName);
	    if (message == null) {
		sysEx = (SystemException) clazz.newInstance();
	    } else {
		Class[] types = { String.class };
		Constructor constructor = clazz.getConstructor(types);
		Object[] args = { message };
		sysEx = (SystemException)constructor.newInstance(args);
	    }
        } catch (Exception someEx) {
	    throw wrapper.badSystemExceptionInReply( 
		CompletionStatus.COMPLETED_MAYBE, someEx );
        }

        sysEx.minor = minorCode;
        sysEx.completed = completionStatus;

        return sysEx;
    }

    public void callback(MessageHandler handler)
        throws java.io.IOException
    {
        handler.handleInput(this);
    }

    public ByteBuffer getByteBuffer()
    {
	return byteBuffer;
    }

    public void setByteBuffer(ByteBuffer byteBuffer)
    {
	this.byteBuffer = byteBuffer;
    }

    public int getThreadPoolToUse()
    {
	return threadPoolToUse;
    }

    public byte getEncodingVersion() {
	return this.encodingVersion;
    }

    public void setEncodingVersion(byte version) {
	this.encodingVersion = version;
    }

    private static void dprint(String msg)
    {
	ORBUtility.dprint("MessageBase", msg);
    }
}
