// $Id$

// @ (#)iiopobj.cpp     1.9 95/11/04
// Copyright 1995 by Sun Microsystems Inc.
// All Rights Reserved
//
// XXXX Bridge:         CORBA::Object operations
//
// Some CORBA::Object and other operations are specific to this STUB
// based implementation, and can neither be used by other kinds of
// objref nor have a default implementation.

#include "tao/Stub.h"
#include "tao/Sequence.h"
#include "tao/Object.h"
#include "tao/GIOP.h"
#include "tao/NVList.h"
#include "tao/Invocation.h"
#include "tao/Asynch_Invocation.h"
#include "tao/ORB_Core.h"
#include "tao/Client_Strategy_Factory.h"
#include "tao/debug.h"
#include "tao/Sync_Strategies.h"
#include "ace/Auto_Ptr.h"

#include "tao/Buffering_Constraint_Policy.h"
#include "tao/Messaging_Policy_i.h"
#include "tao/Client_Priority_Policy.h"

#if !defined (__ACE_INLINE__)
# include "tao/Stub.i"
#endif /* ! __ACE_INLINE__ */

#include "tao/Timeprobe.h"

ACE_RCSID(tao, TAO_Stub, "$Id$")

#if defined (ACE_ENABLE_TIMEPROBES)

static const char *TAO_TAO_Stub_Timeprobe_Description[] =
  {
    "TAO_Stub::do_static_call - start",
    "TAO_Stub::do_static_call - end",
    "TAO_Stub::do_static_call - set_cancel",
    "TAO_Stub::do_static_call - grab_orb_core",
    "TAO_Stub::do_static_call - invocation_ctor",
    "TAO_Stub::do_static_call - invocation_start",
    "TAO_Stub::do_static_call - put_params"
  };

enum
  {
    // Timeprobe description table start key
    TAO_STUB_OBJECT_DO_STATIC_CALL_START = 500,
    TAO_STUB_OBJECT_DO_STATIC_CALL_END,
    TAO_STUB_OBJECT_DO_STATIC_CALL_SET_CANCEL,
    TAO_STUB_OBJECT_DO_STATIC_CALL_GRAB_ORB_CORE,
    TAO_STUB_OBJECT_DO_STATIC_CALL_INVOCATION_CTOR,
    TAO_STUB_OBJECT_DO_STATIC_CALL_INVOCATION_START,
    TAO_STUB_OBJECT_DO_STATIC_CALL_PUT_PARAMS
  };

// Setup Timeprobes
ACE_TIMEPROBE_EVENT_DESCRIPTIONS (TAO_TAO_Stub_Timeprobe_Description,
                                  TAO_STUB_OBJECT_DO_STATIC_CALL_START);

#endif /* ACE_ENABLE_TIMEPROBES */

TAO_Stub::TAO_Stub (char *repository_id,
                    const TAO_MProfile &profiles,
                    TAO_ORB_Core* orb_core)
  : type_id (repository_id),
    base_profiles_ ((CORBA::ULong) 0),
    forward_profiles_ (0),
    profile_in_use_ (0),
    profile_lock_ptr_ (0),
    profile_success_ (0),
    refcount_lock_ (),
    refcount_ (1),
    use_locate_request_ (0),
    first_locate_request_ (0),
    orb_core_ (orb_core),
    orb_ (),
    servant_orb_ ()
#if (TAO_HAS_CORBA_MESSAGING == 1)
    , policies_ (0)
#endif /* TAO_HAS_CORBA_MESSAGING == 1 */
{
  if (this->orb_core_ == 0)
    {
      if (TAO_debug_level > 0)
        {
          ACE_DEBUG ((LM_DEBUG,
                      ASYS_TEXT ("TAO: (%P|%t) TAO_Stub created with default ")
                      ASYS_TEXT ("ORB core\n")));
        }
      this->orb_core_ = TAO_ORB_Core_instance ();
    }

  // Duplicate the ORB. This will help us keep the ORB around until
  // the CORBA::Object we represent dies.
  this->orb_ = CORBA::ORB::_duplicate (this->orb_core_->orb ());

  this->profile_lock_ptr_ =
    this->orb_core_->client_factory ()->create_profile_lock ();

  this->base_profiles (profiles);
}

TAO_Stub::~TAO_Stub (void)
{
  ACE_ASSERT (this->refcount_ == 0);

  if (this->forward_profiles_)
    reset_profiles ();

  if (this->profile_in_use_ != 0)
    {
      if (this->orb_->orb_core () != 0)
        {
          // The hint in the profile is a hint for a client connection
          // handler.  If the ORB core doesn't exist, perhaps due to
          // it being destroy()ed, then no connectors exist so do not
          // reset the hint in case it points to non-existent
          // connection handler.
          this->profile_in_use_->reset_hint ();
        }

      // decrease reference count on profile
      this->profile_in_use_->_decr_refcnt ();
      this->profile_in_use_ = 0;
    }

  delete this->profile_lock_ptr_;

#if (TAO_HAS_CORBA_MESSAGING == 1)
  delete this->policies_;
#endif /* TAO_HAS_CORBA_MESSAGING == 1 */
}

