// -*- Mode: C++; -*-
//                            Package   : omniORBpy
// pyLocalObjects.cc          Created on: 2005/10/20
//                            Author    : Duncan Grisby (dgrisby)
//
//    Copyright (C) 2005 Apasphere Ltd.
//    Copyright (C) 1999 AT&T Laboratories Cambridge
//
//    This file is part of the omniORBpy library
//
//    The omniORBpy library is free software; you can redistribute it
//    and/or modify it under the terms of the GNU Lesser General
//    Public License as published by the Free Software Foundation;
//    either version 2.1 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 Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public
//    License along with this library; if not, write to the Free
//    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
//    MA 02111-1307, USA
//
//
// Description:
//    Implementation of Python servant object

// $Id: pyLocalObjects.cc,v 1.1.2.1 2005/11/09 12:33:32 dgrisby Exp $

// $Log: pyLocalObjects.cc,v $
// Revision 1.1.2.1  2005/11/09 12:33:32  dgrisby
// Support POA LocalObjects.
//

#include <omnipy.h>
#include <pyThreadCache.h>


//
// Local object classes
//

class Py_ServantActivatorObj :
  public virtual PortableServer::ServantActivator
{
public:
  Py_ServantActivatorObj(PyObject* pysa) : impl_(pysa), refcount_(1) { }

  virtual ~Py_ServantActivatorObj() { }

  PortableServer::Servant incarnate(const PortableServer::ObjectId& oid,
				    PortableServer::POA_ptr         poa)
  {
    return impl_.incarnate(oid, poa);
  }

  void etherealize(const PortableServer::ObjectId& oid,
		   PortableServer::POA_ptr         poa,
		   PortableServer::Servant         serv,
		   CORBA::Boolean                  cleanup_in_progress,
		   CORBA::Boolean                  remaining_activations)
  {
    impl_.etherealize(oid, poa, serv, cleanup_in_progress,
		      remaining_activations);
  }

  inline PyObject* pyobj() { return impl_.pyobj(); }

  void _add_ref();
  void _remove_ref();
  void* _ptrToObjRef(const char* id);

private:
  omniPy::Py_ServantActivator impl_;
  int refcount_;

  // Not implemented
  Py_ServantActivatorObj(const Py_ServantActivatorObj&);
  Py_ServantActivatorObj& operator=(const Py_ServantActivatorObj&);
};

void
Py_ServantActivatorObj::_add_ref()
{
  omnipyThreadCache::lock _t;
  OMNIORB_ASSERT(refcount_ > 0);
  ++refcount_;
}

void
Py_ServantActivatorObj::_remove_ref()
{
  omnipyThreadCache::lock _t;

  if (--refcount_ > 0) return;

  OMNIORB_ASSERT(refcount_ == 0);
  delete this;
}

void*
Py_ServantActivatorObj::_ptrToObjRef(const char* id)
{
  if( id == omniPy::string_Py_ServantActivator )
    return (Py_ServantActivatorObj*)this;
  
  if( id == PortableServer::ServantActivator::_PD_repoId )
    return (PortableServer::ServantActivator_ptr) this;

  if( id == PortableServer::ServantManager::_PD_repoId )
    return (PortableServer::ServantManager_ptr) this;

  if( id == CORBA::LocalObject::_PD_repoId )
    return (CORBA::LocalObject_ptr) this;

  if( id == CORBA::Object::_PD_repoId )
    return (CORBA::Object_ptr) this;

  if( omni::strMatch(id, omniPy::string_Py_ServantActivator) )
    return (Py_ServantActivatorObj*)this;

  if( omni::strMatch(id, PortableServer::ServantActivator::_PD_repoId) )
    return (PortableServer::ServantActivator_ptr) this;

  if( omni::strMatch(id, PortableServer::ServantManager::_PD_repoId) )
    return (PortableServer::ServantManager_ptr) this;

  if( omni::strMatch(id, CORBA::LocalObject::_PD_repoId) )
    return (CORBA::LocalObject_ptr) this;

  if( omni::strMatch(id, CORBA::Object::_PD_repoId) )
    return (CORBA::Object_ptr) this;

  return 0;
}


