/*
 *        JacORB - a free Java ORB
 *
 *   Copyright (C) 1997-98  Gerald Brose.
 *
 *   This library is free software; you can redistribute it and/or
 *   modify it under the terms of the GNU Library General Public
 *   License as published by the Free Software Foundation; either
 *   version 2 of the License, or (at your option) any later version.
 *
 *   This library is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *   Library General Public License for more details.
 *
 *   You should have received a copy of the GNU Library General Public
 *   License along with this library; if not, write to the Free
 *   Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

package jacorb.orb.dsi;

/**
 * @author Gerald Brose, FU Berlin
 * @version $Id: ServerRequest.java,v 1.14 2000/03/30 13:55:48 noffke Exp $
 */

import java.io.*;
import java.lang.*;
import jacorb.orb.*;
import jacorb.orb.portableInterceptor.*;

public class ServerRequest 
    extends org.omg.CORBA.ServerRequest 
    implements org.omg.CORBA.portable.ResponseHandler
{
  protected jacorb.orb.giop.RequestInputStream in;
  protected jacorb.orb.giop.ReplyOutputStream out;	
  protected jacorb.orb.Connection connection;
    
  protected int status = org.omg.GIOP.ReplyStatusType._NO_EXCEPTION;
  protected byte[] oid;
  protected org.omg.CORBA.Object reference = null;
  protected String[] rest_of_name = null;

  /* is this request stream or DSI-based ? */
  protected boolean stream_based; 
  protected org.omg.CORBA.SystemException sys_ex;
  protected org.omg.PortableServer.ForwardRequest location_forward;
  protected org.omg.CORBA.Any  ex;
  protected org.omg.CORBA.Any result;
  protected jacorb.orb.NVList args;

  private jacorb.orb.ORB orb;

  private ServerRequestInfoImpl info = null;

  /** only to be called implicitly be subclasses */
  protected ServerRequest(){}

	
  public ServerRequest(jacorb.orb.ORB orb, byte[] _buf, jacorb.orb.Connection _connection )
  {
    this.orb = orb;
    in = new jacorb.orb.giop.RequestInputStream(_connection,_buf);
    connection = _connection;

    oid = jacorb.poa.util.POAUtil.extractOID( in.req_hdr.object_key);
  }

  /** if this request could not be delivered directly to the
      correct POA because the POA's adapter activator could
      not be called when the parent POA was in holding state,
      the parent will queue the request and later return it
      to the adapter layer. In order to be able to find the right
      POA when trying to deliver again, we have to remember the
      target POA's name 
  */

  public void setRemainingPOAName(String [] r_o_n)
  {
    rest_of_name = r_o_n;
  }

  public String[] remainingPOAName()
  {
    return rest_of_name;
  }
	
  public java.lang.String operation()
  {
    return in.req_hdr.operation;
  }

  public org.omg.CORBA.Any result()
  {
    if( stream_based )
      throw new RuntimeException("This ServerRequest is stream-based!");
    return result;
  }

  public org.omg.CORBA.NVList arguments()
  {
    if( stream_based )
      throw new RuntimeException("This ServerRequest is stream-based!");
    return args;
  }

  public org.omg.CORBA.Any except()
  {
    if( stream_based )
      throw new RuntimeException("This ServerRequest is stream-based!");
    return ex;
  }

  public org.omg.GIOP.ReplyStatusType status()
  {
    return org.omg.GIOP.ReplyStatusType.from_int( status );
  }

  public org.omg.CORBA.Context ctx()
  {
    return null;
  }

  public void arguments(org.omg.CORBA.NVList p)
  {
    if( stream_based )
      throw new RuntimeException("This ServerRequest is stream-based!");
    args = (jacorb.orb.NVList)p;
    // unmarshal

    if( args != null )
      {
	in.mark(0);
	for( java.util.Enumeration e = args.enumerate(); 
	     e.hasMoreElements(); )
	  {
	    org.omg.CORBA.NamedValue nv = 
	      (org.omg.CORBA.NamedValue)e.nextElement();
			
	    if( nv.flags() != org.omg.CORBA.ARG_OUT.value )
	      { 
		// out parameters are not received
		try
		  { 
		    nv.value().read_value( in, nv.value().type() );
		  } 
		catch (Exception ex)
		  {
		    throw new org.omg.CORBA.MARSHAL("Couldn't unmarshal object of type "
						    + nv.value().type() + " in ServerRequest.");
		  }
	      }
	  }
	try
	  { 
	    in.reset();
	  }
	catch (Exception ex)
	  {
	    throw new org.omg.CORBA.UNKNOWN("Could not reset input stream");
	  }
	    
	if (info != null){
	  //invoke interceptors
	  org.omg.Dynamic.Parameter[] params = new org.omg.Dynamic.Parameter[args.count()];
	  for (int i = 0; i < params.length; i++){
	    try{
	      org.omg.CORBA.NamedValue value = args.item(i);

	      org.omg.CORBA.ParameterMode mode = null;
	      if (value.flags() == org.omg.CORBA.ARG_IN.value)
		mode = org.omg.CORBA.ParameterMode.PARAM_IN;
	      else if (value.flags() == org.omg.CORBA.ARG_OUT.value)
		mode = org.omg.CORBA.ParameterMode.PARAM_OUT;
	      else if (value.flags() == org.omg.CORBA.ARG_INOUT.value)
		mode = org.omg.CORBA.ParameterMode.PARAM_INOUT;
		  
	      params[i] = new org.omg.Dynamic.Parameter(value.value(), mode);
	    }catch (Exception e){
	      jacorb.util.Debug.output(2, e);
	    }
	  }

	  info.arguments = params;

	  ServerInterceptorEnumeration intercept_enum = 
	    orb.getInterceptorManager().getServerEnumeration(true);      
	      
	  try{
	    intercept_enum.iterate(info, ServerInterceptorEnumeration.RECEIVE_REQUEST);
	  } catch(org.omg.PortableInterceptor.ForwardRequest _fwd){
	    setLocationForward(new org.omg.PortableServer.ForwardRequest(_fwd.forward));
		
	  } catch (org.omg.CORBA.SystemException _sys_ex) {
	    setSystemException(_sys_ex);
	  }
	      
	  info = null;
	}	
      }
  }

  public void set_result(org.omg.CORBA.Any res)
  {
    if( stream_based )
      throw new RuntimeException("This ServerRequest is stream-based!");
    result = res;
  }

  public void set_exception(org.omg.CORBA.Any ex)
  {
    if( stream_based )
      throw new RuntimeException("This ServerRequest is stream-based!");
    this.ex = ex;
    status = org.omg.GIOP.ReplyStatusType._USER_EXCEPTION;
	
    if (info != null){
      ServerInterceptorEnumeration intercept_enum = 
	orb.getInterceptorManager().getServerEnumeration(true);      
	  
      try{
	intercept_enum.iterate(info, ServerInterceptorEnumeration.RECEIVE_REQUEST);
      } catch(org.omg.PortableInterceptor.ForwardRequest _fwd){
	setLocationForward(new org.omg.PortableServer.ForwardRequest(_fwd.forward));
	    
      } catch (org.omg.CORBA.SystemException _sys_ex) {
	setSystemException(_sys_ex);
      }
	  
      info = null;
    }	
  }

  public void reply()
  {
    if( in.req_hdr.response_expected )
      {
	jacorb.util.Debug.output(6,"ServerRequest: reply to " + operation());
	try 
	  { 
	    if( out == null )
	      {
		out = new jacorb.orb.giop.ReplyOutputStream(connection,
							    new org.omg.IOP.ServiceContext[0],
							    requestId(), 
							    org.omg.GIOP.ReplyStatusType.from_int(status));		       
	      }

	    /* DSI-based servers set results and user exceptions using anys, so 
	       we have to treat this differently */

	    if( !stream_based )
	      {
		if( status == org.omg.GIOP.ReplyStatusType._USER_EXCEPTION )
		  {
		    out.write_string( ex.type().id() );
		    ex.write_value( out );
		  }
		else if( status == org.omg.GIOP.ReplyStatusType._NO_EXCEPTION )
		  {
		    if( args != null )
		      {
			for( java.util.Enumeration e = args.enumerate(); 
			     e.hasMoreElements(); )
			  {
			    jacorb.orb.NamedValue nv = 
			      (jacorb.orb.NamedValue)e.nextElement();
				
			    if( nv.flags() != org.omg.CORBA.ARG_IN.value )
			      { 
				// in parameters are not returnd
				try
				  { 
				    nv.send( out );
				  } 
				catch (Exception ex)
				  {
				    throw new org.omg.CORBA.MARSHAL("Couldn't return (in)out arg of type "
								    + nv.value().type() + " in ServerRequest.");
				  }
			      }
			  }
		      }
		    result.write_value( out );
		  }
	      }

	    /* these two exceptions are set in the same way for both stream-based and
	       DSI-based servers */

	    if( status == org.omg.GIOP.ReplyStatusType._LOCATION_FORWARD )
	      {
		out.write_Object( location_forward.forward_reference );
	      }
	    else if( status == org.omg.GIOP.ReplyStatusType._SYSTEM_EXCEPTION )
	      {
		jacorb.orb.SystemExceptionHelper.write( out, sys_ex );
	      }

	    /* everything is written to out by now, be it results or exceptions */

	    out.close();
	    ((ServerSideConnection)connection).sendReply( out );
		    
	  }
	catch ( Exception ioe )
	  {
	    jacorb.util.Debug.output(2,ioe);
	    System.err.println("ServerRequest: Error replying to request!");
	  }
      }
  }

  /* ResponseHandler */

  public org.omg.CORBA.portable.OutputStream createReply()
  {
    stream_based = true;
    if( out != null )
      throw new Error("Internal: reply already created!");
    if( !stream_based )
      throw new Error("Internal: ServerRequest not stream-based!");

    out = new jacorb.orb.giop.ReplyOutputStream(connection,new org.omg.IOP.ServiceContext[0],
						requestId(),
						org.omg.GIOP.ReplyStatusType.NO_EXCEPTION,
						orb.hasServerRequestInterceptors());
    return out;
  }

  public org.omg.CORBA.portable.OutputStream createExceptionReply()
  {
    stream_based = true;

    status = org.omg.GIOP.ReplyStatusType._USER_EXCEPTION;
    out = new jacorb.orb.giop.ReplyOutputStream(connection,new org.omg.IOP.ServiceContext[0],
						requestId(),
						org.omg.GIOP.ReplyStatusType.USER_EXCEPTION,
						orb.hasServerRequestInterceptors());
    return out;
  }

  /** our own: */


  public void setSystemException(org.omg.CORBA.SystemException s)
  {
    jacorb.util.Debug.output(2, s);
    status = org.omg.GIOP.ReplyStatusType._SYSTEM_EXCEPTION;

    /* we need to create a new output stream here because a system exception may
       have occurred *after* a no_exception request header was written onto the
       original output stream*/

    out = new jacorb.orb.giop.ReplyOutputStream(connection,new org.omg.IOP.ServiceContext[0],
						requestId(),
						org.omg.GIOP.ReplyStatusType.SYSTEM_EXCEPTION,
						orb.hasServerRequestInterceptors());
    sys_ex = s;
  }

  public void setLocationForward(org.omg.PortableServer.ForwardRequest r)
  {
    jacorb.util.Debug.output(2,"Location Forward");
    status = org.omg.GIOP.ReplyStatusType._LOCATION_FORWARD;
    out = new jacorb.orb.giop.ReplyOutputStream(connection,new org.omg.IOP.ServiceContext[0],
						requestId(),
						org.omg.GIOP.ReplyStatusType.LOCATION_FORWARD,
						orb.hasServerRequestInterceptors());
    location_forward = r;
  }

  /**
   * @returns the InputStream. This operation sets the
   * request be stream-based, ie. all attempts to extract
   * data using DII-based operations will throw exceptions
   * For internal access to the stream use get_in()
   *
   */

  public jacorb.orb.CDRInputStream getInputStream()
  {
    stream_based = true;
    return in;
  }
  
  public jacorb.orb.giop.ReplyOutputStream getReplyOutputStream(){
    if (out == null)
      createReply();

    return out;
  }

  public boolean responseExpected(){
    return in.req_hdr.response_expected;
  }

  public org.omg.CORBA.SystemException getSystemException(){
    return sys_ex;
  }

  public int requestId()
  {
    return in.req_hdr.request_id;
  }

  public byte[] objectKey()
  {
    return in.req_hdr.object_key;
  }

  public org.omg.IOP.ServiceContext[] getServiceContext()
  {
    return in.req_hdr.service_context;
  }

  public byte[] objectId()
  {
    return oid;
  }

  public boolean streamBased()
  {
    return stream_based;
  }


  public void setReference(org.omg.CORBA.Object o)
  {
    reference = o;
  }

  public org.omg.CORBA.Object getReference()
  {
    return reference;
  }

  public byte [] getBuffer()
  {
    return in.getBuffer();
  }

  public void updateBuffer( byte[] _buf )
  {
    jacorb.orb.giop.RequestInputStream rin = new jacorb.orb.giop.RequestInputStream(connection,_buf);
    //  	byte[] n_oid = jacorb.poa.util.POAUtil.extractOID( in.req_hdr.object_key);
    //  	if( oid != n_oid )
    //  	    throw new org.omg.CORBA.UNKNOWN("Invalid message buffer update");
    in = rin;
  }

  public jacorb.orb.giop.RequestInputStream get_in(){
    return in;
  }

  public void setServerRequestInfo(ServerRequestInfoImpl info){
    this.info = info;
  }

  public org.omg.CORBA.Object getForwardReference(){
    if (location_forward != null)
      return location_forward.forward_reference;
    else
      return null;
  }

  public void reply(byte[] buf){
    reply(buf, buf.length);
  }

  public void reply(byte[] buf,int len)
  {
    if( out == null )
      out = new jacorb.orb.giop.ReplyOutputStream(connection, 
						  new org.omg.IOP.ServiceContext[0],
						  requestId(), 
						  org.omg.GIOP.ReplyStatusType.from_int(status));
    

    out.setBuffer(buf);
    //correct the requestId (unsigned long)
    out.setGIOPRequestId(requestId());
    out.setSize(len);
    //	out.insertMsgSize(); stream copied.. not needed
    try{
      ((ServerSideConnection)connection).sendReply( out );
    }catch (Exception ioe){
      jacorb.util.Debug.output(2,ioe);
      ioe.printStackTrace();
      System.out.println("ServerRequest: Error replying to request!");
    }
    in.req_hdr.response_expected=false; // make sure that no
    // other reply is send
  }    
}





