#!/usr/bin/env python
#############################################################################
# Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1997, 1998, 1999, 2000
# All Rights Reserved.
#
# The software contained on this media is the property of the DSTC Pty
# Ltd.  Use of this software is strictly in accordance with the
# license agreement in the accompanying LICENSE.HTML file.  If your
# distribution of this software does not contain a LICENSE.HTML file
# then you have no rights to use this software in any manner and
# should contact DSTC at the address below to determine an appropriate
# licensing arrangement.
# 
#      DSTC Pty Ltd
#      Level 7, GP South
#      Staff House Road
#      University of Queensland
#      St Lucia, 4072
#      Australia
#      Tel: +61 7 3365 4310
#      Fax: +61 7 3365 4311
#      Email: enquiries@dstc.edu.au
# 
# This software is being provided "AS IS" without warranty of any
# kind.  In no event shall DSTC Pty Ltd be liable for damage of any
# kind arising out of or in connection with the use or performance of
# this software.
#
# Project:      Fnorb
# File:         $Source: /projects/fnorb/Fnorb-1.1/orb/RCS/CORBA.py,v $
# Version:      @(#)$RCSfile: CORBA.py,v $ $Revision: 1.48 $
#
#############################################################################
""" CORBA module. """


# Fnorb modules.
import fnorb_thread, TypeManager, Util


# ORB identifier.
ORB_ID = 'The ORB called Fnorb v1.1.Return.of.Fnorb'

# Boolean 'constants' as specified in the Python language mapping.
TRUE  = 1
FALSE = 0

#############################################################################
# Exception classes.
#############################################################################

# Exception base class.
class CORBAException: pass

# User exceptions.
class UserException(CORBAException): pass

# System exceptions.
#
# Enum: IDL:omg.org/CORBA/completion_status:1.0
COMPLETED_YES     = Util.EnumMember('COMPLETED_YES',   0)
COMPLETED_NO      = Util.EnumMember('COMPLETED_NO',    1)
COMPLETED_MAYBE   = Util.EnumMember('COMPLETED_MAYBE', 2)
completion_status = Util.Enum("IDL:omg.org/CORBA/completion_status:1.0",
			      [COMPLETED_YES, COMPLETED_NO, COMPLETED_MAYBE])

TypeManager.TypeManager_init().add_type("IDL:omg.org/CORBA/completion_status:1.0", "000000000000001100000088000000000000002849444C3A6F6D672E6F72672F434F5242412F636F6D706C6574696F6E5F7374617475733A312E300000000012636F6D706C6574696F6E5F737461747573000000000000030000000E434F4D504C455445445F5945530000000000000D434F4D504C455445445F4E4F0000000000000010434F4D504C455445445F4D4159424500", completion_status)

class SystemException(CORBAException):
    """ Struct: IDL:omg.org/CORBA/SystemException:1.0 """

    _FNORB_ID = "IDL:omg.org/CORBA/SystemException:1.0"

    def __init__(self, _minor=0, _completed=COMPLETED_NO):
        """ Constructor. """

        self.minor = _minor
        self.completed = _completed
        return

    def __getinitargs__(self):
        """ Return the constructor arguments for unpickling. """

        return (self.minor, self.completed)

    def __str__(self):
 	""" Return a string representation of the exception. """

 	return 'Minor: ' + `self.minor` + ' Completed: ' + str(self.completed)

    # fixme: Replace these methods with typecode marshalling?
    def _fnorb_marshal(self, cursor):
        """ Marshal the structure onto an octet stream. """

        cursor.marshal('L', self.minor)
        cursor.marshal('L', self.completed.value())
        return

    def _fnorb_unmarshal(self, cursor):
        """ Unmarshal the structure from an octet stream. """

        self.minor = cursor.unmarshal('L')
        self.completed = completion_status[int(cursor.unmarshal('L'))]
        return

# A class for every system exception!
class UNKNOWN         (SystemException): pass # The unknown exception!
class BAD_PARAM       (SystemException): pass # Invalid parameter was passed.
class NO_MEMORY       (SystemException): pass # Memory allocation failure.
class IMP_LIMIT       (SystemException): pass # Violated implementation limit.
class COMM_FAILURE    (SystemException): pass # Communication failure.
class INV_OBJREF      (SystemException): pass # Invalid object reference.
class NO_PERMISSION   (SystemException): pass # No permission for operation.
class INTERNAL        (SystemException): pass # ORB internal error.
class MARSHAL         (SystemException): pass # Error marshalling param/result.
class INITIALIZE      (SystemException): pass # ORB initialisation failure.
class NO_IMPLEMENT    (SystemException): pass # Op. implementation unavailable.
class BAD_TYPECODE    (SystemException): pass # Bad typecode.
class BAD_OPERATION   (SystemException): pass # Invalid operation.
class NO_RESOURCES    (SystemException): pass # Insufficient resources for req.
class NO_RESPONSE     (SystemException): pass # Response to req. not available.
class PERSIST_STORE   (SystemException): pass # Persistent storage failure.
class BAD_INV_ORDER   (SystemException): pass # Routine inv. out of order
class TRANSIENT       (SystemException): pass # Transient failure, reissue req.
class FREE_MEM        (SystemException): pass # Cannot free memory.
class INV_IDENT       (SystemException): pass # Invalid identifier syntax.
class INV_FLAG        (SystemException): pass # Invalid flag was specified.
class INTF_REPOS      (SystemException): pass # Error accessing I/F repository.
class BAD_CONTEXT     (SystemException): pass # Error processing context obj.
class OBJ_ADAPTER     (SystemException): pass # Failure detected by adapter.
class DATA_CONVERSION (SystemException): pass # Data conversion error.
class OBJECT_NOT_EXIST(SystemException): pass # Non-existent object, del OR.