class Py_ServantLocatorObj :
  public virtual PortableServer::ServantLocator
{
public:
  Py_ServantLocatorObj(PyObject* pysl) : impl_(pysl), refcount_(1) { }

  virtual ~Py_ServantLocatorObj() { }

  PortableServer::Servant preinvoke(const PortableServer::ObjectId& oid,
				    PortableServer::POA_ptr         poa,
				    const char*                     operation,
				    void*&                          cookie)
  {
    return impl_.preinvoke(oid, poa, operation, cookie);
  }

  void postinvoke(const PortableServer::ObjectId& oid,
		  PortableServer::POA_ptr         poa,
		  const char*                     operation,
		  void*                           cookie,
		  PortableServer::Servant         serv)
  {
    impl_.postinvoke(oid, poa, operation, cookie, serv);
  }

  inline PyObject* pyobj() { return impl_.pyobj(); }

  void _add_ref();
  void _remove_ref();
  void* _ptrToObjRef(const char* id);

private:
  omniPy::Py_ServantLocator impl_;
  int refcount_;

  // Not implemented
  Py_ServantLocatorObj(const Py_ServantLocatorObj&);
  Py_ServantLocatorObj& operator=(const Py_ServantLocatorObj&);
};

void
Py_ServantLocatorObj::_add_ref()
{
  omnipyThreadCache::lock _t;
  OMNIORB_ASSERT(refcount_ > 0);
  ++refcount_;
}

void
Py_ServantLocatorObj::_remove_ref()
{
  omnipyThreadCache::lock _t;
  if (--refcount_ > 0) return;

  OMNIORB_ASSERT(refcount_ == 0);
  delete this;
}

void*
Py_ServantLocatorObj::_ptrToObjRef(const char* id)
{
  if( id == omniPy::string_Py_ServantLocator )
    return (Py_ServantLocatorObj*)this;
  
  if( id == PortableServer::ServantLocator::_PD_repoId )
    return (PortableServer::ServantLocator_ptr) this;

  if( id == PortableServer::ServantManager::_PD_repoId )
    return (PortableServer::ServantManager_ptr) this;

  if( id == CORBA::LocalObject::_PD_repoId )
    return (CORBA::LocalObject_ptr) this;

  if( id == CORBA::Object::_PD_repoId )
    return (CORBA::Object_ptr) this;

  if( omni::strMatch(id, omniPy::string_Py_ServantLocator) )
    return (Py_ServantLocatorObj*)this;

  if( omni::strMatch(id, PortableServer::ServantLocator::_PD_repoId) )
    return (PortableServer::ServantLocator_ptr) this;

  if( omni::strMatch(id, PortableServer::ServantManager::_PD_repoId) )
    return (PortableServer::ServantManager_ptr) this;

  if( omni::strMatch(id, CORBA::LocalObject::_PD_repoId) )
    return (CORBA::LocalObject_ptr) this;

  if( omni::strMatch(id, CORBA::Object::_PD_repoId) )
    return (CORBA::Object_ptr) this;

  return 0;
}


class Py_AdapterActivatorObj :
  public virtual PortableServer::AdapterActivator
{
public:
  Py_AdapterActivatorObj(PyObject* pyaa) : impl_(pyaa), refcount_(1) { }

  virtual ~Py_AdapterActivatorObj() { }

  CORBA::Boolean unknown_adapter(PortableServer::POA_ptr parent,
				 const char*             name)
  {
    return impl_.unknown_adapter(parent, name);
  }

  inline PyObject* pyobj() { return impl_.pyobj(); }

  void _add_ref();
  void _remove_ref();
  void* _ptrToObjRef(const char* id);

private:
  omniPy::Py_AdapterActivator impl_;
  int refcount_;

  // Not implemented
  Py_AdapterActivatorObj(const Py_AdapterActivatorObj&);
  Py_AdapterActivatorObj& operator=(const Py_AdapterActivatorObj&);
};


void
Py_AdapterActivatorObj::_add_ref()
{
  omnipyThreadCache::lock _t;
  OMNIORB_ASSERT(refcount_ > 0);
  ++refcount_;
}

void
Py_AdapterActivatorObj::_remove_ref()
{
  omnipyThreadCache::lock _t;
  if (--refcount_ > 0) return;

  OMNIORB_ASSERT(refcount_ == 0);
  delete this;
}

