/*
 * @(#)RequestImpl.java	1.90 04/04/07
 *
 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
/*
 * Licensed Materials - Property of IBM
 * RMI-IIOP v1.0
 * Copyright IBM Corp. 1998 1999  All Rights Reserved
 *
 * US Government Users Restricted Rights - Use, duplication or
 * disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
 */

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


import org.omg.CORBA.Any;
import org.omg.CORBA.ARG_IN;
import org.omg.CORBA.ARG_OUT;
import org.omg.CORBA.ARG_INOUT;
import org.omg.CORBA.Context;
import org.omg.CORBA.ContextList;
import org.omg.CORBA.Environment;
import org.omg.CORBA.ExceptionList;
import org.omg.CORBA.NVList;
import org.omg.CORBA.NamedValue;
import org.omg.CORBA.Request;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.TCKind;
import org.omg.CORBA.TypeCode;
import org.omg.CORBA.TypeCodePackage.BadKind;
import org.omg.CORBA.UnknownUserException;
import org.omg.CORBA.Bounds;
import org.omg.CORBA.UNKNOWN;
import org.omg.CORBA.INTERNAL;
import org.omg.CORBA.NO_IMPLEMENT;
import org.omg.CORBA.CompletionStatus;
import org.omg.CORBA.WrongTransaction;

import org.omg.CORBA.portable.ApplicationException ;
import org.omg.CORBA.portable.RemarshalException ;
import org.omg.CORBA.portable.InputStream ;
import org.omg.CORBA.portable.OutputStream ;

import com.sun.corba.se.spi.orb.ORB;
import com.sun.corba.se.spi.presentation.rmi.StubAdapter;
import com.sun.corba.se.spi.logging.CORBALogDomains;
import com.sun.corba.se.impl.logging.ORBUtilSystemException;
import com.sun.corba.se.impl.corba.AsynchInvoke;

