// FT_TestReplica_i.cpp,v 1.8 2004/04/01 05:16:46 bala Exp
/* -*- C++ -*- */
//=============================================================================
/**
 *  @file    FT_TestReplica_i.cpp
 *
 *  FT_TestReplica_i.cpp,v 1.8 2004/04/01 05:16:46 bala Exp
 *
 *  Implements CORBA interface TestReplica.
 *
 *  @author Dale Wilson <wilson_d@ociweb.com>
 */
//=============================================================================
//

#include "FT_TestReplica_i.h"
#include "FT_ReplicaFactory_i.h"
#include "tao/PortableServer/ORB_Manager.h"
#include "tao/ORB_Constants.h"
#include "FT_TestReplicaC.h"
// FUZZ: disable check_for_streams_include
#include "ace/streams.h"

//////////////////
// TestReplica_i

namespace
{
  /**
   * Endian neutral store of long into indexable object.
   * BUFFER can be sequence of Octet, unsigned char[], etc.
   *
   * TODO: Find this a good home.
   *
   * @param state an object that supports char & operator[] (size_t index);
   * @param offset is the position within state where the first character should be stored.
   * @param value is the data to be inserted into state.
   */
  template<typename BUFFER>
  void storeLong(BUFFER & state, size_t offset, long value)
  {
    state[offset    ] = ACE_static_cast (unsigned char, value >> 24);
    state[offset + 1] = ACE_static_cast (unsigned char, value >> 16);
    state[offset + 2] = ACE_static_cast (unsigned char, value >>  8);
    state[offset + 3] = ACE_static_cast (unsigned char, value      );
  }

  /**
   * Endian neutral load of long from indexable object.
   * BUFFER can be sequence of Octet, unsigned char[], etc.
   *
   * TODO: Find this a good home.
   *
   * @param state an object that supports const char & operator[] (size_t index) const;
   * @param offset is the position within state where the first character can be found
   * @returns value is the data loaded from state.
   */
  template<typename BUFFER>
  long loadLong(const BUFFER & state, size_t offset)
  {
    long result
      = ((state[offset    ] & 0xFF) << 24)
      | ((state[offset + 1] & 0xFF) << 16)
      | ((state[offset + 2] & 0xFF) <<  8)
      | ((state[offset + 3] & 0xFF)      );
    return result;
  }
}

// NO_RESPONSE ->no reinvocation

#define FAULT_CODE CORBA::TRANSIENT

