//Invocation_Adapter.cpp,v 1.13 2004/07/10 01:33:06 bala Exp
#include "Invocation_Adapter.h"
#include "Profile_Transport_Resolver.h"
#include "operation_details.h"
#include "Stub.h"
#include "ORB_Core.h"
#include "Synch_Invocation.h"
#include "debug.h"
#include "Collocated_Invocation.h"
#include "Transport.h"
#include "Transport_Mux_Strategy.h"
#include "Collocation_Proxy_Broker.h"

#if !defined (__ACE_INLINE__)
# include "tao/Invocation_Adapter.inl"
#endif /* __ACE_INLINE__ */


ACE_RCSID (tao,
           Invocation_Adapter,
           "Invocation_Adapter.cpp,v 1.13 2004/07/10 01:33:06 bala Exp")


namespace TAO
{
  void
  Invocation_Adapter::invoke (TAO::Exception_Data *ex_data,
                              unsigned long ex_count
                              ACE_ENV_ARG_DECL)
  {
    // Should stub object be refcounted here?
    TAO_Stub *stub =
      this->get_stub (ACE_ENV_SINGLE_ARG_PARAMETER);
    ACE_CHECK;

    TAO_Operation_Details op_details (this->operation_,
                                      this->op_len_,
                                      this->number_args_ != 0,
                                      this->args_,
                                      this->number_args_,
                                      ex_data,
                                      ex_count);

    this->invoke_i (stub,
                    op_details
                    ACE_ENV_ARG_PARAMETER);
    ACE_CHECK;
  }

  void
  Invocation_Adapter::invoke_i (TAO_Stub *stub,
                                TAO_Operation_Details &details
                                ACE_ENV_ARG_DECL)
  {
    // Cache the target to a local variable.
    CORBA::Object_var effective_target =
      CORBA::Object::_duplicate (this->target_);

    // Initial state
    TAO::Invocation_Status status = TAO_INVOKE_START;

    ACE_Time_Value *max_wait_time = 0;

    while (status == TAO_INVOKE_START ||
           status == TAO_INVOKE_RESTART)
      {
        // Default we go to remote
        Collocation_Strategy strat = TAO_CS_REMOTE_STRATEGY;

        // If we have a collocated proxy broker we look if we maybe
        // can use a collocated invocation.
        if (cpb_ != 0)
          {
            strat =
              TAO_ORB_Core::collocation_strategy (effective_target.in ()
                                                  ACE_ENV_ARG_PARAMETER);
            ACE_CHECK;
          }

        if (strat == TAO_CS_REMOTE_STRATEGY ||
            strat == TAO_CS_LAST)
          {
            status =
              this->invoke_remote_i (stub,
                                     details,
                                     effective_target,
                                     max_wait_time
                                     ACE_ENV_ARG_PARAMETER);
            ACE_CHECK;
          }
        else
          {
            status =
              this->invoke_collocated_i (stub,
                                         details,
                                         effective_target,
                                         strat
                                         ACE_ENV_ARG_PARAMETER);
            ACE_CHECK;
          }

        if (status == TAO_INVOKE_RESTART)
          {
            details.reset_request_service_info ();
            details.reset_reply_service_info ();

            if (TAO_debug_level > 2)
              {
                ACE_DEBUG ((LM_DEBUG,
                  "TAO (%P|%t) - Invocation_Adapter::invoke_i, "
                  "handling forwarded locations \n"));
              }
          }
      }
  }

  bool
  Invocation_Adapter::get_timeout (TAO_Stub *stub,
                                   ACE_Time_Value &timeout)
  {
    bool has_timeout = false;
    this->target_->orb_core ()->call_timeout_hook (stub,
                                                   has_timeout,
                                                   timeout);

    return has_timeout;
  }

  TAO_Stub *
  Invocation_Adapter::get_stub (ACE_ENV_SINGLE_ARG_DECL) const
  {
    TAO_Stub *stub =
      this->target_->_stubobj ();

    if (stub == 0)
      ACE_THROW_RETURN (CORBA::INTERNAL (
                          CORBA::SystemException::_tao_minor_code (
                            TAO_DEFAULT_MINOR_CODE,
                            EINVAL),
                          CORBA::COMPLETED_NO),
                        stub);

    return stub;
  }

  Invocation_Status
  Invocation_Adapter::invoke_collocated_i (TAO_Stub *stub,
                                           TAO_Operation_Details &details,
                                           CORBA::Object_var &effective_target,
                                           Collocation_Strategy strat
                                           ACE_ENV_ARG_DECL)
  {
    // To make a collocated call we must have a collocated proxy broker, the
    // invoke_i() will make sure that we only come here when we have one
    ACE_ASSERT (cpb_ != 0);

    // Initial state
    TAO::Invocation_Status status = TAO_INVOKE_START;

    Collocated_Invocation coll_inv (this->target_,
                                    effective_target.in (),
                                    stub,
                                    details,
                                    this->type_ == TAO_TWOWAY_INVOCATION);

    status = coll_inv.invoke (this->cpb_,
                              strat
                              ACE_ENV_ARG_PARAMETER);
    ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);

