/*
 * @(#)RequestInfoImpl.java	1.41 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.Dynamic.Parameter;
import com.sun.corba.se.interceptor.RequestInfoExt;
import com.sun.corba.se.internal.corba.ClientDelegate;
import com.sun.corba.se.internal.core.ClientResponse;
import com.sun.corba.se.internal.core.ClientSubcontract;
import com.sun.corba.se.internal.core.DuplicateServiceContext;
import com.sun.corba.se.internal.core.GIOPVersion;
import com.sun.corba.se.internal.core.IOR;
import com.sun.corba.se.internal.core.NoSuchServiceContext;
//import com.sun.corba.se.internal.core.ServiceContext;
import com.sun.corba.se.internal.core.ServiceContexts;
import com.sun.corba.se.internal.core.UnknownServiceContext;
import com.sun.corba.se.internal.iiop.CDRInputStream_1_0;
import com.sun.corba.se.internal.corba.EncapsOutputStream;
import com.sun.corba.se.internal.iiop.Connection;

import com.sun.corba.se.internal.corba.CORBAObjectImpl;

import com.sun.corba.se.internal.ior.IIOPProfile;
import com.sun.corba.se.internal.ior.IIOPProfileTemplate;
import com.sun.corba.se.internal.ior.ObjectKeyTemplate ; 

import com.sun.corba.se.internal.orbutil.ORBUtility;
import com.sun.corba.se.internal.orbutil.ORBClassLoader;
import com.sun.corba.se.internal.util.RepositoryId;
import org.omg.PortableInterceptor.ForwardRequest;
import org.omg.PortableInterceptor.InvalidSlot;
import org.omg.PortableInterceptor.RequestInfo;
import org.omg.PortableInterceptor.LOCATION_FORWARD;
import org.omg.IOP.TaggedProfile;
import org.omg.IOP.TaggedComponent;
//import org.omg.IOP.ServiceContext;
import org.omg.IOP.ServiceContextHelper;
import org.omg.Messaging.SYNC_WITH_TRANSPORT;
import org.omg.CORBA.ParameterMode;

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.INTERNAL;
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.Policy;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.UNKNOWN;
import org.omg.CORBA.UserException;
import org.omg.CORBA.portable.ApplicationException;
import org.omg.CORBA.portable.Delegate;
import org.omg.CORBA.portable.InputStream;
import org.omg.CORBA.portable.ObjectImpl;

import java.io.*;
import java.lang.reflect.*;
import java.util.*;

/**
 * Implementation of the RequestInfo interface as specified in
 * orbos/99-12-02 section 5.4.1.
 */