void*
Py_AdapterActivatorObj::_ptrToObjRef(const char* id)
{
  if( id == omniPy::string_Py_AdapterActivator )
    return (Py_AdapterActivatorObj*)this;
  
  if( id == PortableServer::AdapterActivator::_PD_repoId )
    return (PortableServer::AdapterActivator_ptr) this;

  if( id == CORBA::LocalObject::_PD_repoId )
    return (CORBA::LocalObject_ptr) this;

  if( id == CORBA::Object::_PD_repoId )
    return (CORBA::Object_ptr) this;

  if( omni::strMatch(id, omniPy::string_Py_AdapterActivator) )
    return (Py_AdapterActivatorObj*)this;

  if( omni::strMatch(id, PortableServer::AdapterActivator::_PD_repoId) )
    return (PortableServer::AdapterActivator_ptr) this;

  if( omni::strMatch(id, CORBA::LocalObject::_PD_repoId) )
    return (CORBA::LocalObject_ptr) this;

  if( omni::strMatch(id, CORBA::Object::_PD_repoId) )
    return (CORBA::Object_ptr) this;

  return 0;
}


//
// Function to create C++ local objects from Python local objects
//

CORBA::LocalObject_ptr
omniPy::getLocalObjectForPyObject(PyObject* pyobj)
{
  PyObject* pyrepoId = PyObject_GetAttrString(pyobj,(char*)"_NP_RepositoryId");

  if (!(pyrepoId && PyString_Check(pyrepoId)))
    return 0;

  PyRefHolder holder(pyrepoId);
  const char* repoId = PyString_AS_STRING(pyrepoId);

  if (omni::ptrStrMatch(repoId, PortableServer::ServantActivator::_PD_repoId))
    return new Py_ServantActivatorObj(pyobj);

  if (omni::ptrStrMatch(repoId, PortableServer::ServantLocator::_PD_repoId))
    return new Py_ServantLocatorObj(pyobj);

  if (omni::ptrStrMatch(repoId, PortableServer::AdapterActivator::_PD_repoId))
    return new Py_AdapterActivatorObj(pyobj);

  return 0;
}


//
// Function to return Python object for a local object
//

PyObject*
omniPy::getPyObjectForLocalObject(CORBA::LocalObject_ptr lobj)
{
  PyObject* pyobj;

  void* vp;

  if ((vp = lobj->_ptrToObjRef(omniPy::string_Py_ServantActivator)))
    pyobj = ((Py_ServantActivatorObj*)vp)->pyobj();

  else if ((vp = lobj->_ptrToObjRef(omniPy::string_Py_ServantLocator)))
    pyobj = ((Py_ServantLocatorObj*)vp)->pyobj();

  else if ((vp = lobj->_ptrToObjRef(omniPy::string_Py_AdapterActivator)))
    pyobj = ((Py_AdapterActivatorObj*)vp)->pyobj();

  else
    OMNIORB_THROW(INV_OBJREF, INV_OBJREF_NoPythonTypeForPseudoObj,
		  CORBA::COMPLETED_NO);

  Py_INCREF(pyobj);
  return pyobj;
}


//
// Underlying ServantManager and AdapterActivator implementations
//