void
TAO_Stub::add_forward_profiles (const TAO_MProfile &mprofiles)
{
  // we assume that the profile_in_use_ is being
  // forwarded!  Grab the lock so things don't change.
  ACE_MT (ACE_GUARD (ACE_Lock,
                     guard,
                     *this->profile_lock_ptr_));

  TAO_MProfile *now_pfiles = this->forward_profiles_;
  if (now_pfiles == 0)
    now_pfiles = &this->base_profiles_;

  ACE_NEW (this->forward_profiles_,
           TAO_MProfile (mprofiles));

  // forwarded profile points to the new IOR (profiles)
  this->profile_in_use_->forward_to (this->forward_profiles_);

  // new profile list points back to the list which was forwarded.
  this->forward_profiles_->forward_from (now_pfiles);

  // make sure we start at the beginning of mprofiles
  this->forward_profiles_->rewind ();

  // Since we have been forwarded, we must set profile_success_ to 0
  // since we are starting a new with a new set of profiles!
  this->profile_success_ = 0;
}

// Quick'n'dirty hash of objref data, for partitioning objrefs into
// sets.
//
// NOTE that this must NOT go across the network!

// @@ Use all profiles for hash function!!!!!  FRED
//    can get different values, depending on the profile_in_use!!
CORBA::ULong
TAO_Stub::hash (CORBA::ULong max,
                CORBA::Environment &ACE_TRY_ENV)
{
  // we rely on the profile object that its address info
  if (profile_in_use_)
    return profile_in_use_->hash (max, ACE_TRY_ENV);
  ACE_ERROR_RETURN((LM_ERROR, ASYS_TEXT ("(%P|%t) hash called on a null profile!\n")), 0);
}

// Expensive comparison of objref data, to see if two objrefs
// certainly point at the same object. (It's quite OK for this to
// return FALSE, and yet have the two objrefs really point to the same
// object.)
//
// NOTE that this must NOT go across the network!
// @@ Two object references are the same if any two profiles are the same!
CORBA::Boolean
TAO_Stub::is_equivalent (CORBA::Object_ptr other_obj)
{
  if (CORBA::is_nil (other_obj) == 1)
    return 0;

  TAO_Profile *other_profile = other_obj->_stubobj ()->profile_in_use_;
  TAO_Profile *this_profile = this->profile_in_use_;

  if (other_profile == 0 || this_profile == 0)
    return 0;

  // Compare the profiles
  return this_profile->is_equivalent (other_profile);
}

// Memory managment

CORBA::ULong
TAO_Stub::_incr_refcnt (void)
{
  ACE_GUARD_RETURN (ACE_SYNCH_MUTEX,
                    guard,
                    this->refcount_lock_,
                    0);

  return this->refcount_++;
}

CORBA::ULong
TAO_Stub::_decr_refcnt (void)
{
  {
    ACE_GUARD_RETURN (ACE_SYNCH_MUTEX,
                      mon,
                      this->refcount_lock_,
                      0);

    this->refcount_--;
    if (this->refcount_ != 0)
      return this->refcount_;
  }

  delete this;
  return 0;
}

// Note that if the repository ID (typeID) is NULL, it will make
// narrowing rather expensive, though it does ensure that type-safe
// narrowing code gets thoroughly exercised/debugged!  Without a
// typeID, the _narrow will be required to make an expensive remote
// "is_a" call.

// THREADING NOTE: Code below this point is of course thread-safe (at
// least on supported threaded platforms), so the caller of these
// routines need only ensure that the data being passed in is not
// being modified by any other thread.
//
// As an _experiment_ (to estimate the performance cost) remote calls
// are currently deemed "cancel-safe".  That means that they can be
// called by threads when they're in asynchronous cancellation mode.
// The only effective way to do this is to disable async cancellation
// for the duration of the call.  There are numerous rude interactions
// with code generators for C++ ... cancellation handlers just do
// normal stack unwinding like exceptions, but exceptions are purely
// synchronous and sophisticated code generators rely on that to
// generate better code, which in some cases may be very hard to
// unwind.

