home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / fnb101.zip / Lib / site-packages / Fnorb / orb / BOA.py < prev    next >
Text File  |  1999-06-28  |  14KB  |  484 lines

  1. #!/usr/bin/env python
  2. #############################################################################
  3. # Copyright (C) DSTC Pty Ltd (ACN 052 372 577) 1997, 1998, 1999
  4. # All Rights Reserved.
  5. #
  6. # The software contained on this media is the property of the DSTC Pty
  7. # Ltd.  Use of this software is strictly in accordance with the
  8. # license agreement in the accompanying LICENSE.HTML file.  If your
  9. # distribution of this software does not contain a LICENSE.HTML file
  10. # then you have no rights to use this software in any manner and
  11. # should contact DSTC at the address below to determine an appropriate
  12. # licensing arrangement.
  13. #      DSTC Pty Ltd
  14. #      Level 7, GP South
  15. #      Staff House Road
  16. #      University of Queensland
  17. #      St Lucia, 4072
  18. #      Australia
  19. #      Tel: +61 7 3365 4310
  20. #      Fax: +61 7 3365 4311
  21. #      Email: enquiries@dstc.edu.au
  22. # This software is being provided "AS IS" without warranty of any
  23. # kind.  In no event shall DSTC Pty Ltd be liable for damage of any
  24. # kind arising out of or in connection with the use or performance of
  25. # this software.
  26. #
  27. # Project:      Fnorb
  28. # File:         $Source: /units/arch/src/Fnorb/orb/RCS/BOA.py,v $
  29. # Version:      @(#)$RCSfile: BOA.py,v $ $Revision: 1.32 $
  30. #
  31. #############################################################################
  32. """ Implementation of the CORBA Basic Object Adapter (BOA). """
  33.  
  34.  
  35. # Standard/built-in modules.
  36. import regex, string
  37.  
  38. # Fnorb Modules.
  39. import fnorb_thread, CORBA, IOP, Util
  40.  
  41. # BOA identifier.
  42. BOA_ID = 'The Fnorb BOA v1.0.Threads.R.Us'
  43.  
  44.  
  45. #############################################################################
  46. # The BOA interface.
  47. #############################################################################
  48.  
  49. def BOA_init(argv=[], boa_id=BOA_ID):
  50.     """ Initialise the BOA.
  51.  
  52.     This is a factory function for the BOA class (the BOA is a singleton (ie.
  53.     there can only be one BOA instance per process)).
  54.  
  55.     """
  56.     try:
  57.     boa = BOA(argv, boa_id)
  58.  
  59.     except BOA, boa:
  60.     pass
  61.  
  62.     return boa
  63.  
  64.  
  65. class BOA:
  66.     """ The BOA object.
  67.  
  68.     The BOA is a singleton (ie. there can only be one BOA instance per
  69.     process).
  70.  
  71.     """
  72.     # Singleton instance.
  73.     __instance = None
  74.  
  75.     # Mutex to make access via the factory function thread-safe.
  76.     __lk = fnorb_thread.allocate_lock()
  77.  
  78.     def __init__(self, argv, boa_id):
  79.     """ Constructor.
  80.  
  81.     'argv'   command line arguments.
  82.     'boa_id' the unique identifier of the BOA implementation. This MUST
  83.              be the same string as the 'constant' BOA.BOA_ID.
  84.  
  85.     """
  86.     # The BOA id MUST be the same string as the 'constant' BOA_ID.
  87.     if boa_id != BOA_ID:
  88.         raise CORBA.INITIALIZE() # System exception.
  89.  
  90.     # The BOA is a singleton (ie. there can only be one BOA instance per
  91.     # process (have I made this point clear yet?!?).
  92.     BOA.__lk.acquire()
  93.     try:
  94.         if BOA.__instance is not None:
  95.         raise BOA.__instance
  96.  
  97.         BOA.__instance = self
  98.  
  99.     finally:
  100.         BOA.__lk.release()
  101.  
  102.     # Get a reference to the ORB that we are living in.
  103.     orb = CORBA.ORB_init()
  104.  
  105.     # Register ourselves with the ORB.
  106.     orb._fnorb_register_object_adapter(self)
  107.  
  108.     # Process any 'command line' options.
  109.     self.__options = self.__get_options(argv)
  110.  
  111.     # If a particular hostname or port number was specified then use them.
  112.     if self.__options.has_key('OAhost'):
  113.         host = self.__options['OAhost']
  114.  
  115.     else:
  116.         host = ''
  117.  
  118.     if self.__options.has_key('OAport'):
  119.         port = self.__options['OAport']
  120.  
  121.     else:
  122.         port = 0
  123.  
  124.     # To save a dictionary lookup on every operation invocation we set a
  125.     # flag if the 'thread per request' option was specified.
  126.     if self.__options.has_key('OAthread-per-request'):
  127.         self.__thread_per_request = 1
  128.  
  129.     else:
  130.         self.__thread_per_request = 0
  131.  
  132.     # A dictionary of active object implementations in the form:-
  133.     # {ObjectKey: Implementation} with types {String: Instance}.
  134.     self.__object_table = {}
  135.  
  136.     # A mutex for making access to the BOA's data structures thread-safe.
  137.     self.__lk = fnorb_thread.allocate_lock()
  138.     
  139.     # Ask each protocol to create an endpoint on which to listen for
  140.     # connection requests (no requests will actually be serviced until
  141.     # we start the event loop).
  142.     #
  143.     # fixme: We will need some sort of abstraction for protocol
  144.     # addresses. Hostname and port number are just kinda TCP/IP
  145.     # specific!
  146.     for protocol in orb._fnorb_protocols():
  147.         protocol.enable(host, port)
  148.  
  149.     return
  150.  
  151.     #########################################################################
  152.     # BOA interface.
  153.     #########################################################################
  154.  
  155.     def create(self, id, intrep_id):
  156.     """ Generate an object reference.
  157.  
  158.     'id'        is the object reference data (aka the object key).
  159.     'intrep_id' is the interface repository id of the interface.
  160.  
  161.         """
  162.     # Create an IOR.
  163.     ior = self._fnorb_create_ior(id, intrep_id)
  164.  
  165.     # Create an object reference from the IOR.
  166.     return CORBA.ORB_init()._fnorb_ior_to_object(ior)
  167.  
  168.     def obj_is_ready(self, obj, impl):
  169.     """ Inform the BOA that an object is prepared to accept requests.
  170.  
  171.     'obj'  is the object reference that the implementation will use.
  172.     'impl' is the implementation repository object that implements the
  173.            interface (in our case, simply a Python instance!).
  174.     
  175.         """
  176.     # Get the IOR from the object reference.
  177.     ior = obj._fnorb_ior()
  178.  
  179.     # Get the object key from *any* protocol (there is only one object key
  180.     # per IOR, not one per protocol!).
  181.     for protocol in CORBA.ORB_init()._fnorb_protocols():
  182.         object_key = protocol.get_object_key(ior)
  183.         if object_key is not None:
  184.         break
  185.  
  186.     else:
  187.         raise CORBA.INV_OBJREF() # System exception.
  188.  
  189.     # Add the implementation to our object table.
  190.     self.__lk.acquire()
  191.     self.__object_table[object_key] = impl
  192.     self.__lk.release()
  193.  
  194.     # Add the object key to the implementation's key table.
  195.     impl._fnorb_add_object_key(object_key)
  196.  
  197.     return
  198.  
  199.     def deactivate_obj(self, obj):
  200.     """ Remove an object implementation from the BOA. """
  201.  
  202.     # Get the IOR from the object reference.
  203.     ior = obj._fnorb_ior()
  204.  
  205.     # Get the object key from *any* protocol (there is only one object key
  206.     # per IOR, not one per protocol!).
  207.     for protocol in CORBA.ORB_init()._fnorb_protocols():
  208.         object_key = protocol.get_object_key(ior)
  209.         if object_key is not None:
  210.         break
  211.  
  212.     else:
  213.         raise CORBA.INV_OBJREF() # System exception.
  214.  
  215.     self.__lk.acquire()
  216.     try:
  217.         impl = self.__object_table[object_key]
  218.  
  219.         # Remove the object key from the implementation's key table.
  220.         impl._fnorb_delete_object_key()
  221.  
  222.         # Remove the object from the object table.
  223.         del self.__object_table[object_key]
  224.  
  225.     # Ignore attempts to delete an object that wasn't in the table in the
  226.     # first place!
  227.     except KeyError:
  228.         pass
  229.     self.__lk.release()
  230.  
  231.     return
  232.  
  233.     #########################################################################
  234.     # Fnorb-specific interface.
  235.     #########################################################################
  236.  
  237.     def _fnorb_mainloop(self, timeout=None):
  238.     """ Start the event loop. """
  239.  
  240.     # Get a reference to the ORB.
  241.     orb = CORBA.ORB_init()
  242.  
  243.     # If the ORB is running in multi-threaded mode then start a thread-pool
  244.     # to handle incoming requests.
  245.     if orb._fnorb_threading_model() == Util.THREADED:
  246.         # Get the size of the thread pool from the ORB.
  247.         size = orb._fnorb_thread_pool_size()
  248.         if size > 0:
  249.         from ThreadPoolQueue import ThreadPoolQueue
  250.  
  251.         # Create and start the queue.
  252.         self.__queue = ThreadPoolQueue(size, self.__process_request)
  253.         self.__queue.start()
  254.  
  255.     # Ladies and Gentlemen, start your engines...
  256.     for protocol in CORBA.ORB_init()._fnorb_protocols():
  257.         # Ask the protocol to start its event loop.
  258.         protocol.start(timeout)
  259.         
  260.     return
  261.  
  262.     def _fnorb_quit(self):
  263.     """ Exit the event loop. """
  264.  
  265.     # Get a reference to the ORB.
  266.     orb = CORBA.ORB_init()
  267.  
  268.     # Ok, that's enough...
  269.     for protocol in orb._fnorb_protocols():
  270.         # Ask the protocol to stop its event loop.
  271.         protocol.stop()
  272.  
  273.     # If the ORB is running in multi-threaded mode then stop the thread
  274.     # pool.
  275.     if orb._fnorb_threading_model() == Util.THREADED:
  276.         self.__queue.stop()
  277.  
  278.     return
  279.  
  280.     def _fnorb_get_implementation(self, object_key):
  281.     """ Return the implementation for the specified object key. """
  282.  
  283.     self.__lk.acquire()
  284.     try:
  285.         impl = self.__object_table[object_key]
  286.  
  287.     except KeyError:
  288.         impl = None
  289.     self.__lk.release()
  290.  
  291.     return impl
  292.  
  293.     def _fnorb_create_ior(self, object_key, intrep_id):
  294.     """ Create an IOR.
  295.  
  296.     'id'        is the object reference data (aka the object key).
  297.     'intrep_id' is the repository id of the object's interface.
  298.  
  299.         """
  300.     profiles = []
  301.     # Create a profile for each protocol.
  302.     for protocol in CORBA.ORB_init()._fnorb_protocols():
  303.         profiles.append(protocol.create_profile(object_key))
  304.  
  305.     return IOP.IOR(intrep_id, profiles)
  306.  
  307.     def _fnorb_request(self, request):
  308.     """ Process an operation request.
  309.  
  310.     Currently, the request is a tuple in the form:-
  311.  
  312.     (giop_server, request_header, cursor)
  313.  
  314.     """
  315.     if self.__thread_per_request:
  316.         fnorb_thread.start_new_thread(self.__process_request, (request,))
  317.  
  318.     else:
  319.         # Get a reference to the ORB.
  320.         orb = CORBA.ORB_init()
  321.  
  322.         # If the ORB is running in multi-threaded mode and we are using a
  323.         # thread pool to service requests then simply put the request on
  324.         # the queue.
  325.         if orb._fnorb_threading_model() == Util.THREADED and \
  326.            orb._fnorb_thread_pool_size() > 0:
  327.         self.__queue.add_item((request,))
  328.  
  329.         # Otherwise, we handle the request immediately.
  330.         else:
  331.         self.__process_request(request)
  332.  
  333.     return
  334.  
  335.     def _fnorb_object_here(self, object_key):
  336.     """ Does the BOA contain an implementation for this object key? """
  337.  
  338.     # See if the object is here!
  339.     self.__lk.acquire()
  340.     object_here = self.__object_table.has_key(object_key)
  341.     self.__lk.release()
  342.  
  343.     return object_here
  344.  
  345.     #########################################################################
  346.     # Private interface.
  347.     #########################################################################
  348.  
  349.     def __get_options(self, argv):
  350.     """ Process 'command line' options. """
  351.  
  352.     # Work on a copy of the command line arguments.
  353.     args = argv[:]
  354.     
  355.     # Command line arguments used by the BOA are stripped from the list.
  356.     # We accomplish this by first emptying the list and then putting back
  357.     # any non-BOA arguments.
  358.     #
  359.     # Empty the list without the destroying the list object itself.
  360.     del argv[:]
  361.  
  362.     # For convenience we return any BOA options in a dictionary.
  363.     options = {}
  364.  
  365.     i = 0
  366.     while i < len(args):
  367.         arg = args[i]
  368.  
  369.         if regex.match('--OAhost=.*', arg) != -1:
  370.         value = arg[string.find(arg, '=') + 1:]
  371.         options['OAhost'] = value
  372.  
  373.         elif regex.match('--OAport=.*', arg) != -1:
  374.         try:
  375.             value = arg[string.find(arg, '=') + 1:]
  376.             options['OAport'] = string.atoi(value)
  377.  
  378.         except ValueError:
  379.             raise CORBA.INITIALIZE() # System exception.
  380.  
  381.         elif arg == '--OAthread-per-request':
  382.         options['OAthread-per-request'] = None
  383.  
  384.         else:
  385.         argv.append(arg)
  386.  
  387.         i = i + 1
  388.  
  389.     return options
  390.  
  391.     def __process_request(self, request):
  392.     """ Handle a GIOP request. """
  393.  
  394.     # fixme: Need request abstraction?
  395.     (giop_server, request_header, cursor) = request
  396.     try:
  397.         self.__lk.acquire()
  398.         if self.__object_table.has_key(request_header.object_key):
  399.         impl = self.__object_table[request_header.object_key]
  400.         else:
  401.         impl = None
  402.         self.__lk.release()
  403.  
  404.         # Handle any 'non-existent' operations (some ORBs use
  405.         # '_not_existent' instead of '_non_existent').
  406.         if request_header.operation in ['_non_existent', '_not_existent']:
  407.         # If the implementation was found then it is NOT non-existent!
  408.         result = impl is None
  409.  
  410.         # Send the appropriate reply.
  411.         self.__non_existent(giop_server,request_header, cursor, result)
  412.  
  413.         return
  414.  
  415.         # If the implementation is not here then raise an appropriate
  416.         # exception.
  417.         if impl is None:
  418.         raise CORBA.NO_IMPLEMENT() # System exception.
  419.     
  420.         # Look for the skeleton method on the implementation.
  421.         try:
  422.         method = getattr(impl, '_skel_' + request_header.operation)
  423.  
  424.         # If the skeleton method is not implemented then see if there is a
  425.         # Dynamic Invocation Routine (DIR).
  426.         #
  427.         # fixme: Should the DIR be __getattr__?
  428.         except AttributeError:
  429.         try:
  430.             method = getattr(impl, 'invoke')
  431.  
  432.         # If there is no skeleton method and no DIR then the
  433.         # implementation does not support this operation!
  434.             except AttributeError:
  435.             raise CORBA.NO_IMPLEMENT() # System exception.
  436.  
  437.         # Add the object key to the implementation's key table.
  438.         impl._fnorb_add_object_key(request_header.object_key)
  439.  
  440.         # Create a server request.
  441.         server_request = CORBA.ServerRequest(request_header, cursor)
  442.  
  443.         # Invoke the skeleton method/DIR.
  444.         try:
  445.         apply(method, (server_request,))
  446.  
  447.         # Send the reply (unless this was a 'oneway' request).
  448.         if request_header.response_expected:
  449.             giop_server.reply(request_header, server_request)
  450.  
  451.         except CORBA.UserException, ex:
  452.         # Set the exception in the server request.
  453.         server_request.exception(ex)
  454.  
  455.         # Notify the GIOP server of the user exception.
  456.         giop_server.user_exception(request_header, server_request)
  457.  
  458.     except CORBA.SystemException, ex:
  459.         # Notify the GIOP server of the system exception.
  460.         giop_server.system_exception(request_header, ex)
  461.  
  462.     return
  463.  
  464.     def __non_existent(self, giop_server, request_header, cursor, result):
  465.     """ Send a reply to a 'non_existent' request. """
  466.  
  467.     # Create a server request.
  468.     server_request = CORBA.ServerRequest(request_header, cursor)
  469.  
  470.     # Initialise it.
  471.     server_request.initialise([], [CORBA.TC_boolean], [])
  472.  
  473.     # Set the result.
  474.     server_request.results(result)
  475.  
  476.     # Send the reply.
  477.     giop_server.reply(request_header, server_request)
  478.  
  479.     return
  480.  
  481. #############################################################################
  482.