#############################################################################
# The ORB interface.
#############################################################################

def ORB_init(argv=[], orb_id=ORB_ID):
    """ Initialise the ORB.

    This is a factory function for the ORB class (the ORB is a singleton (ie.
    there can only be one ORB instance per process)).

    """
    try:
	orb = ORB(argv, orb_id)

    except ORB, orb:
	pass

    return orb

def ORB_delete():
    orb = ORB_init()
    for srv in orb._ORB__services.keys():
	del orb._ORB__services[srv]
    orb.__class__._ORB__instance = None

class ORB:
    """ The ORB object.

    The ORB is a singleton (ie. there can only be one ORB instance per
    process).

    """
    # Singleton instance.
    __instance = None

    # Mutex to make access via the factory function thread-safe.
    __lk = fnorb_thread.allocate_lock()

    # Exceptions.
    class InvalidName(UserException): pass
 
    def __init__(self, argv, orb_id):
	""" Constructor.

	'argv'   command line arguments.
	'orb_id' the unique identifier of the ORB implementation. This MUST be
	         the same string as the 'constant' CORBA.ORB_ID.

	"""
	# The ORB id MUST be the same string as the 'constant' ORB_ID.
	if orb_id != ORB_ID:
	    raise INITIALIZE() # System exception.

	# The ORB is a singleton (ie. there can only be one ORB instance per
	# process (have I made this point clear yet?!?).
	ORB.__lk.acquire()
	try:
	    if ORB.__instance is not None:
		raise ORB.__instance

	    ORB.__instance = self

	finally:
	    ORB.__lk.release()

	# A list of pending DII requests.
	self.__pending = []

	# The object adapter registered with the ORB.
	self.__object_adapter = None

	# A mutex to make access to the ORB's data structures thread-safe.
	self.__lk = fnorb_thread.allocate_lock()

	# ORB options can be specified via a configuration file, environment
	# variables, or the command-line (in order of increasing precedence).
	self.__options = self.__get_options(argv)

	# Initialise all GIOP transport protocols.
	self.__protocols = self.__initialise_protocols()

	# A dictionary containing the names and references of the available
	# object services. Note that (currently) we do not have to lock this
	# dictionary as it only written to in *this* constructor.  All other
	# accesses are read-only.
	self.__services = {} # {Identifier : ObjectReference}

	# Initialise the available object services.
	#
	# The only two reserved COS ids are 'InterfaceRepository' and 
	# 'NameService' so these are the only two services that we attempt
	# to initialise (it is assumed that, given a naming service,
	# application objects can find any other required services/objects).
	#
	# Naming service.
	ns = self.__initialise_naming_service()
 	if ns is not None:
 	    self.__services['NameService'] = ns

 	# Interface repository.
  	ir = self.__initialise_interface_repository()
  	if ir is not None:
  	    self.__services['InterfaceRepository'] = ir

	return

    #########################################################################
    # CORBA ORB interface.
    #########################################################################

    def object_to_string(self, object):
	""" Create a stringified object reference. """

	if object is not None:
	    # Get the IOR from the object reference.
	    ior = object._fnorb_ior()

	else:
	    # Create an IOR with no profiles.
	    ior = IOP.IOR()

	# Stringify it!
	return ior._fnorb_to_string()

    def string_to_object(self, stringified_ior):
	""" Convert a stringified object reference into a live object!
	
	Note that this call does NOT attempt to connect to the remote object
	and it will succeed even if the object reference is 'stale' i.e even if
	the remote object no longer exists.

	"""
	# This contains a subtle Fnorb-ism as it allows for UOL's in the
	# string!
	return self.__resolve_uol(stringified_ior)

    def list_initial_services(self):
	""" List the names of the available object services. """

	# Only return the names of the services that are 'up and running'!
	up_and_running = []
	for identifier in self.__services.keys():
	    # Get the service's object reference.
	    service = self.__services[identifier]

 	    # To find out if the object reference is still valid we have to
 	    # invoke an operation on it. The '_is_a' operation is pretty
	    # harmless, so we do that!
 	    try:
		# We don't care about the result!
 		service._is_a('IDL:omg.org/CORBA/Object:1.0')

		# The call succeeded, so add the name of the service to the
		# list.
		up_and_running.append(identifier)

 	    except SystemException:
		pass

	return up_and_running

    def resolve_initial_references(self, identifier):
	""" Return a reference to the object service named by 'identifier'. """

	try:
	    service = self.__services[identifier]

 	    # To find out if the object reference is still valid we have to
 	    # invoke an operation on it. The '_is_a' operation is pretty
	    # harmless, so we do that!
	    #
	    # We don't care about the result!
	    service._is_a('IDL:omg.org/CORBA/Object:1.0')

	# We get a 'KeyError' if a service with the name 'identifier' is
	# not present, and a 'SystemException' if the service's object
	# reference is 'stale'.
	except (KeyError, SystemException):
	    raise ORB.InvalidName()

	return service

    def nil(self):
	""" Generate a NIL object reference. """

	return None

    #########################################################################
    # CORBA TypeCode factory interface.
    #########################################################################
    
    def create_struct_tc(self, id, name, members):
	return StructTypeCode(id, name, members)

    def create_union_tc(self, id, name, discriminator_type, members):
	return UnionTypeCode(id, name, discriminator_type, members)

    def create_enum_tc(self, id, name, members):
	return EnumTypeCode(id, name, members)

    def create_alias_tc(self, id, name, original_type):
	return AliasTypeCode(id, name, original_type)

    def create_exception_tc(self, id, name, members):
	return ExceptionTypeCode(id, name, members)

    def create_interface_tc(self, id, name):
	return InterfaceTypeCode(id, name)

    def create_string_tc(self, bound):
	return StringTypeCode(bound)

    def create_wstring_tc(self, bound):
	return WstringTypeCode(bound)

    def create_fixed_tc(self, digits, scale):
	return FixedTypeCode(digits, scale)
    
    def create_sequence_tc(self, bound, element_type):
	return SequenceTypeCode(bound, element_type)

    def create_recursive_sequence_tc(self, bound, offset):
	return RecursiveSequenceTypeCode(bound, offset)

    def create_array_tc(self, length, element_type):
	return ArrayTypeCode(length, element_type)

    #########################################################################
    # CORBA DII interface.
    #########################################################################

    def send_multiple_requests(self, requests):
	""" Send multiple requests. """

	# Add the requests to our list of those pending.
	#
	# fixme: If the client never actually gets the replies, this list will
	# grow and grow!!
	self.__lk.acquire()
	self.__pending = self.__pending + requests
	self.__lk.release()

	# Send each request.
	for request in requests:
	    request.send()

	return

    def send_multiple_requests_oneway(self, requests):
	""" Send multiple requests as 'oneway' operations. """

	# Send each request 'oneway'.
	for request in requests:
	    request.send_oneway()

	return

    def poll_next_response(self):
	""" Return true if there is at least one response waiting. """

	self.__lk.acquire()
	for request in self.__pending:
	    if request.poll_response():
		result = 1
		break

	else:
	    result = 0
	self.__lk.release()

	return result

    def get_next_response(self):
	""" Get the next DII response. """

	self.__lk.acquire()
	got_response = 0
	while not got_response:
	    for i in range(len(self.__pending)):
		if self.__pending[i].poll_response():
		    result = self.__pending[i]
		    got_response = 1
		    break

	# Delete the request from the pending queue.
	del self.__pending[i]

	self.__lk.release()
	return result

    #########################################################################
    # Fnorb-specific interface.
    #########################################################################

    def _fnorb_register_object_adapter(self, object_adapter):
	""" Register an Object Adapter. """

	self.__object_adapter = object_adapter
	return

    def _fnorb_object_adapter(self):
	""" Return the object adapter. """

	return self.__object_adapter

    def _fnorb_threading_model(self):
	""" Return the threading model being used by the ORB. """

	self.__lk.acquire()
	if self.__options['Threading Model'] == 'Threaded':
	    threading_model = Util.THREADED

	else:
	    threading_model = Util.REACTIVE
	self.__lk.release()

	return threading_model

    def _fnorb_thread_pool_size(self):
	""" Return the size of the thread pool. """

	self.__lk.acquire()
	thread_pool_size = eval(self.__options['Thread Pool Size'])
	self.__lk.release()

	return thread_pool_size

    def _fnorb_protocols(self):
	""" Return the list of the GIOP transport protocols. """

	# Return a copy of the list so that it cannot be modified!
	return self.__protocols[:]

    def _fnorb_register_type(self, old, new):
	""" Register a new type. """

	# Get a reference to the Type Manager.
	tm = TypeManager.TypeManager_init()

	# Get the Interface Repository id of the old type.
	ifr_id = tm.get_ifr_id(old)

	# Get the typecode constant of the old type
	typecode_constant = tm.get_typecode_constant(ifr_id)

	# Register the new type.
	tm.add_type(ifr_id, typecode_constant, new)

	return

    # This method is used by the BOA and the TypeCode modules.
    def _fnorb_ior_to_object(self, ior):
	""" Convert an IOR instance into a live object! """

	# If the IOR has an empty list of profiles then it is a NIL object
	# reference!
	if len(ior.profiles) == 0:
	    object = None

	else:
	    # If the IOR references an object in *this* ORB, then we create
	    # a 'local' object reference.  Local object references are more
	    # efficient as they bypass the usual marshalling and unmarshalling
	    # stages of operation invocations.
	    if self.__is_local_object(ior):
		object = LocalObject(ior)

	    # Otherwise, the IOR references a 'remote' object.
	    else:
		# Get a reference to the type manager.
		tm = TypeManager.TypeManager_init()

		# Find the Python stub class that implements the interface.
		klass = tm.get_python_object(ior.type_id)
		
		# If the Python class was found then create an instance of it!
		if klass is not None:
		    object = apply(klass, (ior,))

		# Otherwise, we create an instance of the generic type,
		# 'CORBA.Object'.  Operations can still be invoked on the
		# object via the DII.
		#
		# This is used extensively in applications such as the Naming
		# Service where IOR's must be sent and received without the
		# application having access to the stubs.
		else:
		    object = Object(ior)

	return object

    def _fnorb_override_options(self, options):
	""" Override ORB options. """
	
	self.__lk.acquire()
	self.__options.update(options)
	options = self.__options.copy()
	self.__lk.release()

	return options

    def _fnorb_options(self):
	""" Return the ORB options in a dictionary. """

	self.__lk.acquire()
	options = self.__options.copy()
	self.__lk.release()

	return options

    #########################################################################
    # Private interface.
    #########################################################################

    def __get_options(self, argv):
	""" Get any ORB options.

	ORB options can be specified via a configuration file, environment
	variables, or the command-line (in order of increasing precedence).

	"""
	# Start with a dictionary of required default options.
	options = {'Threading Model': 'Reactive', 'Thread Pool Size': '10'}

	# Get any options specified on the command line.
	command_line = self.__get_command_line_options(argv)

	# Get any options specified via environment variables.
	environment = self.__get_environment_options()

	# Does the command line contain the name of a configuration file?
	if command_line.has_key('ConfigFile'):
	    # Read the options from the configuration file.
	    config_file = self.__get_config_options(command_line['ConfigFile'])

	# How about the environment?
	elif environment.has_key('ConfigFile'):
	    # Read the options from the configuration file.
	    config_file = self.__get_config_options(environment['ConfigFile'])

	# Otherwise, there is no configuration file.
	else:
	    config_file = {}

	# Now lets put them all together, starting with least precedence.
	options.update(config_file)
	options.update(environment)
	options.update(command_line)

	return options

    def __get_command_line_options(self, argv):
	""" Get any options specified on the command line. """

	# Work on a copy of the command line arguments.
	args = argv[:]
	
	# Command line arguments used by the ORB are stripped from the list.
	# We accomplish this by first emptying the list and then putting back
	# any non-ORB arguments!
	#
	# Empty the list without the destroying the list object itself.
	del argv[:]

	# For convenience we return any ORB options in a dictionary.
	options = {}

	i = 0
	while i < len(args):
	    arg = args[i]

	    if regex.match('--ORBconfig-file=.*', arg) != -1:
		value = arg[string.find(arg, '=') + 1:]
		options['ConfigFile'] = value

	    elif regex.match('--ORBinterface-repository=.*', arg) != -1:
		value = arg[string.find(arg, '=') + 1:]
		options['Interface Repository'] = value

	    elif regex.match('--ORBnaming-service=.*', arg) != -1:
		value = arg[string.find(arg, '=') + 1:]
		options['Naming Service'] = value

	    elif regex.match('--ORBthreading-model=.*', arg) != -1:
		value = arg[string.find(arg, '=') + 1:]
		options['Threading Model'] = value

	    elif regex.match('--ORBthread-pool-size=.*', arg) != -1:
		value = arg[string.find(arg, '=') + 1:]
		options['Thread Pool Size'] = value

	    else:
		argv.append(arg)

	    i = i + 1

	return options

    def __get_environment_options(self):
	""" Get any options specified via environment variables. """

	options = {}

	if os.environ.has_key('FNORB_CONFIG_FILE'):
	    options['ConfigFile'] = os.environ['FNORB_CONFIG_FILE']

	if os.environ.has_key('FNORB_INTERFACE_REPOSITORY'):
	    options['Interface Repository'] = os.environ['FNORB_INTERFACE_REPOSITORY']
	if os.environ.has_key('FNORB_NAMING_SERVICE'):
	    options['Naming Service'] = os.environ['FNORB_NAMING_SERVICE']

	if os.environ.has_key('FNORB_THREADING_MODEL'):
	    options['Threading Model'] = os.environ['FNORB_THREADING_MODEL']

	if os.environ.has_key('FNORB_THREAD_POOL_SIZE'):
	    options['Thread Pool Size'] = os.environ['FNORB_THREAD_POOL_SIZE']

	return options

    def __get_config_options(self, filename):
	""" Get any options specified via a configuration file. """

	options = {}
	try:
	    # Read the configuration file as lines of text.
	    lines = open(filename).readlines()

	    for line in lines:
		# If the line starts with a hash ('#') then it is a comment.
		if line[0] != '#':
		    # Find the position of the first colon (':').
		    colon = string.find(line, ':')

		    # If there is no colon then just ignore the line.
		    if colon != -1:
			# Get the key and value.
			key = string.strip(line[:colon])
			value = string.strip(line[colon+1:])

			# Add them to the dictionary.
			options[key] = value

	# We get an IOError if the file doesn't exist or is not readable!
	except IOError:
	    pass

	return options

    def __initialise_protocols(self):
	""" Initialise the GIOP transport protocols. """

	# fixme: Currently only IIOP!
	modules = ['IIOPProtocol']

	protocols = []
	for protocol in modules:
	    try:
		# Import the module.
		exec('from Fnorb.orb import %s' % protocol)

		# Find the class that implements the protocol (derived from the
		# base class 'Protocol.'Protocol').
		klass = eval('%s.%s' % (protocol, protocol))

		# Add the protocol to the list of protocols available to the
		# ORB.
		protocols.append(apply(klass, ()))

	    # If anything goes wrong with the import then just ignore the
	    # protocol.
	    except:
		pass

	# If there are no protocols then we can't do much!
	assert(len(protocols) > 0)

	return protocols

    def __initialise_interface_repository(self):
	""" Initialise the interface repository! """

	# Get the UOL for the interface repository.
	if self.__options.has_key('Interface Repository'):
	    # Resolve the UOL to create an active object reference for the
	    # interface repository.
	    ir = self.__resolve_uol(self.__options['Interface Repository'])

	# No UOL for the interface repository 8^(
	else:
	    # If there's no UOL then there sure as hell ain't no interface
	    # repository!
	    ir = None

	return ir

    def __initialise_naming_service(self):
	""" Initialise the naming service! """

	# fixme: We do the import here to get around the old circular import
	# problems!
	from Fnorb.cos.naming import CosNaming

	# Get the UOL for the naming service.
	if self.__options.has_key('Naming Service'):
	    # Resolve the UOL to create an active object reference for the
	    # naming service.
	    ns = self.__resolve_uol(self.__options['Naming Service'], 1)

	# No UOL for the naming service 8^(
	else:
	    # If there's no UOL then there sure as hell ain't no naming
	    # service!
	    ns = None

	return ns

    def __resolve_uol(self, uol, bootstrap=0):
	""" Resolve a UOL to produce a 'live' object reference.

	UOL's can currently be either:-
	
	1) 'file:/the/pathname/of/a/file/containing/a/UOL'
	2) 'IOR:a stringified object reference'
	3) 'http://a/URL/pointing/to/a/text/document/containing/a/UOL'
	4) 'name:/a/path/through/the/naming/service'

	Naming service names are not allowed if this is a 'boostrap' UOL (ie.
	if we are resolving the UOL of the naming service!).

	"""
	# Stringified IOR.
	if string.lower(uol[:4]) == 'ior:':
	    # Convert the stringified IOR into an IOR instance.
	    ior = new.instance(IOP.IOR, {})
	    ior._fnorb_from_string(uol)

	    # Create an object reference from the IOR.
	    #
	    # Note that the call to '_fnorb_ior_to_object' does not attempt to
	    # connect to the remote object,  and it will succeed even if the
	    # object reference is 'stale' i.e even if the remote object no
	    # longer exists.
	    object = self._fnorb_ior_to_object(ior)

	# Filename.
	elif string.lower(uol[:5]) == 'file:':
	    try:
		file = open(uol[5:])
		uol = string.strip(file.read())
		file.close()

		# Recursively resolve the UOL.
		object = self.__resolve_uol(uol, bootstrap)

	    # We get an IOError if the file doesn't exist or is not readable!
	    except IOError:
		object = None

	# URL.
	elif string.lower(uol[:5]) == 'http:':
	    # Do the import here 'cos I'm not sure how much this will actually
	    # be used.
	    import urllib
	    try:
		file = urllib.urlopen(uol)
		uol = string.strip(file.read())
		file.close()

		# Recursively resolve the UOL.
		object = self.__resolve_uol(uol, bootstrap)

	    except:
		object = None

	# Naming service name.
	elif string.lower(uol[:5]) == 'name:' and not bootstrap:
	    # Get a reference to the initial naming service context.
	    ctx = self.resolve_initial_references("NameService")

	    # Create a naming service name from the string representation.
	    #
	    # fixme: This only works for names that contain components that
	    # do not use the 'kind' field (let's face it the 'kind' field
	    # was a crap idea anyway ;^)
	    #
	    # fixme: This should now be implemented as per the extended naming
	    # service specification.
	    name = Util._fnorb_string_to_name(uol)
	    
	    # Lookup the name.
	    object = ctx.resolve(name)

	else:
	    object = None

	return object

    def __is_local_object(self, ior):
	""" Does the IOR reference an object in *this* ORB? """

	# Does the ORB have an object adapter initialised? (ie. are there any
	# servants in this process?).
	#
	# fixme: Multiple object adapters?
	if self.__object_adapter is not None:
	    # If so then we ask each protocol to examine the IOR and see if
	    # they think that the servant is in this process.
	    for protocol in self.__protocols:
		if protocol.is_local(ior):
		    result = 1
		    break

	    else:
		result = 0

	# If there is no object adaptor then there sure as hell ain't no
	# servants!
	else:
	    result = 0
		
	return result


