#!/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/IIOPAcceptor.py,v $
# Version:      @(#)$RCSfile: IIOPAcceptor.py,v $ $Revision: 1.10 $
#
#############################################################################
""" IIOPAcceptor (part of the 'Reactor' pattern). """


# Standard/built-in modules.
import socket

# Fnorb modules.
import Acceptor, CORBA, GIOPServer, EventHandler, Reactor

# IIOP protocol modules.
import IIOPConnection


class IIOPAcceptor(Acceptor.Acceptor, EventHandler.EventHandler):
    """ Acceptor (part of the 'Reactor' pattern). """

    def __init__(self, address=None):
	""" Constructor.

	'address'  is the address to use when creating my endpoint.

	"""
	# Unpack the address.
	#
	# fixme: address abstraction!
	if address is None:
	    (host, port) = ('', 0)

	else:
	    (host, port) = address

	try:
	    # Create a socket on which to listen for connection requests.
	    self.__socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	    self.__socket.bind((host, port))
	    self.__socket.listen(5)

	    # Get the host name, the IP address and the port number of the
	    # socket.

	    # Get the fully qualified host name
	    hostname, aliaslist, iplst = socket.gethostbyaddr(socket.gethostname())
	    aliaslist = [hostname] + aliaslist
	    tmphost = ""
	    for currHost in aliaslist:
		for char in list(currHost):
		    if char == ".":
			tmphost = currHost
			break
	    # If none have a period then use the original hostname
	    self.__host = tmphost or hostname or host
	    (self.__ip_address, self.__port) = self.__socket.getsockname()

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

	# Get a reference to the active reactor.
	self.__reactor = Reactor.Reactor_init()

	# Register our interest in read events (ie. connection requests) with
	# the Reactor.
	self.__reactor.register_handler(self, Reactor.READ)

	return

    #########################################################################
    # Acceptor interface.
    #########################################################################

    def address(self):
	""" Return the address of the acceptor's endpoint. """

	# fixme: Address abstraction!
	return (self.__host, self.__port)

    #########################################################################
    # EventHandler interface.
    #########################################################################

    def handle_event(self, mask):
	""" Callback method to handle all events except close events. """

	# Read event.
	if mask & Reactor.READ:
	    self.__read_event()

	# Exception event.
	elif mask & Reactor.EXCEPTION:
	    self.__exception_event()

	return

    def handle_close(self):
	""" Callback method to handle close events. """

	# Withdraw my Reactor registration.
	self.__reactor.unregister_handler(self, Reactor.READ)

	# Clean up my socket.
	try:
	    self.__socket.close()

	except socket.error:
	    pass

	del self.__socket

	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()

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

    def __read_event(self):
	""" Handle a read event. """

	try:
	    # 'Accept' the connection (in TCP/IP there is, in fact, no way to
	    # reject it!).
	    (client_socket, address) = self.__socket.accept()

	    # Create a 'Connection' instance to look after the socket.
	    connection = IIOPConnection.IIOPConnection(client_socket)

	    # Create a server to handle client operation requests.
	    GIOPServer.GIOPServer(connection)

	except socket.error:
	    self.__exception_event()

	return

    def __exception_event(self):
	""" Handle an exception event. """

	# Clean up.
	self.handle_close()

	return
    
#############################################################################
