/*
 * @(#)ServerRequestInfoImpl.java	1.36 01/12/03
 *
 * Copyright 2002 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.sun.corba.se.internal.Interceptors;
             
import org.omg.CORBA.Any;
import org.omg.CORBA.BAD_INV_ORDER;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.LocalObject;
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.Policy;
import org.omg.CORBA.TypeCode;

import org.omg.CORBA.portable.ObjectImpl;

import org.omg.PortableServer.Servant;

import org.omg.IOP.TaggedProfile;
import org.omg.IOP.ServiceContext;
import com.sun.corba.se.internal.iiop.messages.ReplyMessage;
import org.omg.Dynamic.Parameter;
import org.omg.PortableInterceptor.InvalidSlot;
import org.omg.PortableInterceptor.ServerRequestInfo;
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.internal.core.ServerRequest;
import com.sun.corba.se.internal.core.ServiceContexts;
import com.sun.corba.se.internal.POA.POAImpl;

import java.util.*;

/**
 * Implementation of the ServerRequestInfo interface as specified in
 * orbos/99-12-02 section 5.4.3.
 */
public final class ServerRequestInfoImpl 
    extends RequestInfoImpl 
    implements ServerRequestInfo 
{
    // The available constants for startingPointCall
    static final int CALL_RECEIVE_REQUEST_SERVICE_CONTEXT = 0;
    
    // The available constants for intermediatePointCall.  The default (0)
    // is receive_request, but can be set to none on demand.
    static final int CALL_RECEIVE_REQUEST = 0;
    static final int CALL_INTERMEDIATE_NONE = 1;
    
    // The available constants for endingPointCall
    static final int CALL_SEND_REPLY = 0;
    static final int CALL_SEND_EXCEPTION = 1;
    static final int CALL_SEND_OTHER = 2;

    //////////////////////////////////////////////////////////////////////
    //
    // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
    //
    //////////////////////////////////////////////////////////////////////
    
    // Set to true if the server ending point raised ForwardRequest at some
    // point in the ending point.
    private boolean forwardRequestRaisedInEnding;

    // Sources of server request information:
    private ServerRequest request;
    private java.lang.Object servant;
    private byte[] objectId;
    private byte[] adapterId;
    private ArrayList addReplyServiceContextQueue;
    private ReplyMessage replyMessage;
    private String targetMostDerivedInterface;
    private NVList dsiArguments;
    private Any dsiResult;
    private Any dsiException;
    private boolean isDynamic;
    private POAImpl poaimpl;
    private int serverRequestId;

    // Cached information:
    private Parameter[] cachedArguments;
    private Any cachedSendingException;
    // key = Integer, value = IOP.ServiceContext.
    private HashMap cachedRequestServiceContexts; 
    // key = Integer, value = IOP.ServiceContext.
    private HashMap cachedReplyServiceContexts; 
    
    //////////////////////////////////////////////////////////////////////
    //
    // 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 as declared above.
        
        forwardRequestRaisedInEnding = false;

        request = null;
	servant = null;
	objectId = null;
	adapterId = null;
	addReplyServiceContextQueue = null;
	replyMessage = null;
	targetMostDerivedInterface = null;
	dsiArguments = null;
	dsiResult = null;
	dsiException = null;
	isDynamic = false;
	poaimpl = null;
	serverRequestId = piOrb.allocateServerRequestId();

	// reset cached attributes:
	cachedArguments = null;
	cachedSendingException = null;
	cachedRequestServiceContexts = null;
	cachedReplyServiceContexts = null;

        startingPointCall = CALL_RECEIVE_REQUEST_SERVICE_CONTEXT;
        intermediatePointCall = CALL_RECEIVE_REQUEST;
        endingPointCall = CALL_SEND_REPLY;
    }
    
    /*
     **********************************************************************
     * Access protection
     **********************************************************************/

    // Method IDs for all methods in ServerRequestInfo.  This allows for a 
    // convenient O(1) lookup for checkAccess().
    protected static final int MID_SENDING_EXCEPTION       = MID_RI_LAST +  1;
    protected static final int MID_OBJECT_ID               = MID_RI_LAST +  2;
    protected static final int MID_ADAPTER_ID              = MID_RI_LAST +  3;
    protected static final int MID_TARGET_MOST_DERIVED_INTERFACE 
                                                           = MID_RI_LAST +  4;
    protected static final int MID_GET_SERVER_POLICY       = MID_RI_LAST +  5;
    protected static final int MID_SET_SLOT                = MID_RI_LAST +  6;
    protected static final int MID_TARGET_IS_A             = MID_RI_LAST +  7;
    protected static final int MID_ADD_REPLY_SERVICE_CONTEXT
                                                           = MID_RI_LAST +  8;
    
    // ServerRequestInfo validity table (see ptc/00-08-06 table 21-2).
    // Note: These must be in the same order as specified in contants.
    protected static final boolean validCall[][] = {
        // LEGEND:
        // r_rsc = receive_request_service_contexts
        // r_req = receive_request
        // s_rep = send_reply
        // s_exc = send_exception
        // s_oth = send_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.
        //
        //                              { r_rsc, r_req, s_rep, s_exc, s_oth }
        // RequestInfo methods:
        /*request_id*/                  { true , true , true , true , true  },
        /*operation*/                   { true , true , true , true , true  },
        /*arguments*/                   { false, true , true , false, false },
        /*exceptions*/                  { false, true , true , true , true  },
        /*contexts*/                    { false, true , true , true , true  },
        /*operation_context*/           { false, true , true , false, false },
        /*result*/                      { false, false, true , false, false },
        /*response_expected*/           { true , true , true , true , true  },
        /*sync_scope*/                  { true , true , 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 , true , true , true , true  },
        /*get_reply_service_context*/   { false, false, true , true , true  },
        //
        // ServerRequestInfo methods::
        /*sending_exception*/           { false, false, false, true , false },
        /*object_id*/                   { false, true , true , true , true  },
        /*adapter_id*/                  { false, true , true , true , true  },
        /*target_most_derived_inte...*/ { false, true , false, false, false },
        /*get_server_policy*/           { true , true , true , true , true  },
        /*set_slot*/                    { true , true , true , true , true  },
        /*target_is_a*/                 { false, true , false, false, false },
        /*add_reply_service_context*/   { true , true , true , true , true  }
    };
    
    /*
     **********************************************************************
     * Public interfaces
     **********************************************************************/
    
    /**
     * Creates a new ServerRequestInfo implementation.
     * The constructor is package scope since no other package need create
     * an instance of this class.
     */
    ServerRequestInfoImpl( PIORB piOrb ) { 
        super( piOrb ); 
        startingPointCall = CALL_RECEIVE_REQUEST_SERVICE_CONTEXT;
        intermediatePointCall = CALL_RECEIVE_REQUEST;
        endingPointCall = CALL_SEND_REPLY;
	serverRequestId = piOrb.allocateServerRequestId();
    }
    
    /**
     * Any containing the exception to be returned to the client.
     */
    public Any sending_exception () {
        checkAccess( MID_SENDING_EXCEPTION );

	if( cachedSendingException == null ) {
	    Any result;

	    if( dsiException != null ) {
		result = dsiException;
	    }
	    else if( exception != null ) {
		result = exceptionToAny( exception );
	    }
	    else {
		// sending_exception should not be callable if both dsiException
		// and exception are null.
		throw new INTERNAL(
		    "Exception not available in sending_exception.",
		    MinorCodes.EXCEPTION_UNAVAILABLE, 
		    CompletionStatus.COMPLETED_NO );
	    }

	    cachedSendingException = result;
	}

	return cachedSendingException;
    }
    
    /**
     * The opaque object_id describing the target of the operation invocation.
     */
    public byte[] object_id () {
        checkAccess( MID_OBJECT_ID );

	if( objectId == null ) {
	    // For some reason, we never set object id.  This could be 
	    // because a servant locator caused a location forward or
	    // raised an exception.  As per ptc/00-08-06, section 21.3.14,
	    // we throw NO_RESOURCES
	    throw new NO_RESOURCES( "object id never set.", 
		MinorCodes.PI_OPERATION_NOT_SUPPORTED, 
		CompletionStatus.COMPLETED_NO );
	}

	// Good citizen: In the interest of efficiency, we will assume
	// interceptors will not change the resulting byte[] array.
	// Otherwise, we would need to make a clone of this array.

	return objectId;
    }
    
    /**
     * The opaque identifier for the object adapter.
     */
    public byte[] adapter_id () {
        checkAccess( MID_ADAPTER_ID );

	if( adapterId == null ) {
	    // For some reason, we never set the adapter id.  This could be 
	    // because a servant locator caused a location forward or
	    // raised an exception.  As per ptc/00-08-06, section 21.3.14,
	    // we throw NO_RESOURCES
	    throw new NO_RESOURCES( "adapter id never set.", 
		MinorCodes.PI_OPERATION_NOT_SUPPORTED, 
		CompletionStatus.COMPLETED_NO );
	}

	// Good citizen: In the interest of efficiency, we will assume
	// interceptors will not change the resulting byte[] array.
	// Otherwise, we would need to make a clone of this array.

	return adapterId;
    }
    
    /**
     * The RepositoryID for the most derived interface of the servant.
     */
    public String target_most_derived_interface () {
        checkAccess( MID_TARGET_MOST_DERIVED_INTERFACE );
	return targetMostDerivedInterface;
    }
    
    /**
     * Returns the policy in effect for this operation for the given policy 
     * type.
     */
    public Policy get_server_policy (int type) {
	// access is currently valid for all states:
        //checkAccess( MID_GET_SERVER_POLICY );

	Policy result = null;

	if( poaimpl != null ) {
	    result = poaimpl.get_effective_policy( type );
	}

	// _REVISIT_ RTF Issue: get_server_policy spec not in sync with
	// get_effective_policy spec.

	return result;
    }
    
    /**
     * Allows an Interceptor to set a slot in the Current that is in the scope 
     * of the request.  If data already exists in that slot, it will be 
     * overwritten.  If the ID does not define an allocated slot, InvalidSlot 
     * is raised.
     */
    public void set_slot (int id, Any data) throws InvalidSlot {
	// access is currently valid for all states:
        //checkAccess( MID_SET_SLOT );
   
        slotTable.set_slot( id, data );
    }
    
    /**
     * Returns true if the servant is the given RepositoryId, false if it is 
     * not.
     */
    public boolean target_is_a (String id) {
        checkAccess( MID_TARGET_IS_A );

	boolean result;
	if( servant instanceof Servant ) {
	    result = ((Servant)servant)._is_a( id );
	}
	else if( servant instanceof ObjectImpl ) {
	    result = ((ObjectImpl)servant)._is_a( id );
	}
	else {
	    throw new INTERNAL( 
		"During target_is_a, servant was not a Servant or ObjectImpl",
		MinorCodes.SERVANT_INVALID,
		CompletionStatus.COMPLETED_NO );
	}

	return result;
    }
    
    /**
     * Allows Interceptors to add service contexts to the request.
     */
    public void add_reply_service_context ( ServiceContext service_context, 
                                            boolean replace )
    {
	// access is currently valid for all states:
        //checkAccess( MID_ADD_REPLY_SERVICE_CONTEXT );

        if( currentExecutionPoint == EXECUTION_POINT_ENDING ) {
	    ServiceContexts scs = replyMessage.getServiceContexts();

	    // May be null.  If this is null, create a new one in its place.
	    if( scs == null ) {
		scs = new ServiceContexts( piOrb );
		replyMessage.setServiceContexts( scs );
	    }

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

	    // This is during and ending point, so we now have enough
	    // information to add the reply service context.
	    addServiceContext( cachedReplyServiceContexts, scs, 
			       service_context, replace );
	}

	// We enqueue all adds for the following reasons:
	//
	// If we are not in the ending point then we do not yet have a
	// pointer to the ServiceContexts object so we cannot access the
	// service contexts until we get to the ending point. 
	// So we enqueue this add reply service context request.
	// It is added when we do have a handle on the service contexts object.
	//
	// If we are in the ending point and we just add directly to the
	// SC container but then an interceptor raises a SystemException
	// then that add will be lost since a new container is created
	// for the SystemException response.
	//
	// Therefore we always enqueue and never dequeue (per request) so
	// that all adds will be completed.

	AddReplyServiceContextCommand addReply = 
	    new AddReplyServiceContextCommand();
	addReply.service_context = service_context;
	addReply.replace = replace;

	if( addReplyServiceContextQueue == null ) {
	    addReplyServiceContextQueue = new ArrayList();
	}

	// REVISIT: this does not add to the cache.
	enqueue( addReply );
    }

    // 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 ServerRequestInfo for javadocs.
     */
    public int request_id (){
        // access is currently valid for all states:
        //checkAccess( MID_REQUEST_ID );
	/*
	 * NOTE: The request id in server interceptors is NOT the
	 * same as the GIOP request id.  The ORB may be servicing several
	 * connections, each with possibly overlapping sets of request ids.
	 * Therefore we create a request id specific to interceptors.
	 */
        return serverRequestId;
    }

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

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

	if( cachedArguments == null ) {
	    if( !isDynamic ) {
		throw new NO_RESOURCES(
		    "The Portable Java Bindings do not support arguments()",
		    MinorCodes.PI_OPERATION_NOT_SUPPORTED,
		    CompletionStatus.COMPLETED_NO );
	    }

	    if( dsiArguments == null ) {
		throw new NO_RESOURCES(
		    "ServerRequest::arguments() never called.",
		    MinorCodes.PI_OPERATION_NOT_SUPPORTED,
		    CompletionStatus.COMPLETED_NO );
	    }

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

        // 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 ServerRequestInfo for javadocs.
     */
    public TypeCode[] exceptions (){
        checkAccess( MID_EXCEPTIONS );

	// _REVISIT_ PI RTF Issue: No exception list on server side.

	throw new NO_RESOURCES(
	    "The Portable Java Bindings do not support exceptions()",
	    MinorCodes.PI_OPERATION_NOT_SUPPORTED,
	    CompletionStatus.COMPLETED_NO );
    }

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

	// We do not support this because our ORB does not send contexts.

	throw new NO_RESOURCES(
	    "The Portable Java Bindings do not support contexts()",
	    MinorCodes.PI_OPERATION_NOT_SUPPORTED,
	    CompletionStatus.COMPLETED_NO );
    }

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

	// We do not support this because our ORB does not send 
	// operation_context.

	throw new NO_RESOURCES(
	    "The Portable Java Bindings do not support operation_context()",
	    MinorCodes.PI_OPERATION_NOT_SUPPORTED,
	    CompletionStatus.COMPLETED_NO );
    }

    /**
     * See ServerRequestInfo for javadocs.
     */
    public Any result (){
        checkAccess( MID_RESULT );

	if( !isDynamic ) {
	    throw new NO_RESOURCES(
		"The Portable Java Bindings do not support result()",
		MinorCodes.PI_OPERATION_NOT_SUPPORTED,
		CompletionStatus.COMPLETED_NO );
	}

	if( dsiResult == null ) {
	    throw new INTERNAL(
		"dsiResult should not be null.",
		MinorCodes.PI_DSI_RESULT_IS_NULL,
		CompletionStatus.COMPLETED_NO );
	}

	// 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 dsiResult;
    }

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

    /**
     * See ServerRequestInfo for javadocs.
     */
    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-2
        // footnote 2.
        if( replyStatus != LOCATION_FORWARD.value ) {
            throw new BAD_INV_ORDER(
                "Invalid to query forward_reference() when reply status " +
                "is not LOCATION_FORWARD.",
                MinorCodes.INVALID_PI_CALL, CompletionStatus.COMPLETED_NO );
        }

	return getForwardRequestException().forward;
    }

    /**
     * See ServerRequestInfo for javadocs.
     */
    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, 
				  request.getServiceContexts(), id );
    }

    /**
     * See ServerRequestInfo for javadocs.
     */
    public org.omg.IOP.ServiceContext get_reply_service_context( int id ) {
        checkAccess( MID_GET_REPLY_SERVICE_CONTEXT );       

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

        return getServiceContext( cachedReplyServiceContexts,
				  replyMessage.getServiceContexts(), id );
    }

    /*
     **********************************************************************
     * Private-scope classes and methods
     **********************************************************************/

    // A command encapsulating a request to add a reply service context.
    // These commands are enqueued until we have a handle on the actual
    // reply service context, at which point they are executed.
    private class AddReplyServiceContextCommand {
	ServiceContext service_context;
	boolean replace;
    }

    // Adds the given add reply service context command to the queue of
    // such commands.  If a command is detected to have the same id as
    // the service context in this command, and replace is false, 
    // BAD_INV_ORDER is thrown.  If replace is true, the original command
    // in the queue is replaced by this command.
    private void enqueue( AddReplyServiceContextCommand addReply ) {
	int size = addReplyServiceContextQueue.size();
	boolean found = false;

	for( int i = 0; i < size; i++ ) {
	    AddReplyServiceContextCommand cmd = 
		(AddReplyServiceContextCommand)
		addReplyServiceContextQueue.get( i );

	    if( cmd.service_context.context_id == 
		addReply.service_context.context_id ) 
	    {
		found = true;
		if( addReply.replace ) {
		    addReplyServiceContextQueue.set( i, addReply );
		}
		else {
		    throw new BAD_INV_ORDER( 
			"Service context already exists with the given ID " +
			"and replace flag not set.", 
			MinorCodes.SERVICE_CONTEXT_ADD_FAILED,
			CompletionStatus.COMPLETED_NO );
		}
		break;
	    }
	}

	if( !found ) {
	    addReplyServiceContextQueue.add( addReply );
	}
    }

    /*
     **********************************************************************
     * Package and protected-scope methods
     **********************************************************************/

    /**
     * Overridden from RequestInfoImpl.  This version calls the super
     * and then, if we are changing to ending points, executes all
     * enqueued AddReplyServiceContextCommands.
     */
    protected void setCurrentExecutionPoint( int executionPoint ) {
	super.setCurrentExecutionPoint( executionPoint );

	// If we are transitioning to ending point, we will now have a pointer
	// to the reply service contexts, so we can execute all queued
	// add reply service context requests.
	if( (executionPoint == EXECUTION_POINT_ENDING) &&
	    (addReplyServiceContextQueue != null) ) 
	{
	    int size = addReplyServiceContextQueue.size();
	    for( int i = 0; i < size; i++ ) {
		AddReplyServiceContextCommand addReply = 
		    (AddReplyServiceContextCommand)
		    addReplyServiceContextQueue.get( i );
		try {
		    add_reply_service_context( addReply.service_context, 
					       addReply.replace );
	        }
		catch( BAD_INV_ORDER e ) {
		    // _REVISIT_  The only way this can happen is if during 
		    // rrsc or rr, the interceptor tried to add with 
		    // replace=false to a service context that is present in 
		    // the reply message.  At that time there was no way for 
		    // us to check for this, so the best we can do is ignore 
		    // the original request.
		}
	    }

	    // We specifically do not empty the SC queue so that if
	    // the interceptor raises an exception the queued service contexts
	    // will be put in the exception response.
	}
    }

    /** 
     * Stores the various sources of information used for this info object.
     */
    protected void setInfo( ServerRequest request, POAImpl poaimpl, 
	byte[] objectId, byte[] adapterId ) 
    {
        this.request = request;
        this.objectId = objectId;
	this.adapterId = adapterId;
	this.poaimpl = poaimpl;
	this.connection = 
	    ((com.sun.corba.se.internal.iiop.IIOPInputStream)request)
	    .getConnection();
    }

    /**
     * Stores the various sources of information used for this info object.
     */
    protected void setDSIArguments( NVList arguments ) {
	this.dsiArguments = arguments;
    }

    /**
     * Stores the various sources of information used for this info object.
     */
    protected void setDSIException( Any exception ) {
	this.dsiException = exception;

	// Clear cached exception value:
	cachedSendingException = null;
    }

    /**
     * Stores the various sources of information used for this info object.
     */
    protected void setDSIResult( Any result ) {
	this.dsiResult = result;
    }

    /**
     * Sets the exception to be returned by received_exception and
     * received_exception_id.
     */
    protected void setException( Exception exception ) {
	super.setException( exception );

	// Make sure DSIException is null because this is the more recent one.
	this.dsiException = null;

	// Clear cached exception value:
	cachedSendingException = null;
    }
    
    /**
     * Stores the various sources of information used for this info object.
     */
    protected void setInfo( java.lang.Object servant, 
			    String targetMostDerivedInterface ) 
    {
        this.servant = servant;
	this.targetMostDerivedInterface = targetMostDerivedInterface;
	this.isDynamic = 
	    (servant instanceof 
	    org.omg.PortableServer.DynamicImplementation) ||
	    (servant instanceof org.omg.CORBA.DynamicImplementation);
    }
    
    /**
     * Set reply message
     */
    void setReplyMessage( ReplyMessage replyMessage ) {
	this.replyMessage = replyMessage;
    }

    /**
     * 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_SEND_REPLY;
            break;
        case SYSTEM_EXCEPTION.value:
        case USER_EXCEPTION.value:
            endingPointCall = CALL_SEND_EXCEPTION;
            break;
        case LOCATION_FORWARD.value:
        case TRANSPORT_RETRY.value:
            endingPointCall = CALL_SEND_OTHER;
            break;
        }
    }
    
    /**
     * Release the servant object so the user has control over its lifetime.
     * Called after receive_request is finished executing.
     */
    void releaseServant() {
        this.servant = null;
    }
    
    /**
     * Sets the forwardRequestRaisedInEnding flag to true, indicating that
     * a server ending point has raised location forward at some point.
     */
    void setForwardRequestRaisedInEnding() {
	this.forwardRequestRaisedInEnding = true;
    }

    /**
     * Returns true if ForwardRequest was raised by a server ending point
     * or false otherwise.
     */
    boolean isForwardRequestRaisedInEnding() {
	return this.forwardRequestRaisedInEnding;
    }

    /**
     * Returns true if this is a dynamic invocation, or false if not
     */
    boolean isDynamic() {
      return this.isDynamic;
    }

    /**
     * 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:
            validCallIndex = 0;
            break;
        case EXECUTION_POINT_INTERMEDIATE:
            validCallIndex = 1;
            break;
        case EXECUTION_POINT_ENDING:
            switch( endingPointCall ) {
            case CALL_SEND_REPLY:
                validCallIndex = 2;
                break;
            case CALL_SEND_EXCEPTION:
                validCallIndex = 3;
                break;
            case CALL_SEND_OTHER:
                validCallIndex = 4;
                break;
            }
            break;
        }
        
        // Check the validCall table:
        if( !validCall[methodID][validCallIndex] ) {
            throw new BAD_INV_ORDER( 
                "Cannot access this info attribute/method at this point.",
                MinorCodes.INVALID_PI_CALL, CompletionStatus.COMPLETED_NO );
        }
    }
    
}