class TAO_Synchronous_Cancellation_Required
  // = TITLE
  //     Stick one of these at the beginning of a block that can't
  //     support asynchronous cancellation, and which must be
  //     cancel-safe.
  //
  // = EXAMPLE
  //     somefunc()
  //     {
  //       TAO_Synchronous_Cancellation_Required NOT_USED;
  //       ...
  //     }
{
public:
  // These should probably be in a separate inline file, but they're
  // only used within this one file right now, and we always want them
  // inlined, so here they sit.
  TAO_Synchronous_Cancellation_Required (void)
    : old_type_ (0)
    {
#if !defined (VXWORKS)
      ACE_OS::thr_setcanceltype (THR_CANCEL_DEFERRED, &old_type_);
#endif /* ! VXWORKS */
    }

  ~TAO_Synchronous_Cancellation_Required (void)
    {
#if !defined (VXWORKS)
      int dont_care;
      ACE_OS::thr_setcanceltype(old_type_, &dont_care);
#endif /* ! VXWORKS */
    }
private:
  int old_type_;
};

// "Stub interpreter" for static stubs.  IDL compiler (or human
// equivalent thereof :-) should just dump a read-only description of
// the call into "calldata" and do varargs calls to this routine,
// which does all the work.

void
TAO_Stub::do_static_call (CORBA::Environment &ACE_TRY_ENV,
                          const TAO_Call_Data *info,
                          void** args)