// Macros to simplify suicide.
#define KEVORKIAN(value, method)                                   \
  if (this->death_pending_ == (FT_TEST::TestReplica::value)){      \
    suicide (#value " in method " #method);                        \
    ACE_THROW (FAULT_CODE (                                        \
      CORBA::SystemException::_tao_minor_code (                    \
            TAO_DEFAULT_MINOR_CODE,                                \
            EFAULT),                                               \
            CORBA::COMPLETED_NO));                                 \
    } else ;

#define KEVORKIAN_DURING(method)                                   \
  if (this->death_pending_ == FT_TEST::TestReplica::BEFORE_REPLY ){\
    suicide ("read-only method " #method);                         \
    ACE_THROW (FAULT_CODE (                                        \
      CORBA::SystemException::_tao_minor_code (                    \
            TAO_DEFAULT_MINOR_CODE,                                \
            EFAULT),                                               \
            CORBA::COMPLETED_NO));                                 \
    } else ;

#define KEVORKIAN_RETURN(value, method, result)                    \
  if (this->death_pending_ == (FT_TEST::TestReplica::value)){      \
    suicide (#value " in method " #method);                        \
    ACE_THROW_RETURN (FAULT_CODE (                                 \
      CORBA::SystemException::_tao_minor_code (                    \
            TAO_DEFAULT_MINOR_CODE,                                \
            EFAULT),                                               \
            CORBA::COMPLETED_NO),                                  \
      result);                                                     \
    } else ;

#define KEVORKIAN_DURING_RETURN(method, result)                    \
  if (this->death_pending_ == FT_TEST::TestReplica::BEFORE_REPLY ){\
    suicide ("read-only method " #method);                         \
    ACE_THROW_RETURN (FAULT_CODE (                                 \
      CORBA::SystemException::_tao_minor_code (                    \
            TAO_DEFAULT_MINOR_CODE,                                \
            EFAULT),                                               \
            CORBA::COMPLETED_NO),                                  \
            result);                                               \
    } else ;


//////////////////////////////////////////////////
// class FT_TestReplica_i construction/destruction

FT_TestReplica_i::FT_TestReplica_i (FT_ReplicaFactory_i * factory, const char * name, unsigned long factory_id)
  : death_pending_ (FT_TEST::TestReplica::NOT_YET)
  , verbose_ (1)
  , name_ (name)
  , factory_id_ (factory_id)
  , factory_ (factory)
  , orb_ (0)
  , poa_ (0)
  , object_id_ (0)
{
//  cout << name_.c_str() << '@' << this->factory_->location() << '#' << this->factory_id_ << " Construct" << endl;
}

FT_TestReplica_i::~FT_TestReplica_i ()
{
//  cout << name_.c_str() << '@' << this->factory_->location() << '#' << this->factory_id_ << " Destruct" << endl;
}


void FT_TestReplica_i::suicide(const char * note)
{
  cout << name_.c_str() << '@' << this->factory_->location() << '#' << this->factory_id_ << " Simulate FAULT_CODE fault: " << note << endl;

  // Tell the poa we aren't accepting future calls
  this->poa_->deactivate_object (this->object_id_.in ());
}

/////////////////////////////////////////////////////
// class FT_TestReplica_i public, non-CORBA interface



//static
const char * FT_TestReplica_i::repository_id()
{
  return FT_TEST::_tc_TestReplica->id();
}

int
FT_TestReplica_i::parse_args (int argc, char *argv[])
{
  ACE_UNUSED_ARG (argc);
  ACE_UNUSED_ARG (argv);
  return 0;
}

//static
const char *
FT_TestReplica_i::usage_options()
{
  return "";
}

unsigned long FT_TestReplica_i::factory_id()const
{
  return this->factory_id_;
}

::PortableServer::POA_ptr FT_TestReplica_i::_default_POA (ACE_ENV_SINGLE_ARG_DECL_NOT_USED)
{
  return ::PortableServer::POA::_duplicate(this->poa_.in ());
}

PortableServer::ObjectId FT_TestReplica_i::object_id()const
{
  return this->object_id_.in();
}


/**
 * Initialize this object.
 * @param orbManager our ORB -- we keep var to it.
 * @return zero for success; nonzero is process return code for failure.
 */
int FT_TestReplica_i::init (CORBA::ORB_var & orb ACE_ENV_ARG_DECL)
{
  this->orb_ = orb;

  // Use the ROOT POA for now
  CORBA::Object_var poa_object =
    this->orb_->resolve_initial_references (TAO_OBJID_ROOTPOA
                                            ACE_ENV_ARG_PARAMETER);
  ACE_CHECK_RETURN (-1);

  if (CORBA::is_nil (poa_object.in ()))
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT (" (%P|%t) Unable to initialize the POA.\n")),
                      -1);

  // Get the POA object.
  this->poa_ =
    PortableServer::POA::_narrow (poa_object.in ()
                                  ACE_ENV_ARG_PARAMETER);

  ACE_CHECK_RETURN (-1);

  if (CORBA::is_nil(this->poa_.in ()))
  {
    ACE_ERROR_RETURN ((LM_ERROR,
                       ACE_TEXT (" (%P|%t) Unable to narrow the POA.\n")),
                      -1);
  }

  PortableServer::POAManager_var poa_manager =
    this->poa_->the_POAManager (ACE_ENV_SINGLE_ARG_PARAMETER);
  ACE_CHECK_RETURN (-1);

  poa_manager->activate (ACE_ENV_SINGLE_ARG_PARAMETER);
  ACE_CHECK_RETURN (-1);


  // Register with the POA.

  this->object_id_ = this->poa_->activate_object (this ACE_ENV_ARG_PARAMETER);
  ACE_CHECK_RETURN (-1);

  return 0;
}

void FT_TestReplica_i::_remove_ref (ACE_ENV_SINGLE_ARG_DECL)
{
  //////////////////////////////////////////////////
  // WARNING: The following call invokes fini then deletes this object
  this->factory_->remove_replica(this->factory_id_, this ACE_ENV_ARG_PARAMETER);
}

int FT_TestReplica_i::fini (ACE_ENV_SINGLE_ARG_DECL_NOT_USED)
{
  return 0;
}



/////////////////////////////////////////////////////
// class FT_TestReplica_i:  PullMonitorable interface
CORBA::Boolean FT_TestReplica_i::is_alive (ACE_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  KEVORKIAN_RETURN(DURING_IS_ALIVE, is_alive, 0)
  ACE_ERROR ((LM_ERROR,
    "%s@%s#%d: is_alive: %d\n",
    this->name_.c_str(),
    this->factory_->location(),
    this->factory_id_,
    (this->death_pending_ != FT_TEST::TestReplica::DENY_IS_ALIVE)
    ));

  return this->death_pending_ != FT_TEST::TestReplica::DENY_IS_ALIVE;
}

/////////////////////////////////////////////////////
// class FT_TestReplica_i:  Updateable interface
FT::State * FT_TestReplica_i::get_update (ACE_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException, FT::NoUpdateAvailable))
{
  KEVORKIAN_RETURN(DURING_GET_UPDATE, get_update, 0)
  long counter = load();
  ::FT::State_var vState = new ::FT::State;
  vState->length(sizeof(counter));
  storeLong(vState, 0, counter);
  return vState._retn();
}

void FT_TestReplica_i::set_update (const FT::State & s ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException, FT::InvalidUpdate))
{
#if defined(FT_TEST_LACKS_UPDATE)
  ACE_THROW ( FT::InvalidUpdate () );
#else // FT_TEST_LACKS_UPDATE
  KEVORKIAN(BEFORE_SET_UPDATE, set_update)
  long counter = loadLong<FT::State>(s, 0);
  store(counter);
  KEVORKIAN(AFTER_SET_UPDATE, set_update)
#endif // FT_TEST_LACKS_UPDATE
}

