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


# Standard/built-in modules.
import socket

# Fnorb modules.
import CORBA, EventHandler, Reactor


class Nudger(EventHandler.EventHandler):
    """ Nudger (part of the 'Reactor' pattern). """

    def __init__(self, host='', port=0):
	""" Constructor.

	'host'    is the hostname to use when creating my socket.
	'port'    is the port number to use when creating my socket.

	"""
	try:
	    # Create a socket on which to listen for connection requests.
	    listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	    listen_socket.bind((host, port))
	    listen_socket.listen(1)

	    # Get the host name, the IP address and the port number of the
	    # socket.
	    # listen_host = host or socket.gethostname()

	    # 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
	    listen_host = tmphost or hostname or host
	    (listen_ip_address, listen_port) = listen_socket.getsockname()

	    # Under OSF/1 if you specify '' as the host address then the IP
	    # address returned by 'getsockname' will be '0.0.0.0'!
	    #if listen_ip_address == '0.0.0.0':
            #    listen_ip_address = socket.gethostbyname(listen_host)

	    # Now create a client socket and connect it to the listening
	    # socket.  This uses the little-known semantics of the 'connect'
	    # function i.e. the call will succeed even *before* the 'accept'
	    # has been performed on the listening socket!  If you don't believe
	    # me read Stevens' Network Programming ;^)
	    self.__client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
	    self.__client.connect((listen_host, listen_port))

	    # And finally, we do an 'accept' to complete the connection.
	    (self.__server, address) = listen_socket.accept()

	    # Set the server socket to NON-blocking mode.
	    self.__server.setblocking(0)

	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 with the Reactor.
	self.__reactor.register_handler(self, Reactor.READ)

	return

    def __del__(self):
	""" Destructor. """

	self.handle_close()
	return

    #########################################################################
    # Nudger interface.
    #########################################################################

    def nudge(self):
	""" Nudge myself! """

	self.__client.send('nudge!')
	return

    #########################################################################
    # 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.
	if 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 sockets.
	try:
	    self.__client.close()
	    self.__server.close()

	except socket.error:
	    pass

	del self.__client
	del self.__server

	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.__server.fileno()

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

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

	# We do nothing!  The sole purpose of the nudger is to trip the
	# reactor out of a blocking select!
	self.__server.recv(10)

	return

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

	# Clean up.
	self.handle_close()

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