public class RequestImpl 
    extends Request
{
    ///////////////////////////////////////////////////////////////////////////
    // data members

    protected org.omg.CORBA.Object _target;
    protected String    	 _opName;
    protected NVList 		 _arguments;
    protected ExceptionList	 _exceptions;
    private NamedValue		 _result;
    protected Environment	 _env;
    private Context		 _ctx;
    private ContextList		 _ctxList;
    protected ORB		 _orb;
    private ORBUtilSystemException _wrapper;

    // invocation-specific stuff
    protected boolean 		 _isOneWay	= false;
    private int[]		 _paramCodes;
    private long[]		 _paramLongs;
    private java.lang.Object[] 	 _paramObjects;

    // support for deferred invocations. 
    // protected instead of private since it needs to be set by the
    // thread object doing the asynchronous invocation.
    protected boolean 		 gotResponse 	= false;

    ///////////////////////////////////////////////////////////////////////////
    // constructor

    // REVISIT - used to be protected.  Now public so it can be
    // accessed from xgiop.
    public RequestImpl (ORB orb,
			org.omg.CORBA.Object targetObject,
			Context ctx,
			String operationName,
			NVList argumentList,
			NamedValue resultContainer,
			ExceptionList exceptionList,
			ContextList ctxList)
    {

        // initialize the orb
        _orb 	= orb;
	_wrapper = ORBUtilSystemException.get( orb,
	    CORBALogDomains.OA_INVOCATION ) ;

        // initialize target, context and operation name
        _target     = targetObject;
        _ctx	= ctx;
        _opName	= operationName;

        // initialize argument list if not passed in
        if (argumentList == null)
            _arguments = new NVListImpl(_orb);
        else
            _arguments = argumentList;

        // set result container. 
        _result = resultContainer;

        // initialize exception list if not passed in
        if (exceptionList == null)
            _exceptions = new ExceptionListImpl();
        else
            _exceptions = exceptionList;

        // initialize context list if not passed in
        if (ctxList == null)
            _ctxList = new ContextListImpl(_orb);
        else
            _ctxList = ctxList;

        // initialize environment 
        _env	= new EnvironmentImpl();

    }

    public org.omg.CORBA.Object target()
    {
        return _target;
    }

    public String operation() 
    {
        return _opName;
    }

    public NVList arguments() 
    {
        return _arguments;
    }
    
    public NamedValue result() 
    {
        return _result;
    }
    
    public Environment env() 
    {
        return _env;
    }
    
    public ExceptionList exceptions() 
    {
        return _exceptions;
    }
    
    public ContextList contexts() 
    {
        return _ctxList;
    }
    
    public synchronized Context ctx() 
    {
        if (_ctx == null)
            _ctx = new ContextImpl(_orb);
        return _ctx;
    }
    
    public synchronized void ctx(Context newCtx) 
    {
        _ctx = newCtx;
    }

    public synchronized Any add_in_arg()
    {
        return _arguments.add(org.omg.CORBA.ARG_IN.value).value();
    }

    public synchronized Any add_named_in_arg(String name)
    {
        return _arguments.add_item(name, org.omg.CORBA.ARG_IN.value).value();
    }

    public synchronized Any add_inout_arg()
    {
        return _arguments.add(org.omg.CORBA.ARG_INOUT.value).value();
    }

    public synchronized Any add_named_inout_arg(String name)
    {
        return _arguments.add_item(name, org.omg.CORBA.ARG_INOUT.value).value();
    }

    public synchronized Any add_out_arg()
    {
        return _arguments.add(org.omg.CORBA.ARG_OUT.value).value();
    }

    public synchronized Any add_named_out_arg(String name)
    {
        return _arguments.add_item(name, org.omg.CORBA.ARG_OUT.value).value();
    }

    public synchronized void set_return_type(TypeCode tc)
    {
        if (_result == null)
            _result = new NamedValueImpl(_orb);
        _result.value().type(tc);
    }

    public synchronized Any return_value()
    {
        if (_result == null)
            _result = new NamedValueImpl(_orb);
        return _result.value();
    }

    public synchronized void add_exception(TypeCode exceptionType)
    {
        _exceptions.add(exceptionType);
    }
    
    public synchronized void invoke()
    {
        doInvocation();
    }

    public synchronized void send_oneway()
    {
        _isOneWay = true;
        doInvocation();
    }
    
    public synchronized void send_deferred()
    {
        AsynchInvoke invokeObject = new AsynchInvoke(_orb, this, false);
        new Thread(invokeObject).start();
    }
    
    public synchronized boolean poll_response()
    {
        // this method has to be synchronized even though it seems
        // "readonly" since the thread object doing the asynchronous
        // invocation can potentially update this variable in parallel.
        // updates are currently simply synchronized againt the request
        // object. 
        return gotResponse;
    }
    
    public synchronized void get_response()
        throws org.omg.CORBA.WrongTransaction
    {
        while (gotResponse == false) {
            // release the lock. wait to be notified by the thread that is
            // doing the asynchronous invocation.
            try {
	        wait();
            } 
	    catch (InterruptedException e) {}
        }
    }
    
    ///////////////////////////////////////////////////////////////////////////
    // private helper methods

    /*
     * The doInvocation operation is where the real mechanics of
     * performing the request invocation is done.
     */
    protected void doInvocation()
    {
        org.omg.CORBA.portable.Delegate delegate = StubAdapter.getDelegate( 
	    _target ) ;

	// Initiate Client Portable Interceptors.  Inform the PIHandler that 
        // this is a DII request so that it knows to ignore the second 
        // inevitable call to initiateClientPIRequest in createRequest. 
        // Also, save the RequestImpl object for later use. 
	_orb.getPIHandler().initiateClientPIRequest( true );
	_orb.getPIHandler().setClientPIInfo( this );

	InputStream $in = null;
	try {
	    OutputStream $out = delegate.request(null, _opName, !_isOneWay);
	    // Marshal args
	    try {
		for (int i=0; i<_arguments.count() ; i++) {
		    NamedValue nv = _arguments.item(i);
		    switch (nv.flags()) {
		    case ARG_IN.value:
			nv.value().write_value($out);
			break;
		    case ARG_OUT.value:
			break;
		    case ARG_INOUT.value:
			nv.value().write_value($out);
			break;
	            }
		}
	    } catch ( org.omg.CORBA.Bounds ex ) {
		throw _wrapper.boundsErrorInDiiRequest( ex ) ;
	    }

	    $in = delegate.invoke(null, $out);
	} catch (ApplicationException e) {
	    // REVISIT - minor code.
	    // This is already handled in subcontract.
	    // REVISIT - uncomment.
	    //throw new INTERNAL();
	} catch (RemarshalException e) {
	    doInvocation();
	} catch( SystemException ex ) {
	    _env.exception(ex);
	    // NOTE: The exception should not be thrown.
	    // However, JDK 1.4 and earlier threw the exception,
	    // so we keep the behavior to be compatible.
	    throw ex;
	} finally {
	    delegate.releaseReply(null, $in);
        }
    }

    // REVISIT -  make protected after development - so xgiop can get it.
    public void unmarshalReply(InputStream is)
    {
        // First unmarshal the return value if it is not void
        if ( _result != null ) {
            Any returnAny = _result.value();
            TypeCode returnType = returnAny.type();
            if ( returnType.kind().value() != TCKind._tk_void )
                returnAny.read_value(is, returnType);
        }
        
        // Now unmarshal the out/inout args
        try {
            for ( int i=0; i<_arguments.count() ; i++) {
                NamedValue nv = _arguments.item(i);
                switch( nv.flags() ) {
		case ARG_IN.value:
		    break;
		case ARG_OUT.value:
		case ARG_INOUT.value:
		    Any any = nv.value();	
		    any.read_value(is, any.type());
		    break;
                }
            }
        } 
	catch ( org.omg.CORBA.Bounds ex ) {
	    // Cannot happen since we only iterate till _arguments.count()
        }
    }
}