/////////////////////////////////////////////////////
// class FT_TestReplica_i:  Checkpointable interface
::FT::State * FT_TestReplica_i::get_state (ACE_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException, FT::NoStateAvailable))
{
#if defined(FT_TEST_LACKS_STATE)
  ACE_THROW( FT::NoStateAvailable () );
#else // FT_TEST_LACKS_STATE
  KEVORKIAN_RETURN(DURING_GET_STATE, get_state, 0)
  long counter = load();
  ::FT::State_var vState = new ::FT::State;
  vState->length(sizeof(counter));
  storeLong(vState, 0, counter);
  return vState._retn();
#endif // FT_TEST_LACKS_STATE
}

void FT_TestReplica_i::set_state (const FT::State & s ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException, FT::InvalidState))
{
#if defined(FT_TEST_LACKS_STATE)
  ACE_THROW ( FT::InvalidState () );
#else // FT_TEST_LACKS_STATE
  KEVORKIAN(BEFORE_SET_STATE, set_state)
  long counter = loadLong<FT::State>(s, 0);
  store(counter);
  KEVORKIAN(AFTER_SET_STATE, set_state)
#endif // FT_TEST_LACKS_STATE
}

void FT_TestReplica_i::tao_update_object_group (
    const char * iogr,
    PortableGroup::ObjectGroupRefVersion version,
    CORBA::Boolean is_primary
    ACE_ENV_ARG_DECL
  )
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  ACE_UNUSED_ARG (iogr);
  ACE_UNUSED_ARG (version);
  ACE_UNUSED_ARG (is_primary);

  ACE_THROW (CORBA::NO_IMPLEMENT());
}

//////////////////////////////
// implement FT_TEST::Replica

void FT_TestReplica_i::set (CORBA::Long value
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  KEVORKIAN(BEFORE_STATE_CHANGE, set)
  long counter = value;
  store(counter);
  KEVORKIAN(BEFORE_REPLY, set)
}

CORBA::Long FT_TestReplica_i::increment (CORBA::Long delta
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  KEVORKIAN_RETURN(BEFORE_STATE_CHANGE, increment, 0)
  long counter = load ();
  counter += delta;
  store (counter);
  KEVORKIAN_RETURN(BEFORE_REPLY, increment, 0)
  return counter;
}

CORBA::Long FT_TestReplica_i::get (ACE_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  KEVORKIAN_DURING_RETURN(get, 0)
  long counter = load ();
  return counter;
}