{
  ACE_FUNCTION_TIMEPROBE (TAO_STUB_OBJECT_DO_STATIC_CALL_START);

  TAO_Synchronous_Cancellation_Required NOT_USED;

  ACE_TIMEPROBE (TAO_STUB_OBJECT_DO_STATIC_CALL_SET_CANCEL);

  ACE_TIMEPROBE (TAO_STUB_OBJECT_DO_STATIC_CALL_GRAB_ORB_CORE);

  // Do a locate_request if necessary/wanted.
  // Suspect that you will be forwarded, so be proactive!
  // strategy for reducing overhead when you think a request will
  // be forwarded.  No standard way now to know.
  if (this->use_locate_request_ && this->first_locate_request_)
    {
      TAO_GIOP_Locate_Request_Invocation call (this,
                                               this->orb_core_);

      // Simply let these exceptions propagate up
      // (if any of them occurs.)
      call.start (ACE_TRY_ENV);
      ACE_CHECK;

      call.invoke (ACE_TRY_ENV);
      ACE_CHECK;

      this->first_locate_request_ = 0;
    }

  if (info->is_roundtrip)
    {
      TAO_GIOP_Twoway_Invocation call (this,
                                       info->opname,
                                       ACE_OS::strlen (info->opname),
                                       this->orb_core_);

      ACE_TIMEPROBE (TAO_STUB_OBJECT_DO_STATIC_CALL_INVOCATION_CTOR);

      // We may need to loop through here more than once if we're
      // forwarded to some other object reference.
      //
      // NOTE: A quality-of-service policy may be useful to establish
      // here, specifically one controlling how many times the call is
      // reissued before failing the call on the assumption that
      // something is broken.
      //
      // NOTE: something missing is a dynamic way to change the policy
      // of whether to issue LocateRequest messages or not.  This code
      // uses a simple, fixed policy: never use LocateRequest
      // messages.
      //
      for (;;)
        {
          call.start (ACE_TRY_ENV);
          ACE_CHECK;

          CORBA::Short flag = 131;

          call.prepare_header (ACE_static_cast (CORBA::Octet, flag),
                               ACE_TRY_ENV);
          ACE_CHECK;

          ACE_TIMEPROBE (TAO_STUB_OBJECT_DO_STATIC_CALL_INVOCATION_START);

          // Make the call ... blocking for the response.
          this->put_params (ACE_TRY_ENV, info, call, args);
          ACE_CHECK;

          ACE_TIMEPROBE (TAO_STUB_OBJECT_DO_STATIC_CALL_PUT_PARAMS);

          int status =
            call.invoke (info->excepts,
                         info->except_count,
                         ACE_TRY_ENV);
          ACE_CHECK;

          if (status == TAO_INVOKE_RESTART)
            continue;

          if (status == TAO_INVOKE_EXCEPTION)
            return; // Shouldn't happen

          if (status != TAO_INVOKE_OK)
            ACE_THROW (CORBA::UNKNOWN (TAO_DEFAULT_MINOR_CODE,
                                       CORBA::COMPLETED_MAYBE));

          // The only case left is status == TAO_INVOKE_OK, exit the
          // loop.  We cannot retry because at this point we either
          // got a reply or something with an status of
          // COMPLETED_MAYBE, thus we cannot reissue the request if we
          // are to satisfy the "at most once" semantics.
          break;
        }

      // Now, get all the "return", "out", and "inout" parameters
      // from the response message body ... return parameter is
      // first, the rest are in the order defined in the IDL spec
      // (which is also the order that DII users are required to
      // use).

      const TAO_Param_Data *pdp = info->params;
      for (void** i = args;
           i !=  args + info->param_count;
           i++, pdp++)
        {
          void *ptr = *i;

          // if it is an inout parameter, it would become
          // necessary to first release the "in" memory
          if (pdp->mode == PARAM_INOUT)
            {
              // @@ TODO - add others as we test each case
              // (ASG) will do 03/22/98.
              // @@ IMHO this should be handled in the stub
              // (coryan)
              CORBA::TCKind kind = pdp->tc->kind (ACE_TRY_ENV);
              ACE_CHECK;

              switch (kind)
                {
                  case CORBA::tk_string:
                    CORBA::string_free (*(char **) ptr);
                    *(char **)ptr = 0;
                    break;
                  case CORBA::tk_objref:
                    CORBA::release (*(CORBA::Object_ptr *) ptr);
                    break;
                  case CORBA::tk_any:
                    break;
                  default:
                    break;
                }
            }

          if (pdp->mode == PARAM_RETURN
              || pdp->mode == PARAM_OUT
              || pdp->mode == PARAM_INOUT)
            {
              // The language mapping's memory allocation
              // policy says that some data is heap-allocated.
              // This interpreter is told about the relevant
              // policy by whoever built the operation
              // description (e.g. the IDL compiler) so it
              // doesn't have to know the policy associated
              // with a particular language binding
              // (e.g. C/C++ differ, and C++ even has
              // different policies for different kinds of
              // structures).
              if (pdp->value_size == 0)
                {
                  (void) call.inp_stream ().decode (pdp->tc,
                                                    ptr,
                                                    0,
                                                    ACE_TRY_ENV);
                  ACE_CHECK;
                }
              else
                {
                  // @@ (ASG) -  I think we must completely
                  // get rid of this case because IDL compiler
                  // generated stubs will use this function
                  // and they better allocate all the memory.

                  // assert (value_size == tc->size());
                  ACE_NEW (*(void **)ptr,
                           CORBA::Octet [pdp->value_size]);

                  (void) call.inp_stream ().decode (pdp->tc,
                                                    *(void**)ptr,
                                                    0,
                                                    ACE_TRY_ENV);
                  ACE_CHECK;
                }
            }
        }
    } // if (two way)
  else
    {
      TAO_GIOP_Oneway_Invocation call (this,
                                       info->opname,
                                       ACE_OS::strlen (info->opname),
                                       this->orb_core_);

      ACE_TIMEPROBE (TAO_STUB_OBJECT_DO_STATIC_CALL_INVOCATION_CTOR);

      for (;;)
        {
          call.start (ACE_TRY_ENV);
          ACE_CHECK;

          CORBA::Octet flag =
            ACE_static_cast (CORBA::Octet,
                             call.sync_scope ());

          call.prepare_header (flag,
                               ACE_TRY_ENV);
          ACE_CHECK;

          this->put_params (ACE_TRY_ENV,
                            info,
                            call,
                            args);
          ACE_CHECK;

          ACE_TIMEPROBE (TAO_STUB_OBJECT_DO_STATIC_CALL_PUT_PARAMS);

          int status = call.invoke (ACE_TRY_ENV);
          ACE_CHECK;

          if (status == TAO_INVOKE_RESTART)
            continue;

          if (status == TAO_INVOKE_EXCEPTION)
            return; // Shouldn't happen

          if (status != TAO_INVOKE_OK)
            ACE_THROW (CORBA::UNKNOWN (TAO_DEFAULT_MINOR_CODE,
                                       CORBA::COMPLETED_MAYBE));

          break;
        }
    }
}

void
TAO_Stub::put_params (CORBA::Environment &ACE_TRY_ENV,
                      const TAO_Call_Data *info,
                      TAO_GIOP_Invocation &call,
                      void** args)
{
  // Now, put all "in" and "inout" parameters into the request
  // message body.
  //
  // Some "inout" data have an extra level of indirection,
  // specified by the language mapping's memory allocation
  // policies ... the indirection only shows up here when it's
  // needed later for allocating "out" memory, otherwise there's
  // just one indirection.

  TAO_OutputCDR &cdr = call.out_stream ();

  const TAO_Param_Data *pdp = info->params;
  for (void** i = args;
       i != args + info->param_count;
       i++, pdp++)
    {
      void *ptr = *i;

      if (pdp->mode == PARAM_IN)
        {
          (void) cdr.encode (pdp->tc,
                             ptr,
                             0,
                             ACE_TRY_ENV);
        }
      else if (pdp->mode == PARAM_INOUT)
        {
          if (pdp->value_size == 0)
            (void) cdr.encode (pdp->tc,
                               ptr,
                               0,
                               ACE_TRY_ENV);
          else
            (void) cdr.encode (pdp->tc,
                               *(void**)ptr,
                               0,
                               ACE_TRY_ENV);
        }
      ACE_CHECK;
    }
}

