/*
 * Copyright (c) 2000, 2012, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.corba.se.impl.interceptors;

import java.util.HashMap ;

import org.omg.CORBA.Any;
import org.omg.CORBA.BAD_INV_ORDER;
import org.omg.CORBA.BAD_PARAM;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.Context;
import org.omg.CORBA.ContextList;
import org.omg.CORBA.CTX_RESTRICT_SCOPE;
import org.omg.CORBA.ExceptionList;
import org.omg.CORBA.LocalObject;
import org.omg.CORBA.NamedValue;
import org.omg.CORBA.NO_IMPLEMENT;
import org.omg.CORBA.NO_RESOURCES;
import org.omg.CORBA.NVList;
import org.omg.CORBA.Object;
import org.omg.CORBA.ParameterMode;
import org.omg.CORBA.Policy;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.UserException;
import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.InputStream;
import com.sun.corba.se.spi.servicecontext.ServiceContexts;
import com.sun.corba.se.spi.servicecontext.UnknownServiceContext;

import org.omg.IOP.ServiceContext;
import org.omg.IOP.ServiceContextHelper;
import org.omg.IOP.TaggedProfile;
import org.omg.IOP.TaggedProfileHelper;
import org.omg.IOP.TaggedComponent;
import org.omg.IOP.TaggedComponentHelper;
import org.omg.IOP.TAG_INTERNET_IOP;
import org.omg.Dynamic.Parameter;
import org.omg.PortableInterceptor.ClientRequestInfo;
import org.omg.PortableInterceptor.LOCATION_FORWARD;
import org.omg.PortableInterceptor.SUCCESSFUL;
import org.omg.PortableInterceptor.SYSTEM_EXCEPTION;
import org.omg.PortableInterceptor.TRANSPORT_RETRY;
import org.omg.PortableInterceptor.USER_EXCEPTION;

import com.sun.corba.se.pept.protocol.MessageMediator;

import com.sun.corba.se.spi.ior.IOR;
import com.sun.corba.se.spi.ior.iiop.IIOPProfileTemplate;
import com.sun.corba.se.spi.ior.iiop.GIOPVersion;
import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.protocol.CorbaMessageMediator;
import com.sun.corba.se.spi.protocol.RetryType;
import com.sun.corba.se.spi.transport.CorbaContactInfo;
import com.sun.corba.se.spi.transport.CorbaContactInfoList;
import com.sun.corba.se.spi.transport.CorbaContactInfoListIterator;

import com.sun.corba.se.impl.encoding.CDROutputStream;
import com.sun.corba.se.impl.encoding.CDRInputStream_1_0;
import com.sun.corba.se.impl.orbutil.ORBUtility;
import com.sun.corba.se.impl.protocol.CorbaInvocationInfo;
import com.sun.corba.se.impl.util.RepositoryId;

/**
 * Implementation of the ClientRequestInfo interface as specified in
 * orbos/99-12-02 section 5.4.2.
 */