class Object:
    """ CORBA Object References (implementation of the Object interface). """

    # Repository id attribute.
    _FNORB_ID = 'IDL:omg.org/CORBA/Object:1.0'

    def __init__(self, ior):
	""" Constructor.

	'ior' is the IOR used to construct the object reference.

	"""
	self._ior = ior

	# If the IOR has an empty list of profiles then it is a NIL object
	# reference!
	if len(ior.profiles) == 0:
	    self.__is_nil = 1

	else:
	    # We can only use the object if it lives in an ORB that supports
	    # IIOP!  If the object reference does NOT contain an IIOP profile,
	    # then this call will raise an 'INV_OBJREF' exception.
	    self.__iiop_profile = ior._fnorb_iiop_profile()

	    # Get a reference to the GIOP Client Manager.
	    gcm = GIOPClientManager.GIOPClientManager_init()

	    # Get a GIOP client that is connected to the remote object.
	    self.__client = gcm.get_client(ior)
	    self.__is_nil = 0

	# The following flag indicates whether or not we have received a
	# 'LOCATION_FORWARD' reply from the remote object.
	self.__forwarded = 0

	# Mutex to make access to the object reference thread-safe (this is a
	# 'protected' attribute as it is used by the derived class
	# 'LocalObject').
	self._lk = fnorb_thread.allocate_lock()

	return

    def __del__(self):
	""" Destructor. """
	
	if not self.__is_nil:
	    # Get a reference to the GIOP Client Manager.
	    gcm = GIOPClientManager.GIOPClientManager_init()

	    # Let the Connection Manager know that we have finished with the
	    # GIOP client (GIOP clients are reference counted by the
	    # Connection Manager so that they can be cleaned up when
	    # necessary).
	    gcm.delete_client(self._ior)

	return

    #########################################################################
    # Client-side CORBA Object interface.
    #########################################################################

    def _is_nil(self):
	""" True if the object reference is nil, otherwise false.

	A Nil object reference contains an empty set of profiles!

	"""
	return self.__is_nil

    def _hash(self):
	""" Return a hash value for the object reference. """

	self._lk.acquire()
	if self.__is_nil:
	    hash_value = hash(None)

	else:
	    hash_value = abs(hash(self._ior))
	self._lk.release()

	return hash_value

    def _create_request(self, operation, inputs, outputs, exceptions, **kw):
	""" Create a DII Request. """

	return apply(Request,
		     (self, operation, inputs, outputs, exceptions), kw)

    def _narrow(self, klass):
	""" Narrow the object reference to the specified interface type. """

	# fixme: This assumes the interface stub is loaded. Not a bad
	# assumption!
	# checks self._ior.type_id before trying the (possibly remote)
	# _is_a(). Work around for interoperability with ORBs that do not
	# support _is_a().
	if self._ior.type_id == klass._FNORB_ID or self._is_a(klass._FNORB_ID):
	    # Create a new IOR containing the specified interface type.
	    self._lk.acquire()
	    result = klass(IOP.IOR(klass._FNORB_ID, self._ior.profiles))
	    self._lk.release()

	else:
	    result = None

	return result
	    
    #########################################################################
    # Server-side CORBA Object interface.
    #########################################################################

    def _is_a(self, *args, **kw):
        """ Operation: IDL:omg.org/CORBA/Object/is_a:1.0 """

        # Typecodes for 'in' and 'inout' parameters.
        inputs = [CORBA.TC_string]

        # Typecodes for the result, 'inout' and 'out' parameters.
        outputs = [CORBA.TC_boolean]

        # Create a request object.
        request = self._create_request("_is_a", inputs, outputs, [])

        # Make the request!
        apply(request.invoke, args, kw)

        # Return the results.
        return request.results()

    def _non_existent(self, *args, **kw):
        """ Operation: IDL:omg.org/CORBA/Object/non_existent:1.0 """

        # Typecodes for the result, 'inout' and 'out' parameters.
        outputs = [CORBA.TC_boolean]

        # Create a request object.
        request = self._create_request("_non_existent", [], outputs, [])

        # Make the request!
        apply(request.invoke, args, kw)

        # Return the results.
        return request.results()

    def _interface(self, *args, **kw):
        """ Operation: IDL:omg.org/CORBA/Object/_interface:1.0 """

        # Typecodes for the result, 'inout' and 'out' parameters.
        outputs = [CORBA.typecode("IDL:omg.org/CORBA/InterfaceDef:1.0")]

        # Create a request object.
        request = self._create_request("_interface", [], outputs, [])

        # Make the request!
        apply(request.invoke, args, kw)

        # Return the results.
        return request.results()

    #########################################################################
    # Non-CORBA interface.
    #########################################################################

    def _fnorb_ior(self):
	""" Return the IOR of the object reference. """

	self._lk.acquire()
	ior = self._ior
	self._lk.release()

	return ior

    def _fnorb_client(self):
	""" Return the IIOP client that is connected to the remote object. """

	self._lk.acquire()
	client = self.__client
	self._lk.release()

	return client

    def _fnorb_forwarded(self):
	""" Have we received a 'LOCATION_FORWARD' reply? """

	self._lk.acquire()
	forwarded = self.__forwarded
	self._lk.release()

	return forwarded

    def _fnorb_forward(self, ior):
	""" 'Forward' the object reference to the specified IOR. """

	self._lk.acquire()
	self.__forwarded = 1

	# Get a reference to the GIOP Client Manager.
	gcm = GIOPClientManager.GIOPClientManager_init()

	# Remove old client, update object
	gcm.delete_client(self._ior)
	self._ior = ior

	# Get a GIOP client that is connected to the forwarded address.
	self.__client = gcm.get_client(ior)
	self._lk.release()

	return

    def _fnorb_unforward(self):
	""" 'Unforward' the object back to its original IOR!

	This method is called if the connection to the forwarded object fails.

	"""
	self._lk.acquire()
	self.__forwarded = 0

	# Get a reference to the GIOP Client Manager.
	gcm = GIOPClientManager.GIOPClientManager_init()

	# Get a GIOP client that is connected to the original address.
	self.__client = gcm.get_client(self._ior)
	self._lk.release()

	return

    def _fnorb_marshal(self, cursor):
	""" Marshal the object reference onto an octet stream. """

	self._ior._fnorb_marshal(cursor)
	return

    #########################################################################
    # Convenience methods for getting at the object's IIOP profile!
    #########################################################################

    def _fnorb_iiop_profile(self):
	""" Return the IIOP profile from the object's IOR.

	This is a convenience/performance function to save DII routines
	having to scan the IOR every time.

	"""
	# A NIL object reference doesn't even have any profiles!
	if self.__is_nil:
	    raise INV_OBJREF() # System exception.

	return self.__iiop_profile

    def _fnorb_host(self):
	""" Return the hostname contained in the object's IIOP profile. """

	# A NIL object reference doesn't even have any profiles!
	if self.__is_nil:
	    raise INV_OBJREF() # System exception.

	return self.__iiop_profile.host

    def _fnorb_port(self):
	""" Return the port number contained in the object's IIOP profile. """

	# A NIL object reference doesn't even have any profiles!
	if self.__is_nil:
	    raise INV_OBJREF() # System exception.

	return self.__iiop_profile.port

    def _fnorb_object_key(self):
	""" Return the object key contained in the object's IIOP profile. """

	# A NIL object reference doesn't even have any profiles!
	if self.__is_nil:
	    raise INV_OBJREF() # System exception.

	return self.__iiop_profile.object_key