#if !defined (TAO_HAS_MINIMUM_CORBA)

// DII analogue of the above.

void
TAO_Stub::do_dynamic_call (const char *opname,
                           CORBA::Boolean is_roundtrip,
                           CORBA::NVList_ptr args,
                           CORBA::NamedValue_ptr result,
                           CORBA::Flags,
                           CORBA::ExceptionList &exceptions,
                           int lazy_evaluation,
                           CORBA::Environment &ACE_TRY_ENV)
{
  TAO_Synchronous_Cancellation_Required NOT_USED;

  // Do a locate_request if necessary/wanted.
  // Suspect that you will be forwarded, so be proactive!
  // strategy for reducing overhead when you think a request will
  // be forwarded.  No standard way now to know.
  if (this->use_locate_request_ && this->first_locate_request_)
    {
      TAO_GIOP_Locate_Request_Invocation call (this,
                                               this->orb_core_);

      // Simply let these exceptions propagate up
      // (if any of them occurs.)
      call.start (ACE_TRY_ENV);
      ACE_CHECK;

      call.invoke (ACE_TRY_ENV);
      ACE_CHECK;

      this->first_locate_request_ = 0;
    }

  if (is_roundtrip)
    {
      TAO_GIOP_Twoway_Invocation call (this,
                                       opname,
                                       ACE_OS::strlen (opname),
                                       this->orb_core_);

      // Loop as needed for forwarding; see above.

      for (;;)
        {
          call.start (ACE_TRY_ENV);
          ACE_CHECK;

          CORBA::Short flag = 131;

          call.prepare_header (ACE_static_cast (CORBA::Octet, flag),
                               ACE_TRY_ENV);
          ACE_CHECK;

          this->put_params (call,
                            args,
                            ACE_TRY_ENV);
          ACE_CHECK;

          // Make the call ... blocking for the response.
          int status =
            call.invoke (exceptions,
                         ACE_TRY_ENV);
          ACE_CHECK;

          if (status == TAO_INVOKE_RESTART)
            continue;

          if (status == TAO_INVOKE_EXCEPTION)
            return; // Shouldn't happen

          if (status != TAO_INVOKE_OK)
            ACE_THROW (CORBA::UNKNOWN (TAO_DEFAULT_MINOR_CODE,
                                       CORBA::COMPLETED_MAYBE));

          // The only case left is status == TAO_INVOKE_OK, exit the
          // loop.  We cannot retry because at this point we either
          // got a reply or something with an status of
          // COMPLETED_MAYBE, thus we cannot reissue the request if we
          // are to satisfy the "at most once" semantics.
          break;
        }

      // Now, get all the "return", "out", and "inout" parameters
      // from the response message body ... return parameter is
      // first, the rest are in the order defined in the IDL spec
      // (which is also the order that DII users are required to
      // use).

      if (result != 0)
        {
          result->value ()->_tao_decode (call.inp_stream (),
                                         ACE_TRY_ENV);
          ACE_CHECK;
        }

      args->_tao_incoming_cdr (call.inp_stream (),
                               CORBA::ARG_OUT | CORBA::ARG_INOUT,
                               lazy_evaluation,
                               ACE_TRY_ENV);
    }
  else
    {
      TAO_GIOP_Oneway_Invocation call (this,
                                       opname,
                                       ACE_OS::strlen (opname),
                                       this->orb_core_);

      for (;;)
        {
          call.start (ACE_TRY_ENV);
          ACE_CHECK;

          CORBA::Octet response_flag = ACE_static_cast (CORBA::Octet,
                                                        call.sync_scope ());

          call.prepare_header (response_flag,
                               ACE_TRY_ENV);
          ACE_CHECK;

          this->put_params (call,
                            args,
                            ACE_TRY_ENV);
          ACE_CHECK;

          int status = call.invoke (ACE_TRY_ENV);
          ACE_CHECK;

          if (status == TAO_INVOKE_RESTART)
            continue;

          if (status == TAO_INVOKE_EXCEPTION)
            return; // Shouldn't happen

          if (status != TAO_INVOKE_OK)
            ACE_THROW (CORBA::UNKNOWN (TAO_DEFAULT_MINOR_CODE,
                                       CORBA::COMPLETED_MAYBE));

          break;
        }
    }
}

