/*
 * @(#)ClientRequestInfoImpl.java	1.46 05/11/17
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN 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.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 boolean 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.
        
        retryRequest = false;

        // 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.
    protected 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( boolean retryRequest ) {
        this.retryRequest = retryRequest;
    }
    
    /**
     * Retrieve the current retry request status.
     */
    boolean getRetryRequest() {
        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.
