#!/usr/bin/env python
#############################################################################
# Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1997, 1998, 1999
# 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/DII.py,v $
# Version:      @(#)$RCSfile: DII.py,v $ $Revision: 1.14 $
#
#############################################################################
""" Implementation of the Dynamic Invocation Interface (DII). """


class Request:
    """ Implementation of the PIDL Request interface.

    Request objects are not thread-safe. This is not a problem as it really
    only makes sense to handle requests from within a single thread. If you
    are trying to do otherwise I suggest you take a step back and have a good,
    long, hard look at yourself ;^)

    """
    def __init__(self, object, operation, inputs, outputs, exceptions, **kw):
	""" Constructor. """

	self.__object = object
	self.__operation = operation
	self.__inputs = inputs
	self.__outputs = outputs
	self.__exceptions = exceptions

	# Keyword arguments (as specified in the language mapping document).
	#
	# The 'flags' keyword argument is currently ignored as it is only
	# used in compiled languages to specify memory management policy.
	try:
	    self.__flags = kw['flags']

	except KeyError:
	    self.__flags = 0

	try:
	    self.__context = kw['context']

	except KeyError:
	    self.__context = []

	# Request arguments can be specified either at invocation time, or via
	# a call to the 'arguments' method.
	self.__arguments = ()

	return

    #########################################################################
    # CORBA Request interface.
    #########################################################################

    def object(self):
	""" Return the object reference that we invoke the request on. """
	
	return self.__object

    def operation(self):
	""" Return the operation name. """

	return self.__operation
    
    def inputs(self):
	""" Return the input typecodes. """

	return self.__inputs

    def outputs(self):
	""" Return the output typecodes. """

	return self.__outputs

    def exceptions(self):
	""" Return the exception typecodes. """

	return self.__exceptions

    def flags(self):
	""" Return the request flags.

	Request flags are currently ignored in Fnorb!

	"""
	return self.__flags

    def context(self):
	""" Return the request context.

	Contexts are currently ignored in Fnorb!

	"""
	return self.__context

    def arguments(self, *args):
	""" Initialise the request arguments. """

	self.__arguments = args
	return

    def results(self):
	""" Return the results of the previous invocation. """

	# fixme: Raise an exception here if we have not been invoked!!!!!!!!!
	return self.__results

##     def invoke(self, *args, **kw):
## 	""" Invoke the request (synchronous). """

## 	apply(self.send, args, kw)
## 	return self.get_response()


    def invoke(self, *args, **kw):
	""" Invoke the request (synchronous). """

	# Request arguments can be specified either at invocation time, or via
	# a call to the 'arguments' method.
	if len(args) == 0:
	    args = self.__arguments

	else:
	    self.__arguments = args

	# Make sure we have the right number of arguments!
	if len(args) != len(self.__inputs):
	    raise TypeError, 'Wrong number of arguments'

	# Get the object's GIOP client.
	client = self.__object._fnorb_client()

	# Ask the client to make the request, and to wait for the reply.
	(forwarded, self.__results) = client.synchronous(self, args)

	# If we have been forwarded/unforwarded then try again.
	if forwarded:
	    # Try again.
	    self.__results = self.invoke()

	# Return the results for convenience.
	return self.__results

    def send(self, *args, **kw):
	""" Invoke the request (deferred synchronous). """

	# Request arguments can be specified either at invocation time, or via
	# a call to the 'arguments' method.
	if len(args) == 0:
	    args = self.__arguments

	else:
	    self.__arguments = args

	# Make sure we have the right number of arguments!
	if len(args) != len(self.__inputs):
	    raise TypeError, 'Wrong number of arguments'

	# Get the object's GIOP client.
	client = self.__object._fnorb_client()

	# Ask the client to make the request.
	self.__request_id = client.deferred(self, args)

	return

    def get_response(self):
	""" Get the response for a deferred request. """

	# Get the object's GIOP client.
	client = self.__object._fnorb_client()

	# Get the reply to the last operation request.
	(forwarded, self.__results) = client.reply(self)

	# If we have been forwarded/unforwarded then try again.
	if forwarded:
	    # Try again.
	    self.__results = self.invoke()

	# Return the results for convenience.
	return self.__results

    def poll_response(self):
	""" Has a reply been received? """

	# Get the object's GIOP client.
	client = self.__object._fnorb_client()

	# Poll for the reply to the last operation request.
	(forwarded, result) = client.poll(self)

	# If we have been forwarded/unforwarded then try again.
	if forwarded:
	    # Try again.
	    self.send()

	return result

    def invoke_oneway(self, *args, **kw):
	""" Invoke the request as 'oneway'. """

	# Request arguments can be specified either at invocation time, or via
	# a call to the 'arguments' method.
	if len(args) == 0:
	    args = self.__arguments

	else:
	    self.__arguments = args

	# Make sure we have the right number of arguments!
	if len(args) != len(self.__inputs):
	    raise TypeError, 'Wrong number of arguments'

	# Get the object's GIOP client.
	client = self.__object._fnorb_client()

	# Ask the client to make the request.
	client.oneway(self, args)

	return

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

    def _fnorb_request_id(self):
	""" Return the request id of the current request. """

	return self.__request_id


class LocalRequest(Request):
    """ Request interface for local requests (ie. in same address space). """

    #########################################################################
    # CORBA Request interface.
    #########################################################################

    def invoke(self, *args):
	""" Invoke the request (synchronous). """

	# Request arguments can be specified either at invocation time, or via
	# a call to the 'arguments' method.
	if len(args) == 0:
	    args = self.__arguments

	# Make sure we have the right number of arguments!
	if len(args) != len(self.__inputs):
	    raise TypeError, 'Wrong number of arguments'
	
	# Find the method on the implementation (via the 'LocalObject'
	# instance).
	method = getattr(self.__object, self.__operation)

	# Invoke the operation!
	self.__results = apply(method, args)

	# Return the results for convenience!
	return self.__results

    def invoke_oneway(self, *args):
	""" Invoke the request as 'oneway'. """

	# Request arguments can be specified either at invocation time, or via
	# a call to the 'arguments' method.
	if len(args) == 0:
	    args = self.__arguments

	# Make sure we have the right number of arguments!
	if len(args) != len(self.__inputs):
	    raise TypeError

	# Find the method on the implementation (via the 'LocalObject'
	# instance).
	method = getattr(self.__object, self.__operation)

	# Invoke the operation and throw away any results!
	apply(method, args)

	return

    def send(self, *args):
	""" Invoke the request (deferred synchronous). """

	# Request arguments can be specified either at invocation time, or via
	# a call to the 'arguments' method.
	if len(args) == 0:
	    args = self.__arguments

	# Make sure we have the right number of arguments!
	if len(args) != len(self.__inputs):
	    raise TypeError

	# Find the method on the implementation (via the 'LocalObject'
	# instance).
	method = getattr(self.__object, self.__operation)

	# Invoke the operation!
	self.__results = apply(method, args)

	return

    def poll_response(self):
	""" Has a reply been received? """

	return 1

    def get_response(self):
	""" Get the response for a deferred request. """

	return self.__results

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