void
TAO_Stub::do_deferred_call (const CORBA::Request_ptr req,
                            CORBA::Environment &ACE_TRY_ENV)
{

  TAO_Synchronous_Cancellation_Required NOT_USED;

  // Do a locate_request if necessary/wanted.
  // Suspect that you will be forwarded, so be proactive!
  // strategy for reducing overhead when you think a request will
  // be forwarded.  No standard way now to know.
  if (this->use_locate_request_ && this->first_locate_request_)
    {
      TAO_GIOP_Locate_Request_Invocation call (this,
                                               this->orb_core_);

      // Simply let these exceptions propagate up
      // (if any of them occurs.)
      call.start (ACE_TRY_ENV);
      ACE_CHECK;

      call.invoke (ACE_TRY_ENV);
      ACE_CHECK;

      this->first_locate_request_ = 0;
    }

  TAO_GIOP_DII_Deferred_Invocation call (this,
                                         this->orb_core_,
                                         req);

  // Loop as needed for forwarding; see above.

  for (;;)
    {
      call.start (ACE_TRY_ENV);
      ACE_CHECK;

      CORBA::Short flag = 131;

      call.prepare_header (ACE_static_cast (CORBA::Octet, flag),
                           ACE_TRY_ENV);
      ACE_CHECK;

      this->put_params (call,
                        req->arguments (),
                        ACE_TRY_ENV);
      ACE_CHECK;

      // Make the call without blocking.
      CORBA::ULong status = call.invoke (ACE_TRY_ENV);
      ACE_CHECK;

      if (status == TAO_INVOKE_RESTART)
        continue;

      if (status != TAO_INVOKE_OK)
        ACE_THROW (CORBA::UNKNOWN (TAO_DEFAULT_MINOR_CODE,
                                   CORBA::COMPLETED_MAYBE));

      // The only case left is status == TAO_INVOKE_OK, exit the
      // loop.  We cannot retry because at this point we either
      // got a reply or something with an status of
      // COMPLETED_MAYBE, thus we cannot reissue the request if we
      // are to satisfy the "at most once" semantics.
      break;
    }
}

void
TAO_Stub::put_params (TAO_GIOP_Invocation &call,
                      CORBA::NVList_ptr args,
                      CORBA::Environment &ACE_TRY_ENV)
{
  args->_tao_encode (call.out_stream (),
                     this->orb_core_,
                     CORBA::ARG_IN | CORBA::ARG_INOUT,
                     ACE_TRY_ENV);
}

#endif /* TAO_HAS_MINIMUM_CORBA */

// ****************************************************************

#if (TAO_HAS_CORBA_MESSAGING == 1)

CORBA::Policy_ptr
TAO_Stub::get_policy (
    CORBA::PolicyType type,
    CORBA::Environment &ACE_TRY_ENV)
{
  // No need to lock, the stub only changes its policies at
  // construction time...

  if (this->policies_ == 0)
    return CORBA::Policy::_nil ();

  return this->policies_->get_policy (type,
                                      ACE_TRY_ENV);
}

CORBA::Policy_ptr
TAO_Stub::get_client_policy (CORBA::PolicyType type,
                             CORBA::Environment &ACE_TRY_ENV)
{
  // No need to lock, the stub only changes its policies at
  // construction time...

  CORBA::Policy_var result;
  if (this->policies_ != 0)
    {
      result = this->policies_->get_policy (type,
                                            ACE_TRY_ENV);
      ACE_CHECK_RETURN (CORBA::Policy::_nil ());
    }

  if (CORBA::is_nil (result.in ()))
    {
      TAO_Policy_Current &policy_current = this->orb_core_->policy_current ();
      result = policy_current.get_policy (type,
                                          ACE_TRY_ENV);
      ACE_CHECK_RETURN (CORBA::Policy::_nil ());
    }

  if (CORBA::is_nil (result.in ()))
    {
      // @@ Must lock, but is is harder to implement than just modifying
      //    this call: the ORB does take a lock to modify the policy
      //    manager
      TAO_Policy_Manager *policy_manager =
        this->orb_core_->policy_manager ();
      if (policy_manager != 0)
        {
          result = policy_manager->get_policy (type,
                                               ACE_TRY_ENV);
          ACE_CHECK_RETURN (CORBA::Policy::_nil ());
        }
    }

  if (CORBA::is_nil (result.in ()))
    {
      result = this->orb_core_->get_default_policy (type,
                                                    ACE_TRY_ENV);
      ACE_CHECK_RETURN (CORBA::Policy::_nil ());
    }

  return result._retn ();
}