CORBA::Long FT_TestReplica_i::counter (ACE_ENV_SINGLE_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  KEVORKIAN_DURING_RETURN([get]counter, 0)
  long counter = load ();
  return counter;
}

void FT_TestReplica_i::counter (CORBA::Long counter
    ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  KEVORKIAN(BEFORE_STATE_CHANGE, [set]counter)
  store (counter);
  KEVORKIAN(BEFORE_REPLY, [set]counter)
}

void FT_TestReplica_i::die (FT_TEST::TestReplica::Bane  when
      ACE_ENV_ARG_DECL)
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  cout << name_.c_str() << '@' << this->factory_->location()
    << '#' << this->factory_id_ << " Received death threat: " << when << endl;

  this->death_pending_ = when;
  KEVORKIAN(RIGHT_NOW, die)
}

void FT_TestReplica_i::shutdown (ACE_ENV_SINGLE_ARG_DECL_NOT_USED)
  ACE_THROW_SPEC ((CORBA::SystemException))
{
  cout << name_.c_str() << '@' << this->factory_->location()
    << '#' << this->factory_id_ << " Shut down requested" << endl;
  this->death_pending_ = FT_TEST::TestReplica::CLEAN_EXIT;
}

//////////////////////////////////////////////
// FT_TestReplica_i public non-CORBA interface
int FT_TestReplica_i::idle (int & result ACE_ENV_ARG_DECL)
{
  int quit = 0;
  if (this->death_pending_ == FT_TEST::TestReplica::WHILE_IDLE)
  {
    ACE_ERROR ((LM_ERROR,
      "%s@%s#%d: Simulated fault WHILE_IDLE",
      this->name_.c_str(),
      this->factory_->location(),
      ACE_static_cast(int, this->factory_id_ )
      ));
    this->poa_->deactivate_object (this->object_id_.in ()
                 ACE_ENV_ARG_PARAMETER);
    result = 0;
    quit = 1;
  }
  else if (this->death_pending_ == FT_TEST::TestReplica::CLEAN_EXIT)
  {
    this->poa_->deactivate_object (this->object_id_.in ()
                 ACE_ENV_ARG_PARAMETER);
    result = 0;
    quit = 1;
  }
  return quit;
}

void FT_TestReplica_i::request_quit()
{
  this->death_pending_ = FT_TEST::TestReplica::WHILE_IDLE;
}


void FT_TestReplica_i::store(long counter)
{
  FILE * f = ACE_OS::fopen("Persistent.dat", "w");
  if(f != 0)
  {
    unsigned char* buffer = 0;
    ACE_NEW (buffer, unsigned char [sizeof(long)]);
    storeLong(buffer, 0, counter);
    ACE_OS::fwrite(buffer, 1, sizeof(long), f);
    ACE_OS::fclose(f);
    if (this->verbose_)
    {
      cout << name_.c_str() << '@' << this->factory_->location()
        << '#' << this->factory_id_ << ": " << counter << endl;
    }
    delete[] buffer;
    buffer = 0;
  }
}

long FT_TestReplica_i::load ()
{
  long counter = 0;
  FILE * f = ACE_OS::fopen("Persistent.dat","r");
  if(f != 0)
  {
    unsigned char buffer[sizeof(long)];
    ACE_OS::fread(buffer, 1, sizeof(long), f);
    ACE_OS::fclose(f);
    counter = loadLong<unsigned char *>(buffer, 0);
  }
  return counter;
}

///////////////////////////////////
// Template instantiation for
// competence-challenged compilers.

#if defined (ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION)
  template void storeLong <> (::FT::State_var & state, size_t offset, long value);
  template long loadLong <> (const ::FT::State_var & state, size_t offset);

  template void storeLong <> (unsigned char * & state, size_t offset, long value);
  template long loadLong <> (const unsigned char * & state, size_t offset);

#elif defined (ACE_HAS_TEMPLATE_INSTANTIATION_PRAGMA)
# pragma instantiate void storeLong(::FT::State_var & state, size_t offset, long value)
# pragma long loadLong(const ::FT::State_var & state, size_t offset)

# pragma instantiate void storeLong(unsigned char * & state, size_t offset, long value)
# pragma long loadLong(const unsigned char * & state, size_t offset)

#endif /* ACE_HAS_EXPLICIT_TEMPLATE_INSTANTIATION */