PortableServer::Servant
omniPy::
Py_ServantActivator::incarnate(const PortableServer::ObjectId& oid,
			       PortableServer::POA_ptr         poa)
{
  PyObject *method, *argtuple, *pyservant;
  omnipyThreadCache::lock _t;

  method = PyObject_GetAttrString(pysa_, (char*)"incarnate");
  if (!method) {
    PyErr_Clear();
    OMNIORB_THROW(NO_IMPLEMENT, NO_IMPLEMENT_NoPythonMethod,
		  CORBA::COMPLETED_MAYBE);
  }
  PortableServer::POA::_duplicate(poa);
  argtuple = Py_BuildValue((char*)"s#N",
			   (const char*)oid.NP_data(), oid.length(),
			   omniPy::createPyPOAObject(poa));

  // Do the up-call
  pyservant = PyEval_CallObject(method, argtuple);
  Py_DECREF(method);
  Py_DECREF(argtuple);

  if (pyservant) {
    omniPy::Py_omniServant* servant = omniPy::getServantForPyObject(pyservant);
    Py_DECREF(pyservant);

    if (servant)
      return servant;
    else
      OMNIORB_THROW(BAD_PARAM, BAD_PARAM_WrongPythonType, CORBA::COMPLETED_NO);
  }
  else {
    // An exception of some sort was thrown
    PyObject *etype, *evalue, *etraceback;
    PyObject *erepoId = 0;
    PyErr_Fetch(&etype, &evalue, &etraceback);
    PyErr_NormalizeException(&etype, &evalue, &etraceback);
    OMNIORB_ASSERT(etype);

    if (evalue)
      erepoId = PyObject_GetAttrString(evalue, (char*)"_NP_RepositoryId");

    if (!(erepoId && PyString_Check(erepoId))) {
      Py_XDECREF(erepoId);
      if (omniORB::trace(1)) {
	{
	  omniORB::logger l;
	  l << "Caught an unexpected Python exception during up-call.\n";
	}
	PyErr_Restore(etype, evalue, etraceback);
	PyErr_Print();
      }
      else {
        Py_DECREF(etype); Py_XDECREF(evalue); Py_XDECREF(etraceback);
      }
      OMNIORB_THROW(UNKNOWN, UNKNOWN_PythonException, CORBA::COMPLETED_MAYBE);
    }

    if (omni::strMatch(PyString_AS_STRING(erepoId),
		       PortableServer::ForwardRequest::_PD_repoId)) {
      Py_DECREF(erepoId); Py_DECREF(etype); Py_XDECREF(etraceback);
      PyObject* pyfr = PyObject_GetAttrString(evalue,
					      (char*)"forward_reference");
      Py_DECREF(evalue);
      if (pyfr) {
	CORBA::Object_ptr fr = (CORBA::Object_ptr)omniPy::getTwin(pyfr,
								  OBJREF_TWIN);
	if (fr) {
	  PortableServer::ForwardRequest ex(fr);
	  Py_DECREF(pyfr);
	  throw ex;
	}
      }
      else {
	PyErr_Clear();
	OMNIORB_THROW(BAD_PARAM, BAD_PARAM_WrongPythonType,
		      CORBA::COMPLETED_NO);
      }
    }

    // Is it a LOCATION_FORWARD?
    if (omni::strMatch(PyString_AS_STRING(erepoId),
		       "omniORB.LOCATION_FORWARD")) {
      Py_DECREF(erepoId); Py_DECREF(etype); Py_XDECREF(etraceback);
      omniPy::handleLocationForward(evalue);
    }

    // System exception or unknown user exception
    omniPy::produceSystemException(evalue, erepoId, etype, etraceback);
  }
  OMNIORB_ASSERT(0); // Never reach here
  return 0;
}


void
omniPy::
Py_ServantActivator::etherealize(const PortableServer::ObjectId& oid,
				 PortableServer::POA_ptr poa,
				 PortableServer::Servant serv,
				 CORBA::Boolean          cleanup_in_progress,
				 CORBA::Boolean          remaining_activations)
{
  PyObject *method, *argtuple, *result;
  omnipyThreadCache::lock _t;

  omniPy::Py_omniServant* pyos;
  pyos = (omniPy::Py_omniServant*)serv->
                                _ptrToInterface(omniPy::string_Py_omniServant);
  if (!pyos) {
    omniPy::InterpreterUnlocker _u;
    serv->_remove_ref();
    OMNIORB_THROW(OBJ_ADAPTER, OBJ_ADAPTER_IncompatibleServant,
		  CORBA::COMPLETED_NO);
  }

  method = PyObject_GetAttrString(pysa_, (char*)"etherealize");
  if (!method) {
    PyErr_Clear();
    omniPy::InterpreterUnlocker _u;
    serv->_remove_ref();
    OMNIORB_THROW(NO_IMPLEMENT, NO_IMPLEMENT_NoPythonMethod,
		  CORBA::COMPLETED_NO);
  }
  PortableServer::POA::_duplicate(poa);
  argtuple = Py_BuildValue((char*)"s#NNii",
			   (const char*)oid.NP_data(), oid.length(),
			   omniPy::createPyPOAObject(poa),
			   pyos->pyServant(),
			   (int)cleanup_in_progress,
			   (int)remaining_activations);
  // Do the up-call
  result = PyEval_CallObject(method, argtuple);
  Py_DECREF(method);
  Py_DECREF(argtuple);

  pyos->_locked_remove_ref();

  if (result)
    Py_DECREF(result);
  else {
    omniORB::logs(5, "omniORBpy: Servant etherealization "
		  "raised an exception!");

    if (omniORB::trace(10)) {
      omniORB::logs(10, "omniORBpy: Traceback follows:");
      PyErr_Print();
    }
    else
      PyErr_Clear();
  }
}