public abstract class RequestInfoImpl 
    extends LocalObject 
    implements RequestInfo, RequestInfoExt
{
    //////////////////////////////////////////////////////////////////////
    //
    // NOTE: IF AN ATTRIBUTE IS ADDED, PLEASE UPDATE RESET();
    //
    //////////////////////////////////////////////////////////////////////

    // The ORB from which to get PICurrent and other info
    protected PIORB piOrb;
    
    // The number of interceptors actually invoked for this client request.
    // See setFlowStackIndex for a detailed description.
    protected int flowStackIndex = 0;

    // The type of starting point call to make to the interceptors
    // See ClientRequestInfoImpl and ServerRequestInfoImpl for a list of 
    // appropriate constants.
    protected int startingPointCall;
    
    // The type of intermediate point call to make to the interceptors
    // See ServerRequestInfoImpl for a list of appropriate constants.
    // This does not currently apply to client request interceptors but is
    // here in case intermediate points are introduced in the future.
    protected int intermediatePointCall;
    
    // The type of ending point call to make to the interceptors
    // See ClientRequestInfoImpl and ServerRequestInfoImpl for a list of 
    // appropriate constants.
    protected int endingPointCall;
    
    // The reply status to return in reply_status.  This is initialized
    // to UNINITIALIZED so that we can tell if this has been set or not.
    protected short replyStatus = UNINITIALIZED;
    
    // Constant for an uninitizlied reply status.
    protected static final short UNINITIALIZED = -1;
    
    // Which points we are currently executing (so we can implement the
    // validity table).  
    protected int currentExecutionPoint;
    protected static final int EXECUTION_POINT_STARTING = 0;
    protected static final int EXECUTION_POINT_INTERMEDIATE = 1;
    protected static final int EXECUTION_POINT_ENDING = 2;
    
    // Set to true if all interceptors have had all their points
    // executed.
    protected boolean alreadyExecuted;
    
    // Sources of request information
    protected Connection     connection;
    protected ServiceContexts serviceContexts;
    
    // The ForwardRequest object if this request is being forwarded.
    // Either the forwardRequest or the forwardRequestIOR field is set.
    // When set, the other field is set to null initially.  If the other
    // field is queried, it is lazily calculated and cached.  These
    // two attributes are always kept in sync.
    protected ForwardRequest forwardRequest;
    protected IOR forwardRequestIOR;

    // PICurrent's  SlotTable
    protected SlotTable slotTable;

    // The exception to be returned by received_exception and 
    // received_exception_id
    protected Exception exception;
    
    //////////////////////////////////////////////////////////////////////
    //
    // 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() {

	// Please keep these in the same order as declared above.

        flowStackIndex = 0;
        startingPointCall = 0;
        intermediatePointCall = 0;
        endingPointCall = 0;
        replyStatus = UNINITIALIZED;
        currentExecutionPoint = EXECUTION_POINT_STARTING;
        alreadyExecuted = false;
	connection = null;
        serviceContexts = null;
        forwardRequest = null;
	forwardRequestIOR = null;
        exception = null;

        // We don't need to reset the Slots because they are
        // already in the clean state after recieve_<point> interceptor
        // are called. 
    }

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

    // Method IDs for all methods in RequestInfo.  This allows for a 
    // convenient O(1) lookup for checkAccess().
    protected static final int MID_REQUEST_ID                   =  0;
    protected static final int MID_OPERATION                    =  1;
    protected static final int MID_ARGUMENTS                    =  2;
    protected static final int MID_EXCEPTIONS                   =  3;
    protected static final int MID_CONTEXTS                     =  4;
    protected static final int MID_OPERATION_CONTEXT            =  5;
    protected static final int MID_RESULT                       =  6;
    protected static final int MID_RESPONSE_EXPECTED            =  7;
    protected static final int MID_SYNC_SCOPE                   =  8;
    protected static final int MID_REPLY_STATUS                 =  9;
    protected static final int MID_FORWARD_REFERENCE            = 10;
    protected static final int MID_GET_SLOT                     = 11;
    protected static final int MID_GET_REQUEST_SERVICE_CONTEXT  = 12;
    protected static final int MID_GET_REPLY_SERVICE_CONTEXT    = 13;
    // The last value from RequestInfo (be sure to update this):
    protected static final int MID_RI_LAST                      = 13;
    
    /*
     **********************************************************************
     * Public interfaces
     **********************************************************************/
         
    /**
     * Creates a new RequestInfoImpl object.
     */
    public RequestInfoImpl( PIORB piOrb ) { 
        super();
        
        this.piOrb = piOrb;
        // Capture the current TSC and make it the RSC of this request.
        PICurrent current = piOrb.getPICurrent();
        slotTable = current.getSlotTable( );
    }


    /**
     * Implementation for request_id() differs for client and server 
     * implementations.
     *
     * Uniquely identifies an active request/reply sequence.  Once a
     * request/reply sequence is concluded this ID may be reused.  (this
     * is NOT necessarily the same as the GIOP request_id).
     */
    abstract public int request_id ();

    /**
     * Implementation for operation() differs for client and server
     * implementations.
     *
     * The name of the operation being invoked.
     */
    abstract public String operation ();


    /**
     * This method returns the list of arguments for the operation that was
     * invoked. It raises NO_RESOURCES exception if the operation is not invoked
     * by using DII mechanism.
     */
    abstract public Parameter[] arguments ();
    
    /**
     * This method returns the list of exceptios  that was raised when the
     * operation was invoked. It raises NO_RESOURCES exception if the operation
     * is not invoked by using DII mechanism.
     */
    abstract public TypeCode[] exceptions ();
    
    /**
     * This method returns the list of contexts for the DII operation. 
     * It raises NO_RESOURCES exception if the operation is not invoked by 
     * using DII mechanism.
     */
    abstract public String[] contexts ();
    
    /**
     * This method returns the list of operation_context for the DII operation. 
     * It raises NO_RESOURCES exception if the operation is not invoked by 
     * using DII mechanism.
     */
    abstract public String[] operation_context ();

    /**
     * This method returns the result from the invoked DII operation.
     * It raises NO_RESOURCES exception if the operation is not invoked by 
     * using DII mechanism.
     */
    abstract public Any result ();
    
    /**
     * Implementation for response_expected() differs for client and server
     * implementations.
     *
     * Indicates whether a response is expected.  On the client, a reply is
     * not returned when response_expected is false, so receive_reply cannot
     * be called.  receive_other is called unless an exception occurs, in
     * which case receive_exception is called.  On the client, within
     * send_poll, this attribute is true.
     */
    abstract public boolean response_expected ();
    
    /**
     * Defined in the Messaging specification.  Pertinent only when 
     * response_expected is false.  If response_expected is true, the value 
     * of sync_scope is undefined.  It defines how far the request shall 
     * progress before control is returned to the client.  This attribute may 
     * have one of the follwing values:
     * <ul>
     *   <li>Messaging::SYNC_NONE</li>
     *   <li>Messaging::SYNC_WITH_TRANSPORT</li>
     *   <li>Messaging::SYNC_WITH_SERVER</li>
     *   <li>Messaging::SYNC_WITH_TARGET</li>
     * </ul>
     */
    public short sync_scope (){
        checkAccess( MID_SYNC_SCOPE );
        return SYNC_WITH_TRANSPORT.value;
    }
    
    /**
     * Describes the state of the result of the operation invocation.  Its 
     * value can be one of the following:
     * <ul>
     *   <li>PortableInterceptor::SUCCESSFUL</li>
     *   <li>PortableInterceptor::SYSTEM_EXCEPTION</li>
     *   <li>PortableInterceptor::USER_EXCEPTION</li>
     *   <li>PortableInterceptor::LOCATION_FORWARD</li>
     *   <li>PortableInterceptor::TRANSPORT_RETRY</li>
     * </ul>
     */
    public short reply_status (){
        checkAccess( MID_REPLY_STATUS );
        return replyStatus;
    }
    
    /**
     * Implementation for forward_reference() differs for client and server
     * implementations.
     *
     * If the reply_status attribute is LOCATION_FORWARD
     * then this attribute will contain the object
     * to which the request will be forwarded.  It is indeterminate whether a
     * forwarded request will actually occur.
     */
    abstract public Object forward_reference ();

    
    /**
     * Returns the data from the given slot of the PortableInterceptor::Current
     * that is in the scope of the request.
     * <p>
     * If the given slot has not been set, then an any containing a type code 
     * with a TCKind value of tk_null is returned.
     * <p>
     * If the ID does not define an allocated slot, InvalidSlot is raised.
     */
    public Any get_slot (int id) 
        throws InvalidSlot
    {
	// access is currently valid for all states:
        //checkAccess( MID_GET_SLOT );
        // Delegate the call to the slotTable which was set when RequestInfo was
        // created.
        return slotTable.get_slot( id );
    }

    /**
     * Implementation for get_request_service_context() differs for client 
     * and server implementations.
     *
     * This operation returns a copy of the service context with the given ID
     * that is associated with the request.  If the request's service context
     * does not contain an etry for that ID, BAD_PARAM with a minor code of
     * TBD_BP is raised.
     */
    abstract public org.omg.IOP.ServiceContext 
        get_request_service_context(int id);

    /**
     * Implementation for get_reply_service_context() differs for client 
     * and server implementations.
     *
     * This operation returns a copy of the service context with the given ID
     * that is associated with the reply.  IF the request's service context
     * does not contain an entry for that ID, BAD_PARAM with a minor code of
     * TBD_BP is raised.
     */
    abstract public org.omg.IOP.ServiceContext
        get_reply_service_context (int id);

    
    // 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
    //    in both ClientRequestInfoImpl and ServerRequestInfoImpl.


    
    /*
     **********************************************************************
     * Proprietary methods
     **********************************************************************/

    /**
     * @return The connection on which the request is made.
     *
     * Note: we store the connection as an internal type but
     * expose it here as an external type.
     */
    public com.sun.corba.se.connection.Connection connection()
    {
	return connection;
    }

    /*
     **********************************************************************
     * Private utility methods
     **********************************************************************/

    /**
     * Inserts the UserException inside the given ApplicationException
     * into the given Any.  Throws an UNKNOWN with minor code
     * MinorCodes.UNKNOWN_USER_EXCEPTION if the Helper class could not be 
     * found to insert it with.
     */
    private void insertApplicationException( ApplicationException appException,
                                             Any result )
        throws UNKNOWN
    {
        boolean success = false;

        try {
            // Extract the UserException from the ApplicationException.
            // Look up class name from repository id:
            RepositoryId repId = RepositoryId.cache.getId(
                appException.getId() );
            String className = repId.getClassName();
           
            // Find the read method on the helper class:
            String helperClassName = className + "Helper";
            Class helperClass = ORBClassLoader.loadClass( helperClassName );
            Class[] readParams = new Class[1];
            readParams[0] = org.omg.CORBA.portable.InputStream.class;
            Method readMethod = helperClass.getMethod( "read", readParams );
           
            // Invoke the read method, passing in the input stream to
            // retrieve the user exception.  Mark and reset the stream
            // as to not disturb it.
            InputStream ueInputStream = appException.getInputStream();
            ueInputStream.mark( 0 );
            UserException userException = null;
            try {
                java.lang.Object[] readArguments = new java.lang.Object[1];
                readArguments[0] = ueInputStream;
                userException = (UserException)readMethod.invoke(
                    null, readArguments );
            }
            finally {
                try {
                    ueInputStream.reset();
                }
                catch( IOException e ) {
                    // What can we do here?  We are unsure if the
                    // stream is reset or not!
		    throw new INTERNAL( 
			"Reset failed on Application Exception.", 
			MinorCodes.MARK_AND_RESET_FAILED,
                        CompletionStatus.COMPLETED_NO );
                }
            }

            // Insert this UserException into the provided Any using the
            // helper class.
	    insertUserException( userException, result );

	    // If insertUserException did not throw UNKNOWN, success!
	    success = true;
        }
        catch( ClassNotFoundException e ) {
            success = false;
        }
        catch( NoSuchMethodException e ) {
            success = false;
        }
        catch( SecurityException e ) {
            success = false;
        }
        catch( IllegalAccessException e ) {
            success = false;
        }
        catch( IllegalArgumentException e ) {
            success = false;
        }
        catch( InvocationTargetException e ) {
            success = false;
        }

        if( !success ) {
            // We couldn't insert the UserException into an Any.
            throw new UNKNOWN( "Could not insert UserException into Any",
                MinorCodes.UNKNOWN_USER_EXCEPTION, 
		CompletionStatus.COMPLETED_MAYBE );
        }
    }

    /**
     * Inserts the UserException into the given Any.  
     * Throws an UNKNOWN with minor code
     * MinorCodes.UNKNOWN_USER_EXCEPTION if the Helper class could not be 
     * found to insert it with.
     */
    private void insertUserException( UserException userException, Any result )
        throws UNKNOWN
    {
        boolean success = false;

        try {
            // Insert this UserException into the provided Any using the
            // helper class.
            if( userException != null ) {
		Class exceptionClass = userException.getClass();
		String className = exceptionClass.getName();
		String helperClassName = className + "Helper";
		Class helperClass = ORBClassLoader.loadClass( helperClassName );
           
                // Find insert( Any, class ) method
                Class[] insertMethodParams = new Class[2];
                insertMethodParams[0] = org.omg.CORBA.Any.class;
                insertMethodParams[1] = exceptionClass;
                Method insertMethod = helperClass.getMethod(
                    "insert", insertMethodParams );

                // Call helper.insert( result, userException ):
                java.lang.Object[] insertMethodArguments =
                    new java.lang.Object[2];
                insertMethodArguments[0] = result;
                insertMethodArguments[1] = userException;
                insertMethod.invoke( null, insertMethodArguments );

                // If we got this far without an exception, it is a success:
                success = true;
            }
        }
        catch( ClassNotFoundException e ) {
            success = false;
        }
        catch( NoSuchMethodException e ) {
            success = false;
        }
        catch( SecurityException e ) {
            success = false;
        }
        catch( IllegalAccessException e ) {
            success = false;
        }
        catch( IllegalArgumentException e ) {
            success = false;
        }
        catch( InvocationTargetException e ) {
            success = false;
        }

        if( !success ) {
            // We couldn't insert the UserException into an Any.
            throw new UNKNOWN( "Could not insert UserException into Any",
                MinorCodes.UNKNOWN_USER_EXCEPTION, 
		CompletionStatus.COMPLETED_MAYBE );
        }
    }

    /*
     **********************************************************************
     * Protected utility methods
     **********************************************************************/

    /**
     * Internal utility method to convert an NVList into a PI Parameter[]
     */
    protected Parameter[] nvListToParameterArray( NVList parNVList ) {

	// _REVISIT_ This utility method should probably be doing a deep
	// copy so interceptor can't accidentally change the arguments.

        int count = parNVList.count();
        Parameter[] plist = new Parameter[count];
        try {
            for( int i = 0; i < count; i++ ) {
                Parameter p = new Parameter();
                plist[i] = p;
                NamedValue nv = parNVList.item( i );
                plist[i].argument = nv.value();
                // ParameterMode spec can be found in 99-10-07.pdf
                // Section:10.5.22
                // nv.flags spec can be found in 99-10-07.pdf
                // Section 7.1.1
                // nv.flags has ARG_IN as 1, ARG_OUT as 2 and ARG_INOUT as 3
                // To convert this into enum PARAM_IN, PARAM_OUT and
                // PARAM_INOUT the value is subtracted by 1.
                plist[i].mode = ParameterMode.from_int( nv.flags() - 1 );
            }
        }
        catch ( Exception e ) {
            throw new INTERNAL( "Exception in Requestnfo.arguments()",
                MinorCodes.EXCEPTION_IN_ARGUMENTS,
                CompletionStatus.COMPLETED_NO );
        }
        return plist;
    }

    /**
     * Utility to wrap the given Exception in an Any object and return it.
     * If the exception is a UserException which cannot be inserted into 
     * an any, then this returns an Any containing the system exception 
     * UNKNOWN.
     */
    protected Any exceptionToAny( Exception exception ){
        Any result = piOrb.create_any();

        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 new INTERNAL( "Exception was null in received_exception",
                MinorCodes.EXCEPTION_WAS_NULL_2, 
		CompletionStatus.COMPLETED_NO );
        }
        else if( exception instanceof SystemException ) {
            ORBUtility.insertSystemException(
                (SystemException)exception, result );
        }
        else if( exception instanceof ApplicationException ) {
            // Use the Helper class for this exception to insert it into an
            // Any.
            try {
                // Insert the user exception inside the application exception
                // into the Any result:
                ApplicationException appException =
                    (ApplicationException)exception;
                insertApplicationException( appException, result );
            }
            catch( UNKNOWN e ) {
                // As per ptc/00-08-06, 21.3.13.4. if we cannot find the
                // appropriate class, then return an any containing UNKNOWN,
                // with a minor code of 1.  This is conveniently the same
                // exception that is returned from the 
		// insertApplicationException utility method.
                ORBUtility.insertSystemException( e, result );
            }
        }
	else if( exception instanceof UserException ) {
	    try {
		UserException userException = (UserException)exception;
		insertUserException( userException, result );
	    }
	    catch( UNKNOWN e ) {
                ORBUtility.insertSystemException( e, result );
	    }
	}


        return result;
    }

    /**
     * Utility method to look up a service context with the given id and
     * convert it to an IOP.ServiceContext.  Uses the given HashMap as
     * a cache.  If not found in cache, the result is inserted in the cache.
     */
    protected org.omg.IOP.ServiceContext 
        getServiceContext ( HashMap cachedServiceContexts, 
			    ServiceContexts serviceContexts, int id )
    {
        org.omg.IOP.ServiceContext result = null;
        Integer integerId = new Integer( id );

	// Search cache first:
        result = (org.omg.IOP.ServiceContext)
	    cachedServiceContexts.get( integerId );

	// null could normally mean that either we cached the value null 
	// or it's not in the cache.  However, there is no way for us to 
	// cache the value null in the following code.
        if( result == null ) {
	    // Not in cache.  Find it and put in cache.
	    try {
		// Get the desired "core" service context.
		com.sun.corba.se.internal.core.ServiceContext context = 
		    serviceContexts.get( id );

		// Convert the "core" service context to an 
		// "IOP" ServiceContext by writing it to a 
		// CDROutputStream and reading it back.
		EncapsOutputStream out = new EncapsOutputStream(piOrb);

		context.write( out, GIOPVersion.V1_2 );
		InputStream inputStream = out.create_input_stream();
		result = ServiceContextHelper.read( inputStream );
	    }
	    catch( NoSuchServiceContext e ) {
		throw new BAD_PARAM( "No such service context", 
				     MinorCodes.INVALID_SERVICE_CONTEXT_ID,
				     CompletionStatus.COMPLETED_NO );
	    }

	    cachedServiceContexts.put( integerId, result );
	}

	// Good citizen: For increased efficiency, we assume that interceptors
	// will not modify the returned ServiceContext.  Otherwise, we would
	// have to make a deep copy.

        return result;
    }


    /**
     * Utility method to add an IOP.ServiceContext to a core.ServiceContexts
     * object.  If replace is true, any service context with the given id
     * is replaced.  
     * <p>
     * Raises BAD_INV_ORDER if replace is false and a service context with
     * the given id already exists.
     * <p>
     * Uses the given HashMap as a cache.  If a service context is placed
     * in the container, it goes in the HashMap as well.  
     */
    protected void addServiceContext( 
	HashMap cachedServiceContexts, 
	ServiceContexts serviceContexts, 
	org.omg.IOP.ServiceContext service_context,
	boolean replace )
    {
        try {
            // Convert IOP.service_context to core.ServiceContext:
            EncapsOutputStream outputStream = new EncapsOutputStream(
                piOrb );
            InputStream inputStream = null;
            UnknownServiceContext coreServiceContext = null;
            ServiceContextHelper.write( outputStream, service_context );
            inputStream = outputStream.create_input_stream();

            // Constructor expects id to already have been read from stream.
            coreServiceContext = new UnknownServiceContext(
                inputStream.read_long(),
                (org.omg.CORBA_2_3.portable.InputStream)inputStream );

	    int id = coreServiceContext.getId();

            try {
                if( replace ) {
                    serviceContexts.delete( id );
                }
            }
            catch( NoSuchServiceContext e ) {
                // Ignore it.
            }

            serviceContexts.put( coreServiceContext );

	    // Place IOP.ServiceContext in cache as well:
	    cachedServiceContexts.put( new Integer( id ), service_context );
        }
        catch( DuplicateServiceContext e ) {
            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 );
        }
    }

    /**
     * Sets the number of interceptors whose starting interception
     * points were successfully invoked on this client call.  As specified
     * in orbos/99-12-02, section 5.2.1., not all interceptors will
     * be invoked if a ForwardRequest exception or a system exception
     * is raised.  This keeps track of how many were successfully executed
     * so we know not to execute the corresponding ending interception
     * points for the interceptors whose starting interception points
     * were not completed.  This simulates the "Flow Stack Visual Model"
     * presented in section 5.1.3.*/
    protected void setFlowStackIndex(int num ) {
        this.flowStackIndex = num;
    }

    /**
     * Returns the number of interceptors whose starting interception
     * points were actually invoked on this client request.  See
     * setFlowStackIndex for more details.
     */
    protected int getFlowStackIndex() {
        return this.flowStackIndex;
    }

    /**
     * Sets which ending interception point should be called
     * for each interceptor in the virtual flow stack.
     */
    protected void setEndingPointCall( int call ) {
        this.endingPointCall = call;
    }

    /**
     * Retrieves the current ending point call type (see
     * setEndingPointCall for more details).
     */
    protected int getEndingPointCall() {
        return this.endingPointCall;
    }

    /**
     * Sets which intermediate interception point should be called
     * for each interceptor in the virtual flow stack.
     */
    protected void setIntermediatePointCall( int call ) {
        this.intermediatePointCall = call;
    }

    /**
     * Retrieves the current intermediate point call type (see
     * setEndingPointCall for more details).
     */
    protected int getIntermediatePointCall() {
        return this.intermediatePointCall;
    }
    
    /**
     * Sets which starting interception point should be called
     * for each interceptor in the virtual flow stack.
     */
    protected void setStartingPointCall( int call ) {
        this.startingPointCall = call;
    }

    /**
     * Retrieves the current starting point call type (see
     * setStartingPointCall for more details).
     */
    protected int getStartingPointCall() {
        return this.startingPointCall;
    }
    
    /**
     * Returns true if all interceptors' starting and ending points
     * have already executed to completion, or false if not yet.
     */
    protected boolean getAlreadyExecuted() {
        return this.alreadyExecuted;
    }
    
    /**
     * Sets whether all interceotrs' starting and ending points
     * have already been executed to completion.
     */
    protected void setAlreadyExecuted( boolean alreadyExecuted ) {
        this.alreadyExecuted = alreadyExecuted;
    }
    
    /**
     * Sets the value to be returned by reply_status
     */
    protected void setReplyStatus( short replyStatus ) {
        this.replyStatus = replyStatus;
    }
    
    /**
     * Gets the current reply_status without doing an access check
     * (available only to package and subclasses)
     */
    protected short getReplyStatus() {
        return this.replyStatus;
    }
    
    /**
     * Stores the given ForwardRequest object for later analysis.
     * This version supplements setForwardRequest( IOR );
     */
    protected void setForwardRequest( ForwardRequest forwardRequest ) {
        this.forwardRequest = forwardRequest;
	this.forwardRequestIOR = null;
    }

    /** 
     * Stores the given IOR for later forward request analysis.
     * This version supplements setForwardRequest( ForwardRequest );
     */
    protected void setForwardRequest( IOR ior ) {
	this.forwardRequestIOR = ior;
	this.forwardRequest = null;
    }
    
    /**
     * Retrieves the ForwardRequest object as a ForwardRequest exception.
     */
    protected ForwardRequest getForwardRequestException() {
	if( this.forwardRequest == null ) {
	    if( this.forwardRequestIOR != null ) {
		// Convert the internal IOR to a forward request exception
		// by creating an object reference.
		org.omg.CORBA.Object obj = iorToObject(this.forwardRequestIOR);
		this.forwardRequest = new ForwardRequest( obj );
	    }
	}

        return this.forwardRequest;
    }

    /**
     * Retrieves the IOR of the ForwardRequest exception.
     */
    protected IOR getForwardRequestIOR() {
	if( this.forwardRequestIOR == null ) {
	    if( this.forwardRequest != null ) {
		// Convert object we are forwarding to into an ior:
		Delegate delegate = 
		    ((ObjectImpl)forwardRequest.forward)._get_delegate();
                ClientSubcontract csub = (ClientSubcontract)delegate;
		this.forwardRequestIOR = csub.marshal();
	    }
	}

	return this.forwardRequestIOR;
    }
    
    /**
     * Sets the exception to be returned by received_exception and
     * received_exception_id.
     */
    protected void setException( Exception exception ) {
        this.exception = exception;
    }
    
    /**
     * Returns the exception to be returned by received_exception and
     * received_exception_id.
     */
    Exception getException() {
        return this.exception;
    }
   
    /**
     * Sets the execution point that we are currently executing
     * (starting points, intermediate points, or ending points).
     * This allows us to enforce the validity table.
     */
    protected void setCurrentExecutionPoint( int executionPoint ) {
        this.currentExecutionPoint = executionPoint;
    }

    /**
     * Check whether the caller is allowed to access this method at
     * this particular time.  This is overridden in subclasses to implement
     * the validity table specified in ptc/00-04-05, table 21-1 and 21-2.
     * The currentExecutionPoint attribute is checked, and if access is
     * forbidden at this time, BAD_INV_ORDER is raised with a minor code of
     * TBD_BIO.
     *
     * @param methodID The ID of this method, one of the MID_* constants.
     *     This allows us to easily look up the method access in a table.
     *     Note that method ids may overlap between subclasses.
     */
    protected abstract void checkAccess( int methodID ) 
        throws BAD_INV_ORDER;

    /**
     * The server side does an explicit set rather than taking the
     * current PICurrent table as is done in the general RequestInfoImpl
     * constructor.
     */
    void setSlotTable(SlotTable slotTable)
    {
	this.slotTable = slotTable;
    }

    protected org.omg.CORBA.Object iorToObject( IOR ior )
    {
	// Create the appropriate client subcontract.

	IIOPProfile iiopProfile = ior.getProfile();
	IIOPProfileTemplate iiopProfileTemplate = iiopProfile.getTemplate();
	ObjectKeyTemplate objectKeyTemplate =
	    iiopProfileTemplate.getObjectKeyTemplate();
	ClientSubcontract clientSubcontract =
	    piOrb.getSubcontractRegistry().getClientSubcontract(objectKeyTemplate);

	// Initialize and return it.
	
	clientSubcontract.setOrb(piOrb);
	clientSubcontract.unmarshal(ior);
	ObjectImpl objectImpl = new CORBAObjectImpl();
	objectImpl._set_delegate((Delegate)clientSubcontract);
	return objectImpl;
    }
    
}
