#!/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/IIOPConnection.py,v $
# Version:      @(#)$RCSfile: IIOPConnection.py,v $ $Revision: 1.8 $
#
#############################################################################
""" IIOPConnection class. """


# Standard/built-in modules.
import select, socket, struct

# Fnorb modules.
import CORBA, Connection


class IIOPConnection(Connection.Connection):
    """ IIOPConnection class. """

    def __init__(self, _socket=None):
	""" Provide an IIOP connection to a remote object!

	'_socket' is a socket that is *already* connected.

	"""
	# If we are given a "here's one I prepared earlier" socket, then we
	# use that.
	#
	if _socket is not None:
	    self.__socket = _socket
	    self.__connected = 1

	    # The 'TCP_NODELAY' option disables the TCP Nagle algorithm which
	    # prevents the transmission of small packets (like for example
	    # 'CloseConnection' messages).
	    #
	    # fixme: We don't use symbolic constants from the 'socket' module
	    # as they don't appear to be present on all platforms (eg. Linux!).
	    # Is this just an ommision or is there a reason?!?
	    self.__socket.setsockopt(6, # IPPROTO_TCP
				     1, # TCP_NODELAY
				     1) # Option on!

	# Otherwise, we will create a socket when the 'connect' method is
	# called.
	else:
	    self.__connected = 0

	return

    #########################################################################
    # Connection interface.
    #########################################################################

    def connect(self, address):
	""" Connect to the remote object. """

	# fixme: Need a better address abstraction than just tuples.
	(host, port) = address

	# Create a socket and connect to the remote object.
	try:
	    self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	    self.__socket.connect((host, port))

	    # Set the socket by default to NON-blocking mode.
	    self.__socket.setblocking(0)

	    # Connected!
	    self.__connected = 1

	except socket.error:
	    raise CORBA.COMM_FAILURE() # System exception.

	return

    def disconnect(self):
	""" Disconnect from the remote server. """

	# Make sure that we are actually connected!
	if self.__connected:
	    try:
		# Close the connection.
		self.__socket.close()

	    except socket.error:
		pass

	    # Disconnected.
	    self.__connected = 0

	return

    def send(self, data, block=0):
	""" Send as much of the data as we can. """

	try:
	    n = self.__socket.send(data)

	except socket.error:
	    raise CORBA.COMM_FAILURE(0, CORBA.COMPLETED_MAYBE)

	return n

    def recv(self, n):
	""" Receive at most 'n' bytes of data. """
	
	try:
	    # Block until there is some data present on the socket.
	    #
	    # We do a 'select' instead of a blocking read, because 'select'
	    # can be interrupted by closing the socket. This is useful in
	    # the presence of threads when we need to shut down the read
	    # thread.
	    (iwtd, owtd, ewtd) = select.select([self.__socket], [], [])

	    # Read the data.
	    data = self.__socket.recv(n)

	# fixme: The 'AttributeError' is caught because it seems that on
	# Windows platforms this exception is raised when the socket has been
	# closed by the peer?
	except (select.error, socket.error, ValueError, AttributeError):
	    raise CORBA.COMM_FAILURE(0, CORBA.COMPLETED_MAYBE)
	
	return data

    def is_connected(self):
	""" Are we connected to the remote address? """

	return self.__connected

    def blocking(self, blocking):
	""" Set the connection to blocking (1) or non-blocking (0). """

	self.__socket.setblocking(blocking)
	return

    def handle(self):
	""" Return my underlying I/O handle.

	In this case, my I/O handle is the file descriptor of my socket. 

	"""
	return self.__socket.fileno()

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