PortableServer::Servant
omniPy::
Py_ServantLocator::preinvoke(const PortableServer::ObjectId& oid,
			     PortableServer::POA_ptr         poa,
			     const char*                     operation,
			     void*&                          cookie)
{
  PyObject *method, *argtuple, *rettuple, *pyservant, *pycookie;
  omnipyThreadCache::lock _t;

  method = PyObject_GetAttrString(pysl_, (char*)"preinvoke");
  if (!method) {
    PyErr_Clear();
    OMNIORB_THROW(NO_IMPLEMENT, NO_IMPLEMENT_NoPythonMethod,
		  CORBA::COMPLETED_NO);
  }
  PortableServer::POA::_duplicate(poa);
  argtuple = Py_BuildValue((char*)"s#Ns",
			   (const char*)oid.NP_data(), oid.length(),
			   omniPy::createPyPOAObject(poa),
			   operation);

  // Do the up-call
  rettuple = PyEval_CallObject(method, argtuple);
  Py_DECREF(method);
  Py_DECREF(argtuple);

  if (rettuple) {
    if (PyTuple_Size(rettuple) != 2) {
      Py_DECREF(rettuple);
      OMNIORB_THROW(BAD_PARAM, BAD_PARAM_WrongPythonType, CORBA::COMPLETED_NO);
    }
    pyservant = PyTuple_GET_ITEM(rettuple, 0);
    pycookie  = PyTuple_GET_ITEM(rettuple, 1);

    omniPy::Py_omniServant* servant = omniPy::getServantForPyObject(pyservant);

    if (servant) {
      Py_INCREF(pycookie);
      cookie = pycookie;
      Py_DECREF(rettuple);
      return servant;
    }
    else {
      Py_DECREF(rettuple);
      OMNIORB_THROW(BAD_PARAM, BAD_PARAM_WrongPythonType, CORBA::COMPLETED_NO);
    }
  }
  else {
    // An exception of some sort was thrown
    PyObject *etype, *evalue, *etraceback;
    PyObject *erepoId = 0;
    PyErr_Fetch(&etype, &evalue, &etraceback);
    PyErr_NormalizeException(&etype, &evalue, &etraceback);
    OMNIORB_ASSERT(etype);

    if (evalue)
      erepoId = PyObject_GetAttrString(evalue, (char*)"_NP_RepositoryId");

    if (!(erepoId && PyString_Check(erepoId))) {
      Py_XDECREF(erepoId);
      if (omniORB::trace(1)) {
	{
	  omniORB::logger l;
	  l << "Caught an unexpected Python exception during up-call.\n";
	}
	PyErr_Restore(etype, evalue, etraceback);
	PyErr_Print();
      }
      else {
	Py_DECREF(etype); Py_XDECREF(evalue); Py_XDECREF(etraceback);
      }
      OMNIORB_THROW(UNKNOWN, UNKNOWN_PythonException, CORBA::COMPLETED_MAYBE);
    }

    if (omni::strMatch(PyString_AS_STRING(erepoId),
		       PortableServer::ForwardRequest::_PD_repoId)) {
      Py_DECREF(erepoId); Py_DECREF(etype); Py_XDECREF(etraceback);
      PyObject* pyfr = PyObject_GetAttrString(evalue,
					      (char*)"forward_reference");
      Py_DECREF(evalue);
      if (pyfr) {
	CORBA::Object_ptr fr = (CORBA::Object_ptr)omniPy::getTwin(pyfr,
								  OBJREF_TWIN);
	if (fr) {
	  PortableServer::ForwardRequest ex(fr);
	  Py_DECREF(pyfr);
	  throw ex;
	}
      }
      else {
	PyErr_Clear();
	OMNIORB_THROW(BAD_PARAM, BAD_PARAM_WrongPythonType,
		      CORBA::COMPLETED_NO);
      }
    }
    // Is it a LOCATION_FORWARD?
    if (omni::strMatch(PyString_AS_STRING(erepoId),
		       "omniORB.LOCATION_FORWARD")) {
      Py_DECREF(erepoId); Py_DECREF(etype); Py_XDECREF(etraceback);
      omniPy::handleLocationForward(evalue);
    }

    // System exception or unknown user exception
    omniPy::produceSystemException(evalue, erepoId, etype, etraceback);
  }
  OMNIORB_ASSERT(0); // Never reach here
  return 0;
}