TAO_RelativeRoundtripTimeoutPolicy *
TAO_Stub::relative_roundtrip_timeout (void)
{
  TAO_RelativeRoundtripTimeoutPolicy *result = 0;

  // No need to lock, the stub only changes its policies at
  // construction time...
  if (this->policies_ != 0)
    result = this->policies_->relative_roundtrip_timeout ();

  // No need to lock, the object is in TSS storage....
  if (result == 0)
    {
      TAO_Policy_Current &policy_current =
        this->orb_core_->policy_current ();
      result = policy_current.relative_roundtrip_timeout ();
    }

  // @@ Must lock, but is is harder to implement than just modifying
  //    this call: the ORB does take a lock to modify the policy
  //    manager
  if (result == 0)
    {
      TAO_Policy_Manager *policy_manager =
        this->orb_core_->policy_manager ();
      if (policy_manager != 0)
        result = policy_manager->relative_roundtrip_timeout ();
    }

  if (result == 0)
    result = this->orb_core_->default_relative_roundtrip_timeout ();

  return result;
}

TAO_Client_Priority_Policy *
TAO_Stub::client_priority (void)
{
  TAO_Client_Priority_Policy *result = 0;

  // No need to lock, the stub only changes its policies at
  // construction time...
  if (this->policies_ != 0)
    result = this->policies_->client_priority ();

  // No need to lock, the object is in TSS storage....
  if (result == 0)
    {
      TAO_Policy_Current &policy_current =
        this->orb_core_->policy_current ();
      result = policy_current.client_priority ();
    }

  // @@ Must lock, but is is harder to implement than just modifying
  //    this call: the ORB does take a lock to modify the policy
  //    manager
  if (result == 0)
    {
      TAO_Policy_Manager *policy_manager =
        this->orb_core_->policy_manager ();
      if (policy_manager != 0)
        result = policy_manager->client_priority ();
    }

  if (result == 0)
    result = this->orb_core_->default_client_priority ();

  return result;
}

TAO_Sync_Scope_Policy *
TAO_Stub::sync_scope (void)
{
  TAO_Sync_Scope_Policy *result = 0;

  // No need to lock, the stub only changes its policies at
  // construction time...
  if (this->policies_ != 0)
    result = this->policies_->sync_scope ();

  // No need to lock, the object is in TSS storage....
  if (result == 0)
    {
      TAO_Policy_Current &policy_current =
        this->orb_core_->policy_current ();
      result = policy_current.sync_scope ();
    }

  // @@ Must lock, but is is harder to implement than just modifying
  //    this call: the ORB does take a lock to modify the policy
  //    manager
  if (result == 0)
    {
      TAO_Policy_Manager *policy_manager =
        this->orb_core_->policy_manager ();
      if (policy_manager != 0)
        result = policy_manager->sync_scope ();
    }

  if (result == 0)
    result = this->orb_core_->default_sync_scope ();

  return result;
}

TAO_Buffering_Constraint_Policy *
TAO_Stub::buffering_constraint (void)
{
  TAO_Buffering_Constraint_Policy *result = 0;

  // No need to lock, the stub only changes its policies at
  // construction time...
  if (this->policies_ != 0)
    result = this->policies_->buffering_constraint ();

  // No need to lock, the object is in TSS storage....
  if (result == 0)
    {
      TAO_Policy_Current &policy_current =
        this->orb_core_->policy_current ();
      result = policy_current.buffering_constraint ();
    }

  // @@ Must lock, but is is harder to implement than just modifying
  //    this call: the ORB does take a lock to modify the policy
  //    manager
  if (result == 0)
    {
      TAO_Policy_Manager *policy_manager =
        this->orb_core_->policy_manager ();
      if (policy_manager != 0)
        result = policy_manager->buffering_constraint ();
    }

  if (result == 0)
    result = this->orb_core_->default_buffering_constraint ();

  return result;
}

TAO_Stub *
TAO_Stub::set_policy_overrides (
    const CORBA::PolicyList & policies,
    CORBA::SetOverrideType set_add,
    CORBA::Environment &ACE_TRY_ENV)
{
  // Notice the use of an explicit constructor....
  auto_ptr<TAO_Policy_Manager_Impl> policy_manager (new TAO_Policy_Manager_Impl);

  if (set_add == CORBA::SET_OVERRIDE)
    {
      policy_manager->set_policy_overrides (policies,
                                            set_add,
                                            ACE_TRY_ENV);
      ACE_CHECK_RETURN (0);
    }
  else if (this->policies_ == 0)
    {
      policy_manager->set_policy_overrides (policies,
                                            CORBA::SET_OVERRIDE,
                                            ACE_TRY_ENV);
      ACE_CHECK_RETURN (0);
     }
  else
    {
      policy_manager->copy_from (this->policies_,
                                 ACE_TRY_ENV);
      ACE_CHECK_RETURN (0);
      policy_manager->set_policy_overrides (policies,
                                            set_add,
                                            ACE_TRY_ENV);
      ACE_CHECK_RETURN (0);
    }

  TAO_Stub* stub;
  ACE_NEW_RETURN (stub, TAO_Stub (CORBA::string_dup (this->type_id.in ()),
                                  this->base_profiles_,
                                  this->orb_core_),
                  0);
  stub->policies_ = policy_manager.release ();

  // Copy the servant ORB if it is present.
  stub->servant_orb (this->servant_orb_var ().in ());

  return stub;
}