class LocalObject(Object):
    """ CORBA Object References for local objects (ie. in *this* ORB). """

    def __init__(self, ior):
	""" Constructor.

	'ior' is the IOR used to construct the object reference.

	"""
	self._ior = ior

	# Set the '_FNORB_ID' as for stub classes.
	self._FNORB_ID = self._ior.type_id

	# We can only use the object if it lives in an ORB that supports IIOP!
	# If the object reference does NOT contain an IIOP profile, then this
	# call will raise an 'INV_OBJREF' exception.
	self._iiop_profile = ior._fnorb_iiop_profile()

	# Local object references CANNOT be nil!
	self.__is_nil = 0

	# Mutex to make access to the object reference thread-safe.
	#
	# This is a 'protected' attribute as it is used by methods in the
	# base class 'Object'.
	self._lk = fnorb_thread.allocate_lock()

	return

    def __del__(self):
	""" Destructor.

	Override the destructor in the base class.

	"""
	pass

    def __getattr__(self, name):
	""" Pass method invocations onto the local implementation. """

	# Get a reference to the BOA.
	boa = BOA.BOA_init()
	
	# Lookup the implementation.
	impl = boa._fnorb_get_implementation(self._iiop_profile.object_key)

	# Lookup the attribute on the implementation.
	return getattr(impl, name)

    #########################################################################
    # Client-side CORBA Object interface.
    #########################################################################

    def _create_request(self, operation, inputs, outputs, exceptions, **kw):
 	""" Create a local DII Request. """

 	return apply(LocalRequest,
 		     (self, operation, inputs, outputs, exceptions), kw)

    #########################################################################
    # Server-side CORBA interface.
    #########################################################################

    def _is_a(self, logical_type_id):
	""" Return true if the object reference is of the specified type. """

	# Get a reference to the BOA.
	boa = BOA.BOA_init()
	
	# Lookup the implementation.
	impl = boa._fnorb_get_implementation(self._iiop_profile.object_key)

	return self.__is_a(impl.__class__, logical_type_id)

    def _non_existent(self):
	""" Return true if the object has been destroyed. """

	# Get a reference to the BOA.
	boa = BOA.BOA_init()
	
	return not boa._fnorb_object_here(self._iiop_profile.object_key)

    def _interface(self):
	""" Return the repository object that defines the interface.

	The ORB must have access to an Interface Repository for this method
	to succeed.

	"""
	# Get a reference to the BOA.
	boa = BOA.BOA_init()
	
	# Lookup the implementation.
	impl = boa._fnorb_get_implementation(self._iiop_profile.object_key)

	# Get a reference to the ORB that we are living in.
	orb = ORB_init()

	# Get a reference to the Interface Repository.
	ir = orb.resolve_initial_references('InterfaceRepository')

	# Lookup our interface id to get the interface definition object.
	return ir.lookup_id(impl._FNORB_ID)

    #########################################################################
    # Private interface.
    #########################################################################

    def __is_a(self, klass, logical_type_id):
	""" Return true if the class is an implementation of the type. """

	if klass._FNORB_ID == logical_type_id:
	    result = 1

	else:
	    for base in klass.__bases__:
		if self.__is_a(base, logical_type_id):
		    result = 1
		    break

	    else:
		result = 0

	return result