    if (status == TAO_INVOKE_RESTART &&
        coll_inv.is_forwarded ())
      {
        effective_target =
            coll_inv.steal_forwarded_reference ();

        (void) this->object_forwarded (effective_target,
                                       stub
                                       ACE_ENV_ARG_PARAMETER);
        ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);
      }

    return status;
  }


  Invocation_Status
  Invocation_Adapter::invoke_remote_i (TAO_Stub *stub,
                                       TAO_Operation_Details &details,
                                       CORBA::Object_var &effective_target,
                                       ACE_Time_Value *&max_wait_time
                                       ACE_ENV_ARG_DECL)
  {
    ACE_Time_Value tmp_wait_time;
    bool is_timeout  =
      this->get_timeout (stub,
                         tmp_wait_time);

    if (is_timeout)
      max_wait_time = &tmp_wait_time;

    // Create the resolver which will pick (or create) for us a
    // transport and a profile from the effective_target.
    Profile_Transport_Resolver resolver (effective_target.in (),
                                         stub);

    resolver.resolve (max_wait_time
                      ACE_ENV_ARG_PARAMETER);
    ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);

    // Update the request id now that we have a transport
    details.request_id (resolver.transport ()->tms ()->request_id ());

    Invocation_Status s = TAO_INVOKE_FAILURE;

    if (this->type_ == TAO_ONEWAY_INVOCATION)
      {
        return this->invoke_oneway (details,
                                    effective_target,
                                    resolver,
                                    max_wait_time
                                    ACE_ENV_ARG_PARAMETER);
      }
    else if (this->type_ == TAO_TWOWAY_INVOCATION)
      {
        // @@ NOTE:Need to change this to something better. Too many
        // hash defines meaning the same  thing..
        details.response_flags (TAO_TWOWAY_RESPONSE_FLAG);
        return this->invoke_twoway (details,
                                    effective_target,
                                    resolver,
                                    max_wait_time
                                    ACE_ENV_ARG_PARAMETER);
      }

    return s;
  }

  Invocation_Status
  Invocation_Adapter::invoke_twoway (TAO_Operation_Details &op,
                                     CORBA::Object_var &effective_target,
                                     Profile_Transport_Resolver &r,
                                     ACE_Time_Value *&max_wait_time
                                     ACE_ENV_ARG_DECL)
  {
    // Simple sanity check
    if (this->mode_ != TAO_SYNCHRONOUS_INVOCATION ||
        this->type_ != TAO_TWOWAY_INVOCATION)
      {
        ACE_THROW_RETURN (CORBA::INTERNAL (
            CORBA::SystemException::_tao_minor_code (
                TAO_DEFAULT_MINOR_CODE,
                EINVAL),
            CORBA::COMPLETED_NO),
                          TAO_INVOKE_FAILURE);
      }

    TAO::Synch_Twoway_Invocation synch (this->target_,
                                        r,
                                        op);

    Invocation_Status status =
      synch.remote_twoway (max_wait_time
                           ACE_ENV_ARG_PARAMETER);
    ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);

    if (status == TAO_INVOKE_RESTART &&
        synch.is_forwarded ())
      {
        effective_target = synch.steal_forwarded_reference ();

        this->object_forwarded (effective_target,
                                r.stub ()
                                ACE_ENV_ARG_PARAMETER);
        ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);
      }

    return status;
  }

  Invocation_Status
  Invocation_Adapter::invoke_oneway (TAO_Operation_Details &op,
                                     CORBA::Object_var &effective_target,
                                     Profile_Transport_Resolver &r,
                                     ACE_Time_Value *&max_wait_time
                                     ACE_ENV_ARG_DECL)
  {
    // Grab the syncscope policy from the ORB.
    bool has_synchronization = false;
    Messaging::SyncScope sync_scope;

    r.stub ()->orb_core ()->call_sync_scope_hook (r.stub (),
                                                  has_synchronization,
                                                  sync_scope);

    if (has_synchronization)
      op.response_flags (CORBA::Octet (sync_scope));
    else
      op.response_flags (CORBA::Octet (Messaging::SYNC_WITH_TRANSPORT));

    TAO::Synch_Oneway_Invocation synch (this->target_,
                                        r,
                                        op);

    Invocation_Status s =
      synch.remote_oneway (max_wait_time
                           ACE_ENV_ARG_PARAMETER);
    ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);

    if (s == TAO_INVOKE_RESTART &&
        synch.is_forwarded ())
      {
        effective_target = synch.steal_forwarded_reference ();

        this->object_forwarded (effective_target,
                                r.stub ()
                                ACE_ENV_ARG_PARAMETER);
        ACE_CHECK_RETURN (TAO_INVOKE_FAILURE);
      }

    return s;
  }

  void
  Invocation_Adapter::object_forwarded (CORBA::Object_var &effective_target,
                                        TAO_Stub *stub
                                        ACE_ENV_ARG_DECL)
  {
    // The object pointer has to be changed to a TAO_Stub pointer
    // in order to obtain the profiles.
    TAO_Stub *stubobj =
      effective_target->_stubobj ();

    if (stubobj == 0)
      ACE_THROW (CORBA::INTERNAL (
        CORBA::SystemException::_tao_minor_code (
          TAO_INVOCATION_LOCATION_FORWARD_MINOR_CODE,
          errno),
        CORBA::COMPLETED_NO));


    // Reset the profile in the stubs
    stub->add_forward_profiles (stubobj->base_profiles ());

    if (stub->next_profile () == 0)
      ACE_THROW (CORBA::TRANSIENT (
        CORBA::SystemException::_tao_minor_code (
          TAO_INVOCATION_LOCATION_FORWARD_MINOR_CODE,
          errno),
        CORBA::COMPLETED_NO));

    return;
  }
} // End namespace TAO