void
omniPy::
Py_ServantLocator::postinvoke(const PortableServer::ObjectId& oid,
			      PortableServer::POA_ptr         poa,
			      const char*                     operation,
			      void*                           cookie,
			      PortableServer::Servant         serv)
{
  PyObject *method, *argtuple, *result;
  omnipyThreadCache::lock _t;

  omniPy::Py_omniServant* pyos;
  pyos = (omniPy::Py_omniServant*)serv->
                                _ptrToInterface(omniPy::string_Py_omniServant);
  if (!pyos) {
    omniPy::InterpreterUnlocker _u;
    serv->_remove_ref();
    OMNIORB_THROW(OBJ_ADAPTER, OBJ_ADAPTER_IncompatibleServant,
		  CORBA::COMPLETED_NO);
  }

  method = PyObject_GetAttrString(pysl_, (char*)"postinvoke");
  if (!method) {
    PyErr_Clear();
    omniPy::InterpreterUnlocker _u;
    serv->_remove_ref();
    OMNIORB_THROW(NO_IMPLEMENT, NO_IMPLEMENT_NoPythonMethod,
		  CORBA::COMPLETED_NO);
  }
  PortableServer::POA::_duplicate(poa);
  argtuple = Py_BuildValue((char*)"s#NsNN",
			   (const char*)oid.NP_data(), oid.length(),
			   omniPy::createPyPOAObject(poa),
			   operation,
			   (PyObject*)cookie,
			   pyos->pyServant());
  // Do the up-call
  result = PyEval_CallObject(method, argtuple);
  Py_DECREF(method);
  Py_DECREF(argtuple);

  pyos->_locked_remove_ref();

  if (result) {
    Py_DECREF(result);
    return;
  }
  else {
    omniPy::handlePythonException();
  }
}


CORBA::Boolean
omniPy::
Py_AdapterActivator::unknown_adapter(PortableServer::POA_ptr parent,
				     const char*             name)
{
  PyObject *method, *argtuple, *pyresult;
  omnipyThreadCache::lock _t;

  method = PyObject_GetAttrString(pyaa_, (char*)"unknown_adapter");
  if (!method) {
    PyErr_Clear();
    OMNIORB_THROW(NO_IMPLEMENT, NO_IMPLEMENT_NoPythonMethod,
		  CORBA::COMPLETED_NO);
  }
  PortableServer::POA::_duplicate(parent);
  argtuple = Py_BuildValue((char*)"Ns",
			   omniPy::createPyPOAObject(parent), name);

  // Do the up-call
  pyresult = PyEval_CallObject(method, argtuple);
  Py_DECREF(method);
  Py_DECREF(argtuple);

  if (pyresult) {
    if (!PyInt_Check(pyresult)) {
      Py_DECREF(pyresult);
      OMNIORB_THROW(BAD_PARAM, BAD_PARAM_WrongPythonType, CORBA::COMPLETED_NO);
    }
    CORBA::Boolean result = PyInt_AS_LONG(pyresult);
    Py_DECREF(pyresult);
    return result;
  }
  else {
    omniORB::logs(5, "omniORBpy: AdapterActivator::unknown_adapter "
		  "raised an exception!");

    if (omniORB::trace(10)) {
      omniORB::logs(10, "omniORBpy: Traceback follows:");
      PyErr_Print();
    }
    else
      PyErr_Clear();
  }
  return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1