class Object_skel:
    """ CORBA Object Skeletons. """

    # Repository id attribute.
    _FNORB_ID = 'IDL:omg.org/CORBA/Object:1.0'

    def __init__(self):
  	""" Constructor.

	Normally, skeleton implementations are not required to call this
	constructor as initialisation is forced via '__getattr__'. However, if
	the implementation overrides '__getattr__' it must call this
	constructor as part of its own initialisation.

	"""

  	self.__initialise()
  	return

    def __getattr__(self, name):
 	""" Trap the first attribute access to force initialisation! """

	# Force initialisation.
	#
	# fixme: Is this thread-safe - I think not!!!!!!!!!!
 	if not self.__dict__.has_key('_initialised'):
 	    self.__initialise()

	# Provide the usual 'getattr' semantics!
 	if not self.__dict__.has_key(name):
 	    raise AttributeError, name

 	return self.__dict__[name]

    #########################################################################
    # Client-side CORBA Object interface.
    #########################################################################

    def _is_nil(self):
	""" True if the object reference is nil, otherwise false.

	Implementations are never nil!

	"""
	return 0

    #########################################################################
    # Server-side CORBA interface.
    #########################################################################

    def _is_a(self, logical_type_id):
	""" Return true if the object reference is of the specified type. """

	return self.__is_a(self.__class__, logical_type_id)

    def _interface(self):
	""" Return the repository object that defines the interface.

	The ORB must have access to an Interface Repository for this method
	to succeed.

	"""
	# Get a reference to the ORB that we are living in.
	orb = ORB_init()

	# Get a reference to the Interface Repository.
	ir = orb.resolve_initial_references('InterfaceRepository')

	# Lookup our interface id to get the interface definition object.
	return ir.lookup_id(self._FNORB_ID)

    #########################################################################
    # Skeleton methods for the server-side CORBA interface.
    #########################################################################

    def _skel__is_a(self, server_request):
        """ Operation: IDL:omg.org/CORBA/Object/_is_a:1.0 """

        # Typecodes for 'in' and 'inout' parameters.
        inputs = [CORBA.TC_string]

        # Typecodes for the result, 'inout' and 'out' parameters.
        outputs = [CORBA.TC_boolean]

        # Initialise the server request object.
        server_request.initialise(inputs, outputs, [])

        # Unmarshal the arguments to the request.
        arguments = server_request.arguments()

        # Invoke the implementation.
        results = apply(self._is_a, arguments)

        # Create the reply.
        server_request.results(results)

        return

    def _skel__interface(self, server_request):
        """ Operation: IDL:omg.org/CORBA/Object/_interface:1.0 """

        # Typecodes for the result, 'inout' and 'out' parameters.
        outputs = [CORBA.typecode("IDL:omg.org/CORBA/InterfaceDef:1.0")]

        # Initialise the server request object.
        server_request.initialise([], outputs, [])

        # Invoke the implementation.
        results = apply(self._interface, ())

        # Create the reply.
        server_request.results(results)

        return

    #########################################################################
    # Fnorb-specific interface.
    #########################################################################

    def _fnorb_ior(self):
 	""" Return the IOR for the current thread. """

	# Get the object key appropriate for the current thread.
	object_key = self._fnorb_object_key()

	# Create an IOR using the object key and the object's interface
	# repository id.
	boa = BOA.BOA_init()
	ior = boa._fnorb_create_ior(object_key, self._FNORB_ID)

 	return ior

    def _fnorb_object_key(self):
	""" Return the object key for the current thread. """

	# Get the object key appropriate for the current thread.
	self.__lk.acquire()
	try:
	    try:
		object_key = self.__object_keys[fnorb_thread.get_ident()]

	    # fixme: Should we pick *any* of our object keys here?
	    except KeyError:
		raise "No object key for this thread!"

	    return object_key

	finally:
	    self.__lk.release()

    def _fnorb_add_object_key(self, object_key):
	""" Add the object key to our key table. """

	self.__lk.acquire()
	self.__object_keys[fnorb_thread.get_ident()] = object_key
	self.__lk.release()

	return

    def _fnorb_delete_object_key(self):
	""" Delete the object key for the current thread. """

	self.__lk.acquire()
	try:
	    del self.__object_keys[fnorb_thread.get_ident()]

	except KeyError:
	    pass
	self.__lk.release()

	return

    def _fnorb_marshal(self, cursor):
	""" Marshal the object reference onto an octet stream. """
	
	# Create the appropriate IOR for the current calling context.
	ior = self._fnorb_ior()

	# Marshal the IOR onto the octet stream.
	ior._fnorb_marshal(cursor)

	return

    #########################################################################
    # Private interface.
    #########################################################################

    def __initialise(self):
	""" (Pseudo) Constructor! """

	# A single implementation instance can be referred to by many
	# different object references (ie. by many different object keys).
	# Therefore, to determine the object key for any individual operation
	# invocation, we store each object key in a dictionary keyed on
	# thread id. In non-threaded environments, there will obviously only
	# be at most one entry in the dictionary.
	self.__object_keys = {} # {ThreadId: ObjectKey}

	# A mutex to make access to the object key dictionary thread-safe.
	self.__lk = fnorb_thread.allocate_lock()

	# A flag to indicate that we have been initialised!
	self._initialised = 1

	return

    def __is_a(self, klass, logical_type_id):
	""" Return true if the class is an implementation of the type. """

	# We check for the existence of the '_FNORB_ID' attribute first to
	# allow implementations to multiply inherit from classes that are
	# nothing to do with Fnorb.
	if hasattr(klass, '_FNORB_ID') and klass._FNORB_ID == logical_type_id:
	    result = 1

	else:
	    for base in klass.__bases__:
		if self.__is_a(base, logical_type_id):
		    result = 1
		    break

	    else:
		result = 0

	return result


# Standard/built-in modules.
import os, regex, string

# Python 1.5.1 and early didn't build new by default
try:
    import new
except ImportError:
    sys.stderr.write("Fnorb: failed to import 'new' module.\n\n")
    sys.stderr.write("       The new module is not built by default in Python 1.5.1 and\n")
    sys.stderr.write("       earlier.  You should either upgrade to Python 1.5.2 (or later)\n")
    sys.stderr.write("       or compile the 'new' extension module.\n\n")
    sys.exit(1)

# Fnorb modules.
import BOA, GIOPClientManager, IOP, TypeManager

# The following modules are defined in the spec. as being part of the CORBA
# module.
from   DII                 import *
from   DSI                 import *
from   TypeCode            import *

# The following modules also defined in the spec. as being part of the CORBA
# module, but they rely on definitions in the TypeCode module.
from   Any                 import *
from   InterfaceRepository import *

#############################################################################
