#!/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/cos/naming/RCS/NamingService.py,v $
# Version:      @(#)$RCSfile: NamingService.py,v $ $Revision: 1.10 $
#
#############################################################################
""" A COS Naming service! """


# Standard/built-in modules.
import errno, getopt, os, shelve, sys

# Fnorb modules.
from Fnorb.orb import uuid, BOA, CORBA

# Naming service modules.
import NamingContext


def PersistentNamingContextFactory_init(db_dir):
    """ Initialise the Persistent Naming Contect Factory.

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

    """
    try:
	ncf = PersistentNamingContextFactory(db_dir)

    except PersistentNamingContextFactory, ncf:
	pass

    return ncf


class PersistentNamingContextFactory(NamingContext.NamingContextFactory):
    """ A factory for persistent naming contexts!

    The factory is a singleton (ie. there can only be one instance per
    process).
    
    """
    # Singleton instance.
    __instance = None

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

	'database' is the name of the directory in which to store the naming
                   context databases.
		 
        """
	# The factory is a singleton (ie. there can only be one instance per
	# process).
	if PersistentNamingContextFactory.__instance is not None:
	    raise PersistentNamingContextFactory.__instance

	PersistentNamingContextFactory.__instance = self

	# The directory in which to store naming context databases.
	self.__database = database

	return
		 
    def create_naming_context(self, object_key=None):
	""" Create and return a new *persistent* naming context. """

	# Create an object key for the context.
	if object_key is None:
	    # Generate a unique object key for the context.
	    object_key = uuid.uuid()

	# Create/open the context's persistent dictionary.
	pathname = '%s/%s' % (self.__database, object_key)
	bindings = shelve.open(pathname)

	# Create an instance of the implementation class.
	impl = NamingContext.NamingContext(self, bindings)

	# Get a reference to the object adapter.
	boa = BOA.BOA_init()
	
	# Create an object reference with a random object key.
	ref = boa.create(object_key, NamingContext.NamingContext._FNORB_ID)

	# Activate the implementation (ie. connect the generated object
	# reference to the implementation).
	boa.obj_is_ready(ref, impl)

	return impl


class NamingService:
    """ A COS Naming service! """

    # Object key for the root context of the Naming Service.
    ROOT_OBJECT_KEY = 'NameService'

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

	# Process command line options.
	#
	# The only options currently allowed are:-
	#
	# '-?, -h, --help'      to print the usage message on stdout.
	# '--ior'               to print the IOR of the root context on stdout.
	# '--iorf=filepath'     to print the IOR of the root context to file
	#                       located at 'filepath'
	# '--database=pathname' to specify the directory name of a persistent
	#                       naming service 'database'.
	options = self.__get_options(argv)

	# If a 'database' directory *was* specified then create a *persistent*
	# naming service.
	if options.has_key('--database'):
	    database = options['--database']
	    root = self.__create_persistent_naming_service(database)

	# Otherwise, create a transient naming service.
	else:
	    root = self.__create_transient_naming_service()

	# store ior
	orb = CORBA.ORB_init()
	self.ior = orb.object_to_string(root)
	# If requested (via a command line option) then print the stringified
	# IOR of the root context on stdout.
	if options.has_key('--ior'):
	    sys.stdout.write(self.ior + '\n')
	    sys.stdout.flush()
	# if requested, print stringified IOR of the root context to file
	if options.has_key('--iorf'):
	    fp = open(options['--iorf'], 'w')
	    fp.write(self.ior + '\n')
	    fp.close()

	return

    #########################################################################
    # Internal interface.
    #########################################################################

    def __get_options(self, argv):
	""" Process command line options. """

	# Parse the command line arguments/options.
	try:
	    (options, rest) = getopt.getopt(argv[1:],
					    'h',
					    ['help', 'ior', 'database=', 'iorf='])
	except getopt.error:
	    self.__usage()

	# Put the options in a dictionary for convenience.
	dictionary = {}
	for (name, value) in options:
	    dictionary[name] = value

	# If the help option was specified then just print the usage and exit!
	if dictionary.has_key('-?') \
	   or dictionary.has_key('-h') \
	   or dictionary.has_key('--help'):
	    self.__usage()

	return dictionary

    def __usage(self):
	""" Print the 'usage' message on stdout and exit! """

	print 'Usage: fnaming [options]'
	print 
	print 'Options:'
	print '-?, -h, --help         ',
	print 'Show this message.'
	print '--ior                  ',
	print 'Print the IOR of the name service on stdout.'
	print '--iorf=filepath        ',
	print 'Specify the pathname of a file to write the IOR to.'
	print '--database=pathname    ',
	print 'Specify the pathname of the name service database.'

	sys.exit(0)

    def __create_persistent_naming_service(self, database):
	""" Create/Re-create a persistent naming service!

	'database' is the directory name of the naming service database.
	
	"""
	# Create the naming service's 'database' directory.
	try:
	    os.mkdir(database)

	# If the directory already exists then carry on!
	except os.error, (code, message):
	    if code != errno.EEXIST:
		raise os.error, (code, message)

	# Create a factory for creating *persistent* naming contexts.
	factory = PersistentNamingContextFactory_init(database)

	# (Re)create the root context.
	root = factory.create_naming_context(NamingService.ROOT_OBJECT_KEY)

	# (Re)create all of the other naming contexts in the naming service
	# (Ok, so this approach is not exactly scaleable ;^)
	#
	# If the executing Python interpreter is using the 'dumbdbm' module
	# then the database directory will contain 3 files for each context
	# (.dat, .dir, .bak).  If any other 'db' module is being used then
	# there will be only 1 file per context.
	#
	# To allow for both cases we list the entire contents of the database
	# directory and then create a list of unique contexts.
	contexts = {}
	for filename in os.listdir(database):
	    (base, ext) = os.path.splitext(filename)
	    if base != NamingService.ROOT_OBJECT_KEY:
		contexts[base] = None

	# Create each unique context.
	for context in contexts.keys():
	    factory.create_naming_context(context)

	return root

    def __create_transient_naming_service(self):
	""" Create a transient naming service.

	A transient naming service loses all of its information when it is
	closed down.

	"""
	# Create a factory for creating *transient* naming contexts.
	factory = NamingContext.NamingContextFactory_init()

	# Create the root context.
	return factory.create_naming_context(NamingService.ROOT_OBJECT_KEY)

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