public final class ClientRequestInfoImpl
    extends RequestInfoImpl
    implements ClientRequestInfo
{

    // The available constants for startingPointCall
    static final int CALL_SEND_REQUEST = 0;
    static final int CALL_SEND_POLL = 1;

    // The available constants for endingPointCall
    static final int CALL_RECEIVE_REPLY = 0;
    static final int CALL_RECEIVE_EXCEPTION = 1;
    static final int CALL_RECEIVE_OTHER = 2;

    //////////////////////////////////////////////////////////////////////
    //
    // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
    //
    //////////////////////////////////////////////////////////////////////

    // The current retry request status.  True if this request is being
    // retried and this info object is to be reused, or false otherwise.
    private RetryType retryRequest;

    // The number of times this info object has been (re)used.  This is
    // incremented every time a request is retried, and decremented every
    // time a request is complete.  When this reaches zero, the info object
    // is popped from the ClientRequestInfoImpl ThreadLocal stack in the ORB.
    private int entryCount = 0;

    // The RequestImpl is set when the call is DII based.
    // The DII query calls like ParameterList, ExceptionList,
    // ContextList will be delegated to RequestImpl.
    private org.omg.CORBA.Request request;

    // Sources of client request information
    private boolean diiInitiate;
    private CorbaMessageMediator messageMediator;

    // Cached information:
    private org.omg.CORBA.Object cachedTargetObject;
    private org.omg.CORBA.Object cachedEffectiveTargetObject;
    private Parameter[] cachedArguments;
    private TypeCode[] cachedExceptions;
    private String[] cachedContexts;
    private String[] cachedOperationContext;
    private String cachedReceivedExceptionId;
    private Any cachedResult;
    private Any cachedReceivedException;
    private TaggedProfile cachedEffectiveProfile;
    // key = Integer, value = IOP.ServiceContext.
    private HashMap cachedRequestServiceContexts;
    // key = Integer, value = IOP.ServiceContext.
    private HashMap cachedReplyServiceContexts;
    // key = Integer, value = TaggedComponent
    private HashMap cachedEffectiveComponents;


    protected boolean piCurrentPushed;

    //////////////////////////////////////////////////////////////////////
    //
    // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
    //
    //////////////////////////////////////////////////////////////////////

    /**
     * Reset the info object so that it can be reused for a retry,
     * for example.
     */
    void reset() {
        super.reset();

        // Please keep these in the same order that they're declared above.

        // 6763340
        retryRequest = RetryType.NONE;

        // Do not reset entryCount because we need to know when to pop this
        // from the stack.

        request = null;
        diiInitiate = false;
        messageMediator = null;

        // Clear cached attributes:
        cachedTargetObject = null;
        cachedEffectiveTargetObject = null;
        cachedArguments = null;
        cachedExceptions = null;
        cachedContexts = null;
        cachedOperationContext = null;
        cachedReceivedExceptionId = null;
        cachedResult = null;
        cachedReceivedException = null;
        cachedEffectiveProfile = null;
        cachedRequestServiceContexts = null;
        cachedReplyServiceContexts = null;
        cachedEffectiveComponents = null;

        piCurrentPushed = false;

        startingPointCall = CALL_SEND_REQUEST;
        endingPointCall = CALL_RECEIVE_REPLY;

    }

    /*
     **********************************************************************
     * Access protection
     **********************************************************************/

    // Method IDs for all methods in ClientRequestInfo.  This allows for a
    // convenient O(1) lookup for checkAccess().
    protected static final int MID_TARGET                  = MID_RI_LAST + 1;
    protected static final int MID_EFFECTIVE_TARGET        = MID_RI_LAST + 2;
    protected static final int MID_EFFECTIVE_PROFILE       = MID_RI_LAST + 3;
    protected static final int MID_RECEIVED_EXCEPTION      = MID_RI_LAST + 4;
    protected static final int MID_RECEIVED_EXCEPTION_ID   = MID_RI_LAST + 5;
    protected static final int MID_GET_EFFECTIVE_COMPONENT = MID_RI_LAST + 6;
    protected static final int MID_GET_EFFECTIVE_COMPONENTS
                                                           = MID_RI_LAST + 7;
    protected static final int MID_GET_REQUEST_POLICY      = MID_RI_LAST + 8;
    protected static final int MID_ADD_REQUEST_SERVICE_CONTEXT
                                                           = MID_RI_LAST + 9;

    // ClientRequestInfo validity table (see ptc/00-08-06 table 21-1).
    // Note: These must be in the same order as specified in contants.
    private static final boolean validCall[][] = {
        // LEGEND:
        // s_req = send_request     r_rep = receive_reply
        // s_pol = send_poll        r_exc = receive_exception
        //                          r_oth = receive_other
        //
        // A true value indicates call is valid at specified point.
        // A false value indicates the call is invalid.
        //
        //
        // NOTE: If the order or number of columns change, update
        // checkAccess() accordingly.
        //
        //                              { s_req, s_pol, r_rep, r_exc, r_oth }
        // RequestInfo methods:
        /*request_id*/                  { true , true , true , true , true  },
        /*operation*/                   { true , true , true , true , true  },
        /*arguments*/                   { true , false, true , false, false },
        /*exceptions*/                  { true , false, true , true , true  },
        /*contexts*/                    { true , false, true , true , true  },
        /*operation_context*/           { true , false, true , true , true  },
        /*result*/                      { false, false, true , false, false },
        /*response_expected*/           { true , true , true , true , true  },
        /*sync_scope*/                  { true , false, true , true , true  },
        /*reply_status*/                { false, false, true , true , true  },
        /*forward_reference*/           { false, false, false, false, true  },
        /*get_slot*/                    { true , true , true , true , true  },
        /*get_request_service_context*/ { true , false, true , true , true  },
        /*get_reply_service_context*/   { false, false, true , true , true  },
        //
        // ClientRequestInfo methods::
        /*target*/                      { true , true , true , true , true  },
        /*effective_target*/            { true , true , true , true , true  },
        /*effective_profile*/           { true , true , true , true , true  },
        /*received_exception*/          { false, false, false, true , false },
        /*received_exception_id*/       { false, false, false, true , false },
        /*get_effective_component*/     { true , false, true , true , true  },
        /*get_effective_components*/    { true , false, true , true , true  },
        /*get_request_policy*/          { true , false, true , true , true  },
        /*add_request_service_context*/ { true , false, false, false, false }
    };


    /*
     **********************************************************************
     * Public ClientRequestInfo interfaces
     **********************************************************************/

    /**
     * Creates a new ClientRequestInfo implementation.
     * The constructor is package scope since no other package need create
     * an instance of this class.
     */
    protected ClientRequestInfoImpl( ORB myORB ) {
        super( myORB );
        startingPointCall = CALL_SEND_REQUEST;
        endingPointCall = CALL_RECEIVE_REPLY;
    }

    /**
     * The object which the client called to perform the operation.
     */
    public org.omg.CORBA.Object target (){
        // access is currently valid for all states:
        //checkAccess( MID_TARGET );
        if (cachedTargetObject == null) {
            CorbaContactInfo corbaContactInfo = (CorbaContactInfo)
                messageMediator.getContactInfo();
            cachedTargetObject =
                iorToObject(corbaContactInfo.getTargetIOR());
        }
        return cachedTargetObject;
    }

    /**
     * The actual object on which the operation will be invoked.  If the
     * reply_status is LOCATION_FORWARD, then on subsequent requests,
     * effective_target will contain the forwarded IOR while target will
     * remain unchanged.
     */
    public org.omg.CORBA.Object effective_target() {
        // access is currently valid for all states:
        //checkAccess( MID_EFFECTIVE_TARGET );

        // Note: This is not necessarily the same as locatedIOR.
        // Reason: See the way we handle COMM_FAILURES in
        // ClientRequestDispatcher.createRequest, v1.32

        if (cachedEffectiveTargetObject == null) {
            CorbaContactInfo corbaContactInfo = (CorbaContactInfo)
                messageMediator.getContactInfo();
            // REVISIT - get through chain like getLocatedIOR helper below.
            cachedEffectiveTargetObject =
                iorToObject(corbaContactInfo.getEffectiveTargetIOR());
        }
        return cachedEffectiveTargetObject;
    }

    /**
     * The profile that will be used to send the request.  If a location
     * forward has occurred for this operation's object and that object's
     * profile change accordingly, then this profile will be that located
     * profile.
     */
    public TaggedProfile effective_profile (){
        // access is currently valid for all states:
        //checkAccess( MID_EFFECTIVE_PROFILE );

        if( cachedEffectiveProfile == null ) {
            CorbaContactInfo corbaContactInfo = (CorbaContactInfo)
                messageMediator.getContactInfo();
            cachedEffectiveProfile =
                corbaContactInfo.getEffectiveProfile().getIOPProfile();
        }

        // Good citizen: In the interest of efficiency, we assume interceptors
        // will not modify the returned TaggedProfile in any way so we need
        // not make a deep copy of it.

        return cachedEffectiveProfile;
    }

    /**
     * Contains the exception to be returned to the client.
     */
    public Any received_exception (){
        checkAccess( MID_RECEIVED_EXCEPTION );

        if( cachedReceivedException == null ) {
            cachedReceivedException = exceptionToAny( exception );
        }

        // Good citizen: In the interest of efficiency, we assume interceptors
        // will not modify the returned Any in any way so we need
        // not make a deep copy of it.

        return cachedReceivedException;
    }

    /**
     * The CORBA::RepositoryId of the exception to be returned to the client.
     */
    public String received_exception_id (){
        checkAccess( MID_RECEIVED_EXCEPTION_ID );

        if( cachedReceivedExceptionId == null ) {
            String result = null;

            if( exception == null ) {
                // Note: exception should never be null here since we will
                // throw a BAD_INV_ORDER if this is not called from
                // receive_exception.
                throw wrapper.exceptionWasNull() ;
            } else if( exception instanceof SystemException ) {
                String name = exception.getClass().getName();
                result = ORBUtility.repositoryIdOf(name);
            } else if( exception instanceof ApplicationException ) {
                result = ((ApplicationException)exception).getId();
            }

            // _REVISIT_ We need to be able to handle a UserException in the
            // DII case.  How do we extract the ID from a UserException?

            cachedReceivedExceptionId = result;
        }

        return cachedReceivedExceptionId;
    }

    /**
     * Returns the IOP::TaggedComponent with the given ID from the profile
     * selected for this request.  IF there is more than one component for a
     * given component ID, it is undefined which component this operation
     * returns (get_effective_component should be called instead).
     */
    public TaggedComponent get_effective_component (int id){
        checkAccess( MID_GET_EFFECTIVE_COMPONENT );

        return get_effective_components( id )[0];
    }

    /**
     * Returns all the tagged components with the given ID from the profile
     * selected for this request.
     */
    public TaggedComponent[] get_effective_components (int id){
        checkAccess( MID_GET_EFFECTIVE_COMPONENTS );
        Integer integerId = new Integer( id );
        TaggedComponent[] result = null;
        boolean justCreatedCache = false;

        if( cachedEffectiveComponents == null ) {
            cachedEffectiveComponents = new HashMap();
            justCreatedCache = true;
        }
        else {
            // Look in cache:
            result = (TaggedComponent[])cachedEffectiveComponents.get(
                integerId );
        }

        // null could mean we cached null or not in cache.
        if( (result == null) &&
            (justCreatedCache ||
            !cachedEffectiveComponents.containsKey( integerId ) ) )
        {
            // Not in cache.  Get it from the profile:
            CorbaContactInfo corbaContactInfo = (CorbaContactInfo)
                messageMediator.getContactInfo();
            IIOPProfileTemplate ptemp =
                (IIOPProfileTemplate)corbaContactInfo.getEffectiveProfile().
                getTaggedProfileTemplate();
            result = ptemp.getIOPComponents(myORB, id);
            cachedEffectiveComponents.put( integerId, result );
        }

        // As per ptc/00-08-06, section 21.3.13.6., If not found, raise
        // BAD_PARAM with minor code INVALID_COMPONENT_ID.
        if( (result == null) || (result.length == 0) ) {
            throw stdWrapper.invalidComponentId( integerId ) ;
        }

        // Good citizen: In the interest of efficiency, we will assume
        // interceptors will not modify the returned TaggedCompoent[], or
        // the TaggedComponents inside of it.  Otherwise, we would need to
        // clone the array and make a deep copy of its contents.

        return result;
    }

    /**
     * Returns the given policy in effect for this operation.
     */
    public Policy get_request_policy (int type){
        checkAccess( MID_GET_REQUEST_POLICY );
        // _REVISIT_ Our ORB is not policy-based at this time.
        throw wrapper.piOrbNotPolicyBased() ;
    }

    /**
     * Allows interceptors to add service contexts to the request.
     * <p>
     * There is no declaration of the order of the service contexts.  They
     * may or may not appear in the order they are added.
     */
    public void add_request_service_context (ServiceContext service_context,
                                             boolean replace)
    {
        checkAccess( MID_ADD_REQUEST_SERVICE_CONTEXT );

        if( cachedRequestServiceContexts == null ) {
            cachedRequestServiceContexts = new HashMap();
        }

        addServiceContext( cachedRequestServiceContexts,
                           messageMediator.getRequestServiceContexts(),
                           service_context, replace );
    }

    // NOTE: When adding a method, be sure to:
    // 1. Add a MID_* constant for that method
    // 2. Call checkAccess at the start of the method
    // 3. Define entries in the validCall[][] table for interception points.

    /*
     **********************************************************************
     * Public RequestInfo interfaces
     *
     * These are implemented here because they have differing
     * implementations depending on whether this is a client or a server
     * request info object.
     **********************************************************************/

    /**
     * See RequestInfoImpl for javadoc.
     */
    public int request_id (){
        // access is currently valid for all states:
        //checkAccess( MID_REQUEST_ID );
        /*
         * NOTE: The requestId in client interceptors is the same as the
         * GIOP request id.  This works because both interceptors and
         * request ids are scoped by the ORB on the client side.
         */
        return messageMediator.getRequestId();
    }

    /**
     * See RequestInfoImpl for javadoc.
     */
    public String operation (){
        // access is currently valid for all states:
        //checkAccess( MID_OPERATION );
        return messageMediator.getOperationName();
    }

    /**
     * See RequestInfoImpl for javadoc.
     */
    public Parameter[] arguments (){
        checkAccess( MID_ARGUMENTS );

        if( cachedArguments == null ) {
            if( request == null ) {
                throw stdWrapper.piOperationNotSupported1() ;
            }

            // If it is DII request then get the arguments from the DII req
            // and convert that into parameters.
            cachedArguments = nvListToParameterArray( request.arguments() );
        }

        // Good citizen: In the interest of efficiency, we assume
        // interceptors will be "good citizens" in that they will not
        // modify the contents of the Parameter[] array.  We also assume
        // they will not change the values of the containing Anys.

        return cachedArguments;
    }

    /**
     * See RequestInfoImpl for javadoc.
     */
    public TypeCode[] exceptions (){
        checkAccess( MID_EXCEPTIONS );

        if( cachedExceptions == null ) {
            if( request == null ) {
               throw stdWrapper.piOperationNotSupported2() ;
            }

            // Get the list of exceptions from DII request data, If there are
            // no exceptions raised then this method will return null.
            ExceptionList excList = request.exceptions( );
            int count = excList.count();
            TypeCode[] excTCList = new TypeCode[count];
            try {
                for( int i = 0; i < count; i++ ) {
                    excTCList[i] = excList.item( i );
                }
            } catch( Exception e ) {
                throw wrapper.exceptionInExceptions( e ) ;
            }

            cachedExceptions = excTCList;
        }

        // Good citizen: In the interest of efficiency, we assume
        // interceptors will be "good citizens" in that they will not
        // modify the contents of the TypeCode[] array.  We also assume
        // they will not change the values of the containing TypeCodes.

        return cachedExceptions;
    }

    /**
     * See RequestInfoImpl for javadoc.
     */
    public String[] contexts (){
        checkAccess( MID_CONTEXTS );

        if( cachedContexts == null ) {
            if( request == null ) {
                throw stdWrapper.piOperationNotSupported3() ;
            }

            // Get the list of contexts from DII request data, If there are
            // no contexts then this method will return null.
            ContextList ctxList = request.contexts( );
            int count = ctxList.count();
            String[] ctxListToReturn = new String[count];
            try {
                for( int i = 0; i < count; i++ ) {
                    ctxListToReturn[i] = ctxList.item( i );
                }
            } catch( Exception e ) {
                throw wrapper.exceptionInContexts( e ) ;
            }

            cachedContexts = ctxListToReturn;
        }

        // Good citizen: In the interest of efficiency, we assume
        // interceptors will be "good citizens" in that they will not
        // modify the contents of the String[] array.

        return cachedContexts;
    }

    /**
     * See RequestInfoImpl for javadoc.
     */
    public String[] operation_context (){
        checkAccess( MID_OPERATION_CONTEXT );

        if( cachedOperationContext == null ) {
            if( request == null ) {
                throw stdWrapper.piOperationNotSupported4() ;
            }

            // Get the list of contexts from DII request data, If there are
            // no contexts then this method will return null.
            Context ctx = request.ctx( );
            // _REVISIT_ The API for get_values is not compliant with the spec,
            // Revisit this code once it's fixed.
            // _REVISIT_ Our ORB doesn't support Operation Context, This code
            // will not be excerscised until it's supported.
            // The first parameter in get_values is the start_scope which
            // if blank makes it as a global scope.
            // The second parameter is op_flags which is set to RESTRICT_SCOPE
            // As there is only one defined in the spec.
            // The Third param is the pattern which is '*' requiring it to
            // get all the contexts.
            NVList nvList = ctx.get_values( "", CTX_RESTRICT_SCOPE.value,"*" );
            String[] context = new String[(nvList.count() * 2) ];
            if( ( nvList != null ) &&( nvList.count() != 0 ) ) {
                // The String[] array will contain Name and Value for each
                // context and hence double the size in the array.
                int index = 0;
                for( int i = 0; i < nvList.count(); i++ ) {
                    NamedValue nv;
                    try {
                        nv = nvList.item( i );
                    }
                    catch (Exception e ) {
                        return (String[]) null;
                    }
                    context[index] = nv.name();
                    index++;
                    context[index] = nv.value().extract_string();
                    index++;
                }
            }

            cachedOperationContext = context;
        }

        // Good citizen: In the interest of efficiency, we assume
        // interceptors will be "good citizens" in that they will not
        // modify the contents of the String[] array.

        return cachedOperationContext;
    }

    /**
     * See RequestInfoImpl for javadoc.
     */
    public Any result (){
        checkAccess( MID_RESULT );

        if( cachedResult == null ) {
            if( request == null ) {
                throw stdWrapper.piOperationNotSupported5() ;
            }
            // Get the result from the DII request data.
            NamedValue nvResult = request.result( );

            if( nvResult == null ) {
                throw wrapper.piDiiResultIsNull() ;
            }

            cachedResult = nvResult.value();
        }

        // Good citizen: In the interest of efficiency, we assume that
        // interceptors will not modify the contents of the result Any.
        // Otherwise, we would need to create a deep copy of the Any.

        return cachedResult;
    }

    /**
     * See RequestInfoImpl for javadoc.
     */
    public boolean response_expected (){
        // access is currently valid for all states:
        //checkAccess( MID_RESPONSE_EXPECTED );
        return ! messageMediator.isOneWay();
    }

    /**
     * See RequestInfoImpl for javadoc.
     */
    public Object forward_reference (){
        checkAccess( MID_FORWARD_REFERENCE );
        // Check to make sure we are in LOCATION_FORWARD
        // state as per ptc/00-08-06, table 21-1
        // footnote 2.
        if( replyStatus != LOCATION_FORWARD.value ) {
            throw stdWrapper.invalidPiCall1() ;
        }

        // Do not cache this value since if an interceptor raises
        // forward request then the next interceptor in the
        // list should see the new value.
        IOR ior = getLocatedIOR();
        return iorToObject(ior);
    }

    private IOR getLocatedIOR()
    {
        IOR ior;
        CorbaContactInfoList contactInfoList = (CorbaContactInfoList)
            messageMediator.getContactInfo().getContactInfoList();
        ior = contactInfoList.getEffectiveTargetIOR();
        return ior;
    }

    protected void setLocatedIOR(IOR ior)
    {
        ORB orb = (ORB) messageMediator.getBroker();

        CorbaContactInfoListIterator iterator = (CorbaContactInfoListIterator)
            ((CorbaInvocationInfo)orb.getInvocationInfo())
            .getContactInfoListIterator();

        // REVISIT - this most likely causes reportRedirect to happen twice.
        // Once here and once inside the request dispatcher.
        iterator.reportRedirect(
            (CorbaContactInfo)messageMediator.getContactInfo(),
            ior);
    }

    /**
     * See RequestInfoImpl for javadoc.
     */
    public org.omg.IOP.ServiceContext get_request_service_context( int id ) {
        checkAccess( MID_GET_REQUEST_SERVICE_CONTEXT );

        if( cachedRequestServiceContexts == null ) {
            cachedRequestServiceContexts = new HashMap();
        }

        return  getServiceContext(cachedRequestServiceContexts,
                                  messageMediator.getRequestServiceContexts(),
                                  id);
    }

    /**
     * does not contain an etry for that ID, BAD_PARAM with a minor code of
     * TBD_BP is raised.
     */
    public org.omg.IOP.ServiceContext get_reply_service_context( int id ) {
        checkAccess( MID_GET_REPLY_SERVICE_CONTEXT );

        if( cachedReplyServiceContexts == null ) {
            cachedReplyServiceContexts = new HashMap();
        }

        // In the event this is called from a oneway, we will have no
        // response object.
        //
        // In the event this is called after a IIOPConnection.purgeCalls,
        // we will have a response object, but that object will
        // not contain a header (which would hold the service context
        // container).  See bug 4624102.
        //
        // REVISIT: this is the only thing used
        // from response at this time.  However, a more general solution
        // would avoid accessing other parts of response's header.
        //
        // Instead of throwing a NullPointer, we will
        // "gracefully" handle these with a BAD_PARAM with minor code 25.

        try {
            ServiceContexts serviceContexts =
                messageMediator.getReplyServiceContexts();
            if (serviceContexts == null) {
                throw new NullPointerException();
            }
            return getServiceContext(cachedReplyServiceContexts,
                                     serviceContexts, id);
        } catch (NullPointerException e) {
            // REVISIT how this is programmed - not what it does.
            // See purge calls test.  The waiter is woken up by the
            // call to purge calls - but there is no reply containing
            // service contexts.
            throw stdWrapper.invalidServiceContextId( e ) ;
        }
    }

    //
    // REVISIT
    // Override RequestInfoImpl connection to work in framework.
    //

    public com.sun.corba.se.spi.legacy.connection.Connection connection()
    {
        return (com.sun.corba.se.spi.legacy.connection.Connection)
            messageMediator.getConnection();
    }



    /*
     **********************************************************************
     * Package-scope interfaces
     **********************************************************************/

    protected void setInfo(MessageMediator messageMediator)
    {
        this.messageMediator = (CorbaMessageMediator)messageMediator;
        // REVISIT - so mediator can handle DII in subcontract.
        this.messageMediator.setDIIInfo(request);
    }

    /**
     * Set or reset the retry request flag.
     */
    void setRetryRequest( RetryType retryRequest ) {
        this.retryRequest = retryRequest;
    }

    /**
     * Retrieve the current retry request status.
     */
    RetryType getRetryRequest() {
        // 6763340
        return this.retryRequest;
    }

    /**
     * Increases the entry count by 1.
     */
    void incrementEntryCount() {
        this.entryCount++;
    }

    /**
     * Decreases the entry count by 1.
     */
    void decrementEntryCount() {
        this.entryCount--;
    }

    /**
     * Retrieve the current entry count
     */
    int getEntryCount() {
        return this.entryCount;
    }

    /**
     * Overridden from RequestInfoImpl.  Calls the super class, then
     * sets the ending point call depending on the reply status.
     */
    protected void setReplyStatus( short replyStatus ) {
        super.setReplyStatus( replyStatus );
        switch( replyStatus ) {
        case SUCCESSFUL.value:
            endingPointCall = CALL_RECEIVE_REPLY;
            break;
        case SYSTEM_EXCEPTION.value:
        case USER_EXCEPTION.value:
            endingPointCall = CALL_RECEIVE_EXCEPTION;
            break;
        case LOCATION_FORWARD.value:
        case TRANSPORT_RETRY.value:
            endingPointCall = CALL_RECEIVE_OTHER;
            break;
        }
    }

    /**
     * Sets DII request object in the RequestInfoObject.
     */
    protected void setDIIRequest(org.omg.CORBA.Request req) {
         request = req;
    }

    /**
     * Keeps track of whether initiate was called for a DII request.  The ORB
     * needs to know this so it knows whether to ignore a second call to
     * initiateClientPIRequest or not.
     */
    protected void setDIIInitiate( boolean diiInitiate ) {
        this.diiInitiate = diiInitiate;
    }

    /**
     * See comment for setDIIInitiate
     */
    protected boolean isDIIInitiate() {
        return this.diiInitiate;
    }

    /**
     * The PICurrent stack should only be popped if it was pushed.
     * This is generally the case.  But exceptions which occur
     * after the stub's entry to _request but before the push
     * end up in _releaseReply which will try to pop unless told not to.
     */
    protected void setPICurrentPushed( boolean piCurrentPushed ) {
        this.piCurrentPushed = piCurrentPushed;
    }

    protected boolean isPICurrentPushed() {
        return this.piCurrentPushed;
    }

    /**
     * Overridden from RequestInfoImpl.
     */
    protected void setException( Exception exception ) {
        super.setException( exception );

        // Clear cached values:
        cachedReceivedException = null;
        cachedReceivedExceptionId = null;
    }

    protected boolean getIsOneWay() {
        return ! response_expected();
    }

    /**
     * See description for RequestInfoImpl.checkAccess
     */
    protected void checkAccess( int methodID )
        throws BAD_INV_ORDER
    {
        // Make sure currentPoint matches the appropriate index in the
        // validCall table:
        int validCallIndex = 0;
        switch( currentExecutionPoint ) {
        case EXECUTION_POINT_STARTING:
            switch( startingPointCall ) {
            case CALL_SEND_REQUEST:
                validCallIndex = 0;
                break;
            case CALL_SEND_POLL:
                validCallIndex = 1;
                break;
            }
            break;
        case EXECUTION_POINT_ENDING:
            switch( endingPointCall ) {
            case CALL_RECEIVE_REPLY:
                validCallIndex = 2;
                break;
            case CALL_RECEIVE_EXCEPTION:
                validCallIndex = 3;
                break;
            case CALL_RECEIVE_OTHER:
                validCallIndex = 4;
                break;
            }
            break;
        }

        // Check the validCall table:
        if( !validCall[methodID][validCallIndex] ) {
            throw stdWrapper.invalidPiCall2() ;
        }
    }

}

// End of file.