CORBA::PolicyList *
TAO_Stub::get_policy_overrides (const CORBA::PolicyTypeSeq &types,
                                CORBA::Environment &ACE_TRY_ENV)
{
  if (this->policies_ == 0)
    return 0;

  return this->policies_->get_policy_overrides (types,
                                           ACE_TRY_ENV);
}

CORBA::Boolean
TAO_Stub::validate_connection (CORBA::PolicyList_out inconsistent_policies,
                               CORBA::Environment &ACE_TRY_ENV)
{
  inconsistent_policies = 0;

  // Check if we care about Client Priority policy, and store the
  // result in the variable called <set>.
  int set = 1;
  POA_TAO::ClientPriorityPolicy *policy =
    this->client_priority ();
  if (policy == 0)
    set = 0;
  else
    // Policy is set.
    {
      TAO::PrioritySpecification priority_spec =
        policy->priority_specification (ACE_TRY_ENV);
      ACE_CHECK_RETURN (0);
      TAO::PrioritySelectionMode mode = priority_spec.mode;

      // Don't care about priority.
      if (mode == TAO::USE_NO_PRIORITY)
        set = 0;
    }

  // Use Locate Request to establish connection/make sure the object
  // is there ...
  TAO_GIOP_Locate_Request_Invocation locate_request (this,
                                                     this->orb_core_);

  //@@ Currently, if we select profiles based on priorities (i.e.,
  //   ClientPriorityPolicy is set), and we get a FORWARD reply to our
  //   location request, we don't go to the new location - just return
  //   0. This is because we don't yet have full support in
  //   MProfiles & friends for profiles used based on priorities.
  //   Once the support is there, we should follow a forwarded object
  //   to track it down, just like in the case where
  //   ClientPriorityPolicy is not set.  At that point, we can remove
  //   a lot of 'special case' code from this function, along with
  //   this comment ;-) (marina).
  for (;;)
    {
      locate_request.start (ACE_TRY_ENV);
      ACE_CHECK_RETURN (0);

      int status = locate_request.invoke (ACE_TRY_ENV);
      ACE_CHECK_RETURN (0);

      // We'll get this only if the object was, in fact, forwarded.
      if (status == TAO_INVOKE_RESTART)
        if (set)
          return 0;
        else
          continue;

      if (status != TAO_INVOKE_OK)
        {
          ACE_THROW_RETURN (CORBA::UNKNOWN (TAO_DEFAULT_MINOR_CODE,
                                            CORBA::COMPLETED_YES),
                            0);

        }
      break;
    }
  return 1;
}

#endif /* TAO_HAS_CORBA_MESSAGING == 1 */

TAO_Sync_Strategy &
TAO_Stub::sync_strategy (void)
{

#if (TAO_HAS_CORBA_MESSAGING == 1)

  POA_Messaging::SyncScopePolicy *policy =
    this->sync_scope ();

  if (policy != 0)
    {
      Messaging::SyncScope scope = policy->synchronization ();

      if (scope == Messaging::SYNC_WITH_TRANSPORT ||
          scope == Messaging::SYNC_WITH_SERVER ||
          scope == Messaging::SYNC_WITH_TARGET)
        return this->orb_core_->transport_sync_strategy ();

      if (scope == Messaging::SYNC_NONE ||
          scope == Messaging::SYNC_EAGER_BUFFERING)
        return this->orb_core_->eager_buffering_sync_strategy ();

      if (scope == Messaging::SYNC_DELAYED_BUFFERING)
        return this->orb_core_->delayed_buffering_sync_strategy ();
    }

#endif /* TAO_HAS_CORBA_MESSAGING == 1 */

  return this->orb_core_->transport_sync_strategy ();
}

#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)

#if (TAO_HAS_CORBA_MESSAGING == 1)
template class auto_ptr<TAO_Policy_Manager_Impl>;
template class ACE_Auto_Basic_Ptr<TAO_Policy_Manager_Impl>;
#endif /* TAO_HAS_CORBA_MESSAGING == 1 */

#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)

#if (TAO_HAS_CORBA_MESSAGING == 1)
#pragma instantiate auto_ptr<TAO_Policy_Manager_Impl>
#pragma instantiate ACE_Auto_Basic_Ptr<TAO_Policy_Manager_Impl>
#endif /* TAO_HAS_CORBA_MESSAGING == 1 */

#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */