
OPUS CORBA Servants, Servers, and
Supporting Classes Design Document
(Last modified: December 21, 2000)
§ Overview
o Problem Areas/Unique Features
§ Supporting Functions/Classes
1. CORBA_obj_helper
2. ORB_Helper
3. BB_Helper
4. Proc_restrictions
§ Resource Blackboard & Related Classes
1. Resource_bb
2. Resource_entry
3. Resource_key
4. Resource_value
5. Opus_resource_lock
6. Ior_bb
1. Entry_event_mgr
2. COSEC_event_mgr
3. Event_id
1. CORBA_pstat_bb
2. CORBA_osf_bb
3. CORBA_entry_lock
4. CORBA_osf_lock
5. CORBA_pstat_lock
6. CORBA_osf
7. CORBA_pstat
§ Servants
1. Pingable_impl
2. Blackboard_impl
3. OpusEnv_impl
1. File_bb_event_poller
This document includes a detailed design for the server-side functionality of the CORBA alternatives to the traditional file system-based OSF and PSTAT blackboards (see A Multi-threaded Caching Blackboard for the OAPI: Design Document) and those additional services required by the Java managers (see Java Managers / OPUS Interface Design Document). In particular, the following aspects of the server-side infrastructure are explained below:
Central to the overall design are the CORBA objects with which the Java managers and OPUS applications interact, so a clear understanding of the terminology employed when describing such a system is important. In simple terms, CORBA is a client/server architecture where the interface between client and server is defined first in IDL, then compiled to client stubs and server skeletons for a particular programming language (not necessarily the same for both client and server). On the client-side, the stubs are ordinary objects in the target programming language that have the same interface as specified by the IDL code. However, the stubs do not provide the actual method functionality, but instead act as request brokers between the caller and (possibly) remote CORBA objects that do. With one exception, generating the stubs and linking them into the code is all that need be done to use CORBA objects on the client-side. The exception is that the client must obtain references to active CORBA objects at run-time before it can make calls on those objects. The CORBA specification does not dictate how object references are to be communicated from the servers to the clients, so this design includes a means of doing so through an OAPI Blackboard interface (refer to the Java Managers / OPUS Interface Design Document for an argument why the COS Naming Service, which performs a similar service, is ill-suited for use by OPUS).
To be useful to the client, an object reference must refer to an active object, incarnated by some servant in a server running on a node accessible through the network (or through the TCP/IP stack if the server is collocated with the client). Servants are programming language entities (e.g., classes in C++ and Java) that implement each method declared in the object’s IDL interface; servers are programs that initialize the CORBA infrastructure (the Object Request Broker [ORB] and Portable Object Adaptor [POA], specifically), incarnate the servants, activate the objects, and then transfer control to the ORB so that client requests can be served on each of the active objects. Servants are developed from skeleton code produced when the IDL code is compiled to a particular programming language (recall that the compilation also produces client stubs). When compiled to C++, an IDL interface generates a skeleton base class for the servant (an alternative approach that uses a delegation model instead of inheritance also exists). The skeleton base class is abstract, forcing the servant to implement each member function of the object as mapped from IDL to C++. Once the servants are developed, server code must be written that instantiates them (as programming language constructs), that activates the CORBA objects (as CORBA constructs), and that transfers control to the ORB so that client requests may be fulfilled.
The next section introduces the servants in this design, and some of the supporting infrastructure required to adapt CORBA into OPUS.
At present, OPUS applications and the managers interact with the PSTAT and OSF blackboards through instances of the File_pstat_bb and File_osf_bb classes, although they do so unknowingly (by design) through the abstract interfaces Pstat_bb and Osf_bb, respectively. These classes directly manipulate a common, networked file system and, as a consequence, place the restriction on a distributed OPUS environment that participating nodes have access to the file system(s) used by OPUS. More worrisome than this restriction is the pathological behavior of this strategy as OPUS scales to large numbers of pipeline processes running simultaneously: each application (and manager) interacts directly with the file system, so file I/O load increases in proportion to the number of running OPUS processes, sometimes at the expense of operating system robustness.
The motivation for the design proposed herein is to offer the OPUS user an alternative to the current PSTAT and OSF blackboard implementation that does not have the same network file system access requirements and one that scales better (i.e., it loads the system less and/or in less detrimental ways), but that is fully compatible with the current system. In other words, the user should be able to switch between the two with little effort (this does not imply that the two would be interoperable, however). Moreover, the Java managers will obtain blackboard information in an entirely different way than the present versions (see Java Managers / OPUS Interface Design Document), so the design must provide for server-side support of the managers no matter which blackboard implementation the user selects.
The manner in which the user selects the blackboard type in OPUS is through the BB_TYPE keyword in the file OPUS_DEFINITIONS_DIR:opus.env. At present, only the value “FILE” is recognized, and it corresponds to an environment where pipeline applications, utilities, and the managers interface with the file system strictly as clients. There is no distributed OPUS server per-se in this model since the file system assumes that role. In the design herein, a new choice for BB_TYPE is added—“CORBA”—that results in the transfer of the responsibility assumed by the file system in the other model to a suite of new servers and distributed objects. It is clear that in this case, server-side functionality must be added to the OAPI, as must corresponding client-side adaptors. New OAPI Blackboard types, CORBA_osf_bb and CORBA_pstat_bb, are instantiated by Opus_env as the PSTAT and OSF blackboards, respectively, on the client side. These classes are adaptors that expose an OAPI Blackboard interface, but that make calls on a CORBA object after translating the inputs and outputs to and from their CORBA counterparts.
Two CORBA objects, Blackboard and OpusEnv, provide the blackboard and user context functionality on the server side. The Blackboard interface is modeled on the abstract OAPI Blackboard class; the OpusEnv interface extracts key functionality from the OAPI Opus_env class for (exclusive) use by the managers. Both of these interfaces inherit behavior from two supporting interfaces: the Pingable interface defines a single, low-overhead method (ping) that is used to test whether an object reference is functional (in general, there are no reliable facilities in CORBA for determining whether an object reference points to an active object, short of making a calling to it); the Gaugable interface is a means of querying an object for usage statistics (the number and significance of statistics accessed through this interface are particular to each interface).
The Blackboard servant implementation in this design, Blackboard_impl, specializes the Blackboard interface to adapt to an OAPI Blackboard. Although any OAPI Blackboard can be adapted by Blackboard_impl, an assumption is made that it is “push event-aware”, meaning that it overrides the interpretation of the return value of the member function Blackboard::search. Instead of indicating the number of matching entries found by the search, a push event-aware Blackboard returns an event identifier that helps push event consumers (i.e., the OMG and PMG) to sequence events relative to search results. The motivation for this change is the decoupling of the search and event streams in the new managers: the PMG and OMG establish a baseline for their displays by conducting a wildcard search on the target blackboard at startup, but they also can receive events during this time. If traffic is heavy on a blackboard, it is possible for the managers to receive events that already are reflected in the search results. By tagging each event and each search call with a time-ordered, unique identifier, the managers are able to discard events that occurred before the search was completed. The class Event_id encapsulates the identifier type on the server-side, and has a conversion operator to type integer so that it can be returned by Blackboard::search.
Two push event-aware OAPI Blackboard classes are employed alternatively by Blackboard_impl: Mt_cache_bb (see A Multi-threaded Caching Blackboard for the OAPI: Design Document, but note that this document has been revised since the original design review) and File_bb_event_poller. The former blackboard is a caching, multithreaded OAPI Blackboard that uses an instance of either File_osf_bb or File_pstat_bb as a persistent store, and is constructed when BB_TYPE is set to CORBA. In such a case, the managers and OPUS applications communicate through this blackboard. Class File_bb_event_poller is a multithreaded Blackboard that polls a file system-based blackboard (again, either File_osf_bb or File_pstat_bb) at regular intervals, generating events for the managers, while also acting as a proxy to the file system-based blackboard services. Blackboard_impl uses this type of push event-aware blackboard when BB_TYPE is set to FILE (in this case, only the managers use the CORBA infrastructure; pipeline applications interact with the blackboards through the traditional file system implementations).
Both Mt_cache_bb and File_bb_event_poller delegate event generation responsibilities to an instance of the Entry_event_mgr hierarchy. Class Entry_event_mgr is an abstract base class with a single member function, notify, and an enumerated list of event of types: Entry_event_mgr::CREATED (generated in response to a Blackboard::post operation), Entry_event_mgr::DELETED (generated in response to a Blackboard::erase operation), and Entry_event_mgr::MODIFIED (generated in response to a Blackboard::replace operation). Although the design calls for only one concrete class derived from Entry_event_mgr (COSEC_event_mgr that pushes events out to a COS Event Channel), additional event managers might be needed in the future, so it is worthwhile to insulate the push event-aware blackboards from knowledge of the exact form of event handling through the abstract base class Entry_event_mgr. Class COSEC_event_mgr does the necessary conversion from OAPI Entry (the affected entry or entries) and event type to the IDL EntryEvent structure, which is the CORBA object pushed to the managers over the event channel.
The event flow from Blackboard_impl to the event channel through Entry_event_mgr, and on to the managers, is illustrated below for a CORBA PSTAT blackboard servant:

Note that there are three CORBA servants per OPUS blackboard: the push consumer that is part of the managers (Entry_event_consumer_servant), the event channel (ProxyPushConsumer), and the blackboard implementation (Blackboard_impl). Although the same server could incarnate the event channel and blackboard servants in this case, it is advantageous from the point of view of performance to distribute these tasks across CPUs by giving each servant its own server. The tradeoff in doing so is increased network load if the servers run on different nodes. The Blackboard_impl servant must communicate with the event channel over the network in this case, unlike the scenario where they are collocated in the same server. The intermediate case—separate servers running on the same node—does not introduce additional network traffic, but performance still is less than collocated servants since communications occur through the TCP/IP stack. Overall, separate servers afford the most flexibility, so they are adopted by this design.
Another server, opus_env_server, incarnates the OpusEnv servant, OpusEnv_impl. Unlike the blackboard and event channel servers, only one instance of this server runs per OPUS user. The OMG and PMG use this interface to obtain user context information such as resource, pipeline, stage, and path file names and content, and to start pipeline processes. Piggybacked onto opus_env_server is a simple sockets-based server for bootstrapping access to OpusEnv by the client stubs in the manager code. At startup, the managers connect to the node and port on which this server is listening, exchange authentication information, and then receive the IOR for the OpusEnv object. Once the managers have a reference to OpusEnv, they can begin calling its methods, one of which affords them access to other IORs that they will need (i.e., the blackboards and event channels). The PMG and OMG get the information necessary to connect to the bootstrap server from the user (the node on which opus_env_server is running, and the user’s account and password for that node) and from a file (.opus_env_server) located in the user’s home directory on that node that is created by opus_env_server at startup. The file is read by the managers using FTP, and contains the port address that opus_env_server is listening to and an authentication token. Reading the port address from a file relieves the managers from having to query the user for this information, and since the address changes each time opus_env_server is restarted, this is a significant effort saving measure for the user (establishing a fixed address for opus_env_server would require coordination between all OPUS users, as well as other TCP/UDP services, on a given node). Moreover, the user’s remote password is needed to complete the FTP operation, so this procedure also serves as an authentication mechanism. Since the port address can be discovered by any user logged into the node on which the server is running without looking in the .opus_env_server file (the file is created with user read/write permission to prevent simple inspection), an authentication token also is placed in the file that must be passed back to the server once a connection is established. This prevents unauthorized users from retrieving the IOR for another user’s OpusEnv object through the interface.
Once the managers have a reference to OpusEnv, they call the OpusEnv::getIOR method to obtain references to the Blackboard and EventChannel objects they need. On the server side, this method uses the function CORBA_obj_helper::get_object to perform the IOR lookup and server startup (if necessary) given a string object name. Other CORBA clients like CORBA_pstat_bb, CORBA_osf_bb, and COSEC_event_mgr also use this facility to retrieve object references. The object name lookup portion of CORBA_obj_helper::get_object is done using new OAPI Blackboard objects, Resource_bb and Ior_bb. Class Resource_bb is a general purpose OAPI Blackboard abstraction of a resource file. The entries on this blackboard are the keyword/value pairs in the resource file, and the lock_entry method of Resource_bb permits locking at this level. Class Ior_bb, derived from Resource_bb, specifically deals with CORBA IOR values, performing an equivalent service as the COS Naming Service for OPUS. A database of all CORBA object names and their IORs are maintained on the IOR blackboard—servers post the IOR(s) of their objects on the IOR blackboard when they start up, and CORBA_obj_helper::get_object reads IORs as requested by clients. Each OPUS CORBA object is identified on the IOR blackboard by a unique string name assigned to it as outlined according to the following rules:
OPUS CORBA Object Type
|
Naming Scheme
|
|
Blackboard |
File system directory specification of the underlying OAPI blackboard |
|
EventChannel |
The name of its associated blackboard + “/event/” |
|
Pingable (opus_event_service) |
The name of the event channel + “/pingable/” |
|
OpusEnv |
user name |
Once an IOR is retrieved from the IOR blackboard, the Pingable interface is used by CORBA_obj_helper::get_object to determine whether the reference points to an active object. Note that because the EventChannel interface is not derived from Pingable, the opus_event_service server activates a separate Pingable object in the same POA as the event channel so that the state of the server can be determined just like the other objects. If a ping call fails, it is assumed that the associated server is not running and an attempt is made to start it by first looking up the server type for the named object, then looking up that server’s command-line in the resource file OPUS_DEFINITIONS_DIR:opus_corba_objs (both queries are done through a Resource_bb object). This file contains mappings from object name to server type as well as from server type to command-line for each server and object. If no type entry exists for the named object in that file, CORBA_obj_helper::get_object determines the server type automatically (either “OSF” [blackboard], “PSTAT” [blackboard], “EVENT”, or “CONTEXT”) and creates a new entry. Once the server type is identified, another search of the file is done for the command-line associated with that server type. The user is responsible for initially configuring the command-line for every server type. For example, a typical opus_corba_objs file might be configured by the user to read (instances of the token “^F” on the server command-line are substituted with the requested object name during the startup process):
CONTEXT =
rsh -n odocluster1 opus_env_server &
EVENT =
rsh -n odocluster1 opus_event_service -i ^F &
OSF =
rsh -n odocluster1 opus_bb_server -i ^F -t OSF &
PSTAT =
rsh -n odocluster1 opus_bb_server -i ^F -t PSTAT &
Note that the automatic load balancing feature of TruCluster is taken advantage of in this case by use of the cluster alias. Over time, the file will be updated automatically as blackboards are accessed by the managers and pipeline applications, and later might look like:
/info/devcl/pipe/wmiller/pipe/ =
PSTAT
/info/devcl/pipe/wmiller/pipe/event/ =
EVENT
/info/devcl/pipe/wmiller/pipe/sciosfs/ =
OSF
/info/devcl/pipe/wmiller/pipe/sciosfs/event/ =
EVENT
CONTEXT =
rsh -n odocluster1 opus_env_server &
EVENT = rsh
-n odocluster1 opus_event_service -i ^F &
OSF =
rsh -n odocluster1 opus_bb_server -i ^F -t OSF &
PSTAT =
rsh -n odocluster1 opus_bb_server -i ^F -t PSTAT &
wmiller =
CONTEXT
The corresponding IOR blackboard database (OPUS_HOME:opus_iors) maintained by the system for the case above might look like:
/info/devcl/pipe/wmiller/pipe/ = IOR:010000003200000049444c3a6470742e73747363692e6564752f4f5055532f4f5055535f49444c2f42422f426c61636b626f6172643a312e300000000200000000000000740000000101026b0e0000006f646f616c706861362d6d633000d80f1b00000014010f005253548615e23957b102000000000001000000010000006c03000000000000000800000001901f40004f41540100000014000000011e194001000100000000000901010000000000004f415404000000011e0000000000007c0000000101026b140000006f646f616c706861362e73747363692e65647500d80f0f001b00000014010f005253548615e23957b1020000000000010000000100000000030000000000000008000000011e1940004f41540100000014000000011e194001000100000000000901010000000000004f415404000000011e0000
/info/devcl/pipe/wmiller/pipe/event/ =
IOR:010000003200000049444c3a6f6d672e6f72672f436f734576656e744368616e6e656c41646d696e2f4576656e744368616e6e656c3a312e300000000200000000000000740000000101026b0e0000006f646f616c706861362d6d6330009a101b00000014010f005253549719e23920230d000000000001000000010000006c03000000000000000800000001000000004f415401000000140000000100000001000100000000000901010000000000004f41540400000001000000000000007c0000000101026b140000006f646f616c706861362e73747363692e656475009a100f001b00000014010f005253549719e23920230d000000000001000000010000000003000000000000000800000001000000004f415401000000140000000100000001000100000000000901010000000000004f41540400000001000000
/info/devcl/pipe/wmiller/pipe/event/pingable/ =
IOR:010000002d00000049444c3a6470742e73747363692e6564752f4f5055532f4f5055535f49444c2f50696e6761626c653a312e30000000000200000000000000740000000101026b0e0000006f646f616c706861362d6d6330009a101b00000014010f005253549719e23920230d000100000001000000020000000103000000000000000800000001000000004f415401000000140000000143184001000100000000000901010000000000004f41540400000001430000000000007c0000000101026b140000006f646f616c706861362e73747363692e656475009a100f001b00000014010f005253549719e23920230d000100000001000000020000000003000000000000000800000001431840004f415401000000140000000143184001000100000000000901010000000000004f41540400000001430000
/info/devcl/pipe/wmiller/pipe/sciosfs/ =
IOR:010000003200000049444c3a6470742e73747363692e6564752f4f5055532f4f5055535f49444c2f42422f426c61636b626f6172643a312e300000000200000000000000740000000101026b0e0000006f646f616c706861362d6d633000e5101b00000014010f005253546e1ce23905bf0b000000000001000000010000006c030000000000000008000000014a1f40004f41540100000014000000011e194001000100000000000901010000000000004f415404000000011e0000000000007c0000000101026b140000006f646f616c706861362e73747363692e65647500e5100f001b00000014010f005253546e1ce23905bf0b0000000000010000000100000000030000000000000008000000011e1940004f41540100000014000000011e194001000100000000000901010000000000004f415404000000011e0000
/info/devcl/pipe/wmiller/pipe/sciosfs/event/ =
IOR:010000003200000049444c3a6f6d672e6f72672f436f734576656e744368616e6e656c41646d696e2f4576656e744368616e6e656c3a312e30000000020000000000000074000000010102630e0000006f646f616c706861352d6d633000cd101b00000014010f005253549808d539212401000000000001000000010000007603000000000000000800000001000000004f415401000000140000000100000001000100000000000901010000000000004f41540400000001000000000000007c00000001010263140000006f646f616c706861352e73747363692e65647500cd100f001b00000014010f005253549808d539212401000000000001000000010000000003000000000000000800000001000000004f415401000000140000000100000001000100000000000901010000000000004f41540400000001000000
/info/devcl/pipe/wmiller/pipe/sciosfs/event/pingable/ =
IOR:010000002d00000049444c3a6470742e73747363692e6564752f4f5055532f4f5055535f49444c2f50696e6761626c653a312e3000000000020000000000000074000000010102630e0000006f646f616c706861352d6d633000cd101b00000014010f005253549808d539212401000100000001000000020000000103000000000000000800000001000000004f415401000000140000000143184001000100000000000901010000000000004f41540400000001430000000000007c00000001010263140000006f646f616c706861352e73747363692e65647500cd100f001b00000014010f005253549808d539212401000100000001000000020000000003000000000000000800000001431840004f415401000000140000000143184001000100000000000901010000000000004f41540400000001430000
wmiller =
IOR:010000003000000049444c3a6470742e73747363692e6564752f4f5055532f4f5055535f49444c2f454e562f4f707573456e763a312e3000020000000000000084000000010102000e0000006f646f616c706861362d6d633000d60f2b00000014010f004e555000000011000000000100000000776d696c6c6572000000000001000000776d696c6c65720003000000000000000800000001542040004f415401000000140000000154204001000100000000000901010000000000004f41540400000001540000000000008c00000001010200140000006f646f616c706861362e73747363692e65647500d60f0f002b00000014010f004e555000000011000000000100000000776d696c6c6572000000000001000000776d696c6c65720003000000000000000800000001542040004f415401000000140000000154204001000100000000000901010000000000004f41540400000001540000
It is important to note that the user only configures the server command-line entries in opus_corba_objs—everything else is managed by the system automatically, including the IOR database. The command-line syntax for each of the servers is discussed later in this document.
Since it is possible for more than one client to trigger the start up sequence for a server at the same time (e.g., two pipeline processes that poll the same OSF blackboard brought up at the same time), provisions are made to ensure that only one instance of the server is executed by CORBA_obj_helper::get_object. The first client that is able to add a “starting flag” to the opus_corba_objs resource file (the flag is an entry composed of “.STARTING” appended to the object name as the keyword, and a time stamp as the value) executes the server; all other clients wait for the object’s ping response. The server removes the starting flag entry once it comes up, so should it be brought down later another instance could be started by get_object. The exception to this automated startup mechanism is opus_env_server, for which the user must take the responsibility for ensuring that it is running before starting either of the managers (the managers can be run outside of the OPUS environment, so they cannot use the IOR blackboard to start opus_env_server). A utility is provided (opus_server_monitor) that helps in this regard: it pings the user’s OpusEnv object, then exits. If the ping fails, it starts the server. One strategy for maintaining opus_env_server would be to configure one’s login scripts so that opus_server_monitor is run each time opus_login.csh is sourced, although this comes at the expense of a more lengthy login sequence.
Once the servers are started, there is no mechanism for terminating them except through the operating system. Lifecycle issues are complicated in CORBA, particularly when objects have persistent roles (e.g., blackboard). A more thorough examination of server lifecycle is left as a future enhancement to the system.
The following diagrams illustrate the set of network connections and servers created by an OPUS user running both of the managers and several applications distributed across multiple paths in the traditional file system-based and new CORBA-based blackboard environments. Each of the 3-D figures symbolizes a process running on an arbitrary node. The network connections between the processes are color-coded to the interface used by that connection. For example, the OPUS pipeline applications instantiate CORBA_osf_bb and CORBA_pstat_bb objects to communicate with the blackboard servers through the IDL Blackboard interface in the upper diagram, and File_osf_bb and File_pstat_bb objects to communicate with the file system in the lower diagram (the “~” notation in the legend indicates that the object on the left is an adaptor for the object on the right). The dashed line represents the socket interface used by the managers to obtain the IOR for OpusEnv.


At the node level, these scenarios might look like:

opus_env_server [[-d OPUS_DEBUG_LEVEL] [-t thread_pool_size]
[[ORB_argument] …]
where:
|
OPUS_DEBUG_LEVEL |
One of the enumerated message report levels of the OAPI
Msg class (e.g., ALL, INFO, …). |
DEFAULT: INFO |
|
thread_pool_size |
The number of threads to create for servicing clients
of the bootstrap server. |
DEFAULT: 3 |
|
ORB_argument |
Any of the ACE/TAO ORB command-line options. |
DEFAULT: ACE/TAO default ORB |
This server activates the OpusEnv object used by the managers to gain access to the user’s OPUS environment and to start processes. At any given time, only one instance of opus_env_server runs per OPUS user (the server associates itself with the user running the process). The servant that incarnates OpusEnv (OpusEnv_impl) uses a Proc_restrictions object (which requires access to the user’s PSTAT blackboard) to determine whether or not a request by the PMG to start a process on a given node and in a given path should be honored based on the contents of the pmg_restrictions.dat file and the state of the PSTAT blackboard. The server also creates additional threads that establish and manage the bootstrap socket interface to which the PMG and OMG connect at startup to retrieve the IOR for the OpusEnv object. The bootstrap server listens on an ephemeral port for connection attempts by the PMG and OMG, and once a connection is established, authentication takes place through token exchange, and then the IOR for OpusEnv is transmitted over the socket to the manager. The managers FTP the port address and the authentication token from the file .opus_env_server written in the user’s login directory (with user read/write permissions) by the server at startup.
Before the main thread relinquishes control to the ORB, the starting flag is removed from the file opus_corba_objs and the IOR for the OpusEnv object is posted to the IOR blackboard.
The relationship among objects in the server is illustrated in the following diagram:

opus_event_service –i event_channel_ID [[-d
OPUS_DEBUG_LEVEL] [[ORB_argument]…]
where:
|
event_channel_ID |
The string name of the event channel (i.e., the
associated blackboard name + “/event/”). |
|
OPUS_DEBUG_LEVEL |
One of the enumerated message report levels of the OAPI
Msg class (e.g., ALL, INFO, …). |
DEFAULT: INFO |
|
ORB_argument |
Any of the ACE/TAO ORB command-line options. |
DEFAULT: ACE/TAO default ORB |
Each Blackboard object has an associated event channel over which events are broadcast as changes occur on that blackboard. The servant that incarnates the EventChannel object is part of the TAO distribution, so unlike Blackboard and OpusEnv, it does not inherit the Pingable interface used by CORBA_obj_helper::get_object to determine whether the server is up or needs to be started. Instead, a Pingable object is incarnated by the Pingable_impl servant in the same POA as the EventChannel for this purpose.
Before the main thread relinquishes control to the ORB, the starting flag is removed from the file opus_corba_objs and the IOR for the OpusEnv object is posted to the IOR blackboard.
The relationship among objects in the server is illustrated in the following diagram:

opus_bb_server –i blackboard_ID –t blackboard_type {PSTAT
| OSF} [[-r polling_interval] [-d OPUS_DEBUG_LEVEL] [[ORB_argument]…]
where:
|
blackboard_ID |
The string name of the blackboard. |
|
blackboard_type |
Either “PSTAT” or “OSF” depending on the type of
blackboard served. |
|
OPUS_DEBUG_LEVEL |
One of the enumerated message report levels of the OAPI
Msg class (e.g., ALL, INFO, …). |
DEFAULT: INFO |
|
ORB_argument |
Any of the ACE/TAO ORB command-line options. |
DEFAULT: ACE/TAO default ORB |
|
polling_interval |
When BB_TYPE = FILE, the number of seconds between
polling attempts. |
DEFAULT: 5 |
An instance of opus_bb_server runs for each PSTAT and OSF blackboard configured by the user, but only after the blackboard is accessed by a client (PMG, OMG, pipeline application, or a utility like osf_update). Depending on the value of BB_TYPE in the file opus.env, either an instance of Mt_cache_bb or File_bb_event_poller is instantiated for use by the Blackboard_impl servant. If BB_TYPE is set to FILE, an instance of File_bb_event_poller is created; if BB_TYPE is set to CORBA, an instance of Mt_cache_bb is created. Both Mt_cache_bb and File_bb_event_poller expect a pointer to a file system-based blackboard (either File_pstat_bb or File_osf_bb, depending on the blackboard type) passed to their constructors along with a pointer to the event manager object (COSEC_event_mgr in both cases). The Mt_cache_bb class is described in a separate design document (see A Multi-threaded Caching Blackboard for the OAPI: Design Document). File_bb_event_poller is a simple proxy for the underlying file system-based blackboard; however, its constructor creates a new thread that polls the entire blackboard contents at regular intervals (user configurable), and generates events based on changes deduced from consecutive polling results.
Before the main thread relinquishes control to the ORB, the starting flag is removed from the file opus_corba_objs and the IOR for the OpusEnv object is posted to the IOR blackboard.
The relationship among objects in the server for the case BB_TYPE = CORBA is illustrated in the following diagram:

[How does this design allow the program to be used in different situations, handle different kinds of data, and change the behavior without changing the code?]
To a great extent, the design is object-oriented and encapsulates key functionality in classes or behind generic interfaces. The implementation behind the interfaces is decoupled from the clients that use them. In cases where it made sense to do so, abstract interfaces were developed for some class of functionality, and then specialized implementations were developed. This approach is more flexible should new implementations of the functionality be required in the future.
[Where does the flexibility stop?]
The mechanism for automatically starting the servers contains knowledge about the range of server types, and does not extend to opus_env_server directly. The system associates object names with server types in a non-user configurable way. The lifespan of the server processes has not been addressed (once brought up, there is no way to shut them down short of killing them).
[What assumptions must not be violated or the program will fail?]
The user must maintain opus_env_server else the managers will not function.
[What extensions might be considered in the future, but do not fall under the scope of the current project?]
Development of various tools to manage the CORBA servers and collect performance statistics is made possible, but not fulfilled, by the design. A mechanism for shutting down the servers on command or after a period of inaction might be developed as a future enhancement.
[What areas are the trickiest to implement? These are candidates for bugs, so more careful review should be focused here.]
Error handling and automatic startup of the servers are the most difficult aspects of the design. The managers and pipeline applications will not function if the objects they need are or become unavailable. Error recovery must be implemented to the fullest extent possible. Many processes/object are multithreaded, which mandates careful attention to thread synchronization. Much of the software is subject to memory leaks since C++ is the primary language, and the IDL to C++ mapping places memory management responsibility on the developer.
[What existing packages/modules will be reused in this
design? What enhancements to these modules will be needed? Are there any
candidates created in this design for reuse by other projects?]
Extensive reuse and extension of the OAPI were employed in this design. The resource blackboard class and infrastructure may be useful for other projects.






#ifndef OPUS_CORBA_BB_IDL_LOADED
#define OPUS_CORBA_BB_IDL_LOADED
#pragma prefix "dpt.stsci.edu"
module OPUS {
struct Field {
long id;
long len;
string value;
boolean unique;
};
typedef sequence<Field> Entry;
typedef string ErrMsg;
typedef sequence<Entry>
EntryList;
typedef string BlackboardID;
typedef string BlackboardType;
exception BadVal {
ErrMsg msg;
};
exception NoEntry {
ErrMsg msg;
};
exception Already {
ErrMsg msg;
};
exception Locked {
ErrMsg msg;
};
exception Exec {
ErrMsg msg;
};
interface Pingable {
void ping();
};
typedef sequence<long> Gauges;
struct Statistics {
long baseline;
Gauges stats;
};
interface Gaugable {
void resetStats();
Statistics getStats();
};
module BB {
typedef string LockID;
struct EntryLock {
LockID id;
Entry lockedEntry;
};
struct BBStateInfo {
BlackboardID id;
BlackboardType bbType;
Entry entryInfo;
};
interface Blackboard : Pingable,
Gaugable {
BBStateInfo getState();
enum COMMAND { HALT, SUSPEND,
RESUME, REINIT, DLETE, CREATE, MODIFY };
void post(in Entry e)
raises(BadVal, Already, Exec);
void erase(in Entry e)
raises(BadVal, NoEntry, Exec);
void replace(in Entry oldEntry,
in Entry newEntry) raises(BadVal,
NoEntry,
Exec);
long search(in Entry criterion,
out EntryList matches) raises(BadVal,
Exec);
EntryLock lockEntry(in Entry
target) raises(BadVal, Locked, Exec);
void unlockEntry(in LockID lid)
raises(BadVal, Already);
long getAll(out EntryList
contents) raises(BadVal, Exec);
boolean doManagerTask(in
COMMAND cmd, in Entry target) raises(BadVal,
NoEntry,
Exec);
};
};
module ENTRY_EVENT {
enum EVENT_TYPE { CREATED,
DELETED, MODIFIED };
struct EntryEvent {
long id;
Entry theEntry;
EVENT_TYPE eventType;
};
};
module ENV {
typedef sequence<string>
ResourceNames;
struct StageStatus {
char c;
string description;
};
typedef
sequence<StageStatus> StatusDef;
struct StageInfo {
string title;
string description;
long position;
StatusDef cstatus;
StatusDef nstatus;
StatusDef tstatus;
StatusDef pstatus;
};
typedef sequence<StageInfo>
Pipeline;
struct NodeInfo {
string nodeName;
string userName;
};
typedef sequence<NodeInfo>
NodeList;
struct ProcInfo {
string procName;
string path;
string authKey;
string commandLine;
};
typedef sequence<ProcInfo>
ProcList;
enum StartFailureType { RESTRICTED,
NOAUTH, NOSUCHPROC, NOSUCHNODE,
NOSUCHPATH, RUNTIME };
struct ProcFailInfo {
long procNum;
StartFailureType reason;
ErrMsg msg;
};
exception StartFailure {
sequence<ProcFailInfo>
failedProcs;
};
interface OpusEnv : Pingable,
Gaugable {
readonly attribute string
OPUS_HOME_DIR;
long getPathNames(out
ResourceNames paths);
long getResourceNames(out
ResourceNames resourceFiles);
long getPipelineNames(out
ResourceNames resourceFiles);
string getPathValue(in string
pathName, in string key)
raises(BadVal,
NoEntry);
string getResourceValue(in
string resourceName, in string key)
raises(BadVal,
NoEntry);
string getKeyValue(in string
fileSpec, in string key)
raises(BadVal,
NoEntry);
Pipeline getPipeline(in string
pathName) raises(BadVal);
string getFilePath(in string
name) raises(BadVal);
long getNodeInfo(out NodeList
nodes);
string getIOR(in in string name) raises(Exec);
void startProcesses(in ProcList
procs, in NodeList nodes)
raises(StartFailure);
};
};
};
#endif
· Namespace CORBA_obj_helper : Contains functions for retrieving a reference to a CORBA object from the IOR blackboard given its string name, and for starting the object server if it is not running.
o getObject(string object name) (Given an object’s string name, fetch a reference to the object from the IOR blackboard, starting the object’s server if necessary)
{
loop over number of attempts (3) {
call get_object on Ior_bb;
if the returned object reference is not NIL {
narrow object to Pingable;
if narrow fails, append “pingable/” to the string object name & continue;
else call ping() on the object, and if successful, break out of loop;
}
if the server had to be started, sleep for a few seconds & continue;
else call start_object_server with a new Ior_bb entry created from the string object name;
}
return object narrowed to requested type;
}
o start_object_server(Entry pointer) (Attempt to start the server for the indicated object)
{
instantiate a Resource_bb for OPUS_DEFINITIONS_DIR:opus_corba_objs;
search for requested entry;
if not found {
if entry matches user name {
add new CONTEXT entry to Resource_bb;
else if entry matches definition of OPUS_HOME_DIR {
add new PSTAT entry to Resource_bb;
add corresponding EVENT channel entry to Resource_bb;
else entry must be an OSF blackboard {
add new OSF entry to Resource_bb;
add corresponding EVENT channel entry to Resource_bb;
}
}
add “.STARTING” entry for the object to the Resource_bb, trapping exception in case already starting (if so, return);
search Resource_bb for object type (CONTEXT, PSTAT, OSF, EVENT) to get command-line for starting server;
perform “^F” substitution for string object name on command-line;
spawn new process;
}
o get_exception_text(CORBA exception) (Capture ostream CORBA exception information in a string)
{
send CORBA exception to output string stream using output operator;
return captured string;
}
· Class BBID : CORBA Blackboard identification object.
o BBID(string object name, string blackboard type) (Constructor)
{
store blackboard name and type strings;
}
o Get_type() (Get the string object name for the Blackboard)
{
return string object name;
}
o Get_type() (Get the Blackboard type [PSTAT or OSF])
{
return Blackboard type;
}
· Class ORB_Helper : ORB singleton class.
o instance(command-line arguments) (Initialize the ORB and/or return a pointer to the ORB object)
{
if not initialized, call CORBA::ORB_init with command-line arguments;
return pointer to the ORB;
}
o is_inited() (Returns true if the ORB has been initialized, else returns false)
{
return true if ORB is initialized;
return false;
}
o resolve_init(string object well-known name) (Calls resolve_initial_reference on the ORB, then narrows the object to the requested type)
{
call resolve_initial_reference on the ORB with the object name;
if the returned reference is not NIL, return narrowed object;
else return NIL;
}
· Class BB_Helper : Static helper class for adapting CORBA Blackboard calls to OAPI Blackboard calls.
o Init_osf_fields(OAPI Entry) (Initialize the ID-to-Field object map for OSF Entry’s)
{
loop over entry size {
call get_Nth_field on entry;
place field in map indexed by ID;
}
}
o Init_pstat_fields(OAPI Entry) (Initialize the ID-to-Field object map for PSTAT Entry’s)
{
loop over entry size {
call get_Nth_field on entry;
place field in map indexed by ID;
}
}
o Create_oapi_entry(Blackboard type, CORBA Entry) (Convert a CORBA Entry to an OAPI Entry)
{
lock mutex;
if blackboard type is OSF, create new OAPI CORBA_osf Entry object & call init_osf_fields if not inited;
else if blackboard type is PSTAT, create new OAPI CORBA_pstat Entry object & call init_pstat_fields if not inited;
loop over CORBA entry size {
clone OAPI Field in ID/Field map that matches CORBA Field ID;
assign value of CORBA Field to OAPI Field;
set field in OAPI Entry;
}
unlock mutex;
return OAPI Entry;
}
o Create_corba_entry(Blackboard type, OAPI Entry) (Convert an OAPI Entry to a CORBA Entry)
{
create new CORBA Entry;
loop over OAPI Entry size {
call get_Nth_field on OAPI Entry;
create new CORBA Field, and set structure values based on OAPI Field parameters;
add CORBA Field to CORBA Entry;
}
return CORBA Entry;
}
o Generate_ID(Blackboard type, OAPI Entry) (Create a string ID for an Entry composed of the concatenated unique field values)
{
lock mutex;
if blackboard type is OSF and not inited, call init_osf_fields with OAPI Entry;
else if blackboard type is PSTAT and not inited, call init_pstat_fields with OAPI Entry;
iterate over appropriate ID/Field map {
if call to Bb_params_db::is_unique is true for a Field in the map {
clone map Field;
get copy of OAPI Entry field by calling get_field with clone;
append str() call results on Field to ID string;
}
}
return ID string;
}
o Get_enum_chars(BB_Helper enum type) (Convert an enumerated type to string blackboard type)
{
return either “PSTAT” or “OSF” based on enumerator argument;
}
o Get_entry_info(Blackboard type) (Create a CORBA Entry where the Field values are set to the OAPI Field names)
{
create new CORBA Entry;
if blackboard type is OSF, create new OAPI CORBA_osf Entry object;
else if blackboard type is PSTAT, create new OAPI CORBA_pstat Entry object;
loop over OAPI Entry size {
call get_Nth_field on OAPI Entry;
create new CORBA Field, and set structure values based on OAPI Field parameters, but set the field value to the result of name() called on the OAPI Field;
add CORBA Field to CORBA Entry;
}
return CORBA Entry;
}
o Create_search_entry(Blackboard type) (Create a CORBA Entry where the Field values are set to the OAPI Field wildcards)
{
create new CORBA Entry;
if blackboard type is OSF, create new OAPI CORBA_osf Entry object;
else if blackboard type is PSTAT, create new OAPI CORBA_pstat Entry object;
call search_fill_all on the OAPI Entry;
return call to Create_corba_entry;
}
o Create_updated_entry(Blackboard type, CORBA Blackboard COMMAND type, OAPI Entry, OAPI Entry) (Given an OAPI Entry and a manager issued command, create the updated OAPI Entry)
{
call clone on the Entry object to create updated Entry;
if blackboard type is OSF {
if COMMAND is “MODIFY” {
create new Obs_stat object;
call get_field on update Entry with Obs_stat object;
create new Obs_stat object;
call get_field on current Entry with Obs_stat object;
call operator% on current Obs_stat object with update Obs_stat object;
} else {
if COMMAND is “SUSPEND”, create new Obs_cmd object with Obs_cmd::OSF_SUSPEND_COMMAND;
else create new Obs_cmd object with Obs_cmd::OSF_HALT_COMMAND;
}
} else {
if COMMAND is “SUSPEND”, create new Proc_cmd object with Proc_cmd::PSTAT_SUSPEND_COMMAND;
else if COMMAND is “RESUME”, create new Proc_cmd object with Proc_cmd::PSTAT_RESUME_COMMAND;
else if COMMAND is “REINIT”, create new Proc_cmd object with Proc_cmd::PSTAT_REINIT_COMMAND;
else if COMMAND is “HALT”, create new Proc_cmd object with Proc_cmd::PSTAT_HALT_COMMAND;
}
call set_field on the updated Entry object with the Field object;
return the updated Entry;
}
· Class Proc_restrictions : Determine whether a request to start a process by the PMG can be honored based on the current state of the PSTAT blackboard and the contents of pmg_restrictions.dat.
o Proc_restrictions(restrictions file name, Pstat_bb pointer) (Constructor)
{
load the restrictions file into an Oresource object;
}
o ~Proc_restrictions() (Destructor)
{
loop over entries in restricted entries map {
if the list of Entry object pointers for the map entry is not empty, delete the first Entry;
delete the list;
}
}
o is_runnable(ProcInfo reference, NodeInfo reference) (Determine if the indicated process can be run in the indicated path on the indicated node)
{
create a new ProcList;
place ProcInfo object in ProcList;
create a new NodeList;
place NodeInfo object in NodeList;
call are_runnable with the list objects;
return the results;
}
o are_runnable(ProcList reference, NodeList reference, Boolean results list) (Determine if the indicated processes can be run in the indicated paths on the indicated nodes)
{
validate arguments;
call new_pstat on the PSTAT blackboard;
call search_fill_all on the PSTAT;
call search on the PSTAT blackboard with the wildcarded entry;
loop over items in ProcList {
get the process name from the ProcInfo procName field;
search the restricted entries map for this process, and if not found, call add_restricted_entries;
set the Node, Process, and Path Fields of a Pstat object with the procName, nodeName and path elements of the ProcInfo and NodeInfo objects;
count the number of Entry’s matching this one in the PSTAT Blackboard search results;
count the number of Entry’s matching this one in the restricted entries map;
if there are no matching restricted entries or the number of matches on the PSTAT blackboard is less than the number in the restricted entries map {
add a copy of the PSTAT to the blackboard search results;
set element in Boolean results list to true;
} else {
set element in Boolean results list to false;
}
}
}
o add_restricted_entries(string process name) (Add the named process to the restricted entries map according to the contents of the pmg_restrictions.dat file)
{
call new_pstat on the PSTAT blackboard to get Pstat object;
call search_fill_all on the Pstat object;
create a new Process object with the process name, and add it to the Pstat object using set_field;
get all entries for this process from pmg_restrictions.dat by calling get_class on the Oresource object;
create a new vector of Entry pointers object;
loop over pmg_restrictions.dat entries {
if entry specifies any path / any node, then clone the Pstat object and push it into the vector;
else if entry specifies any path on a single node, then create a Node object with the specified node, add it to the Pstat object, and push a clone of the Pstat into the vector;
else if entry specifies any node on a single path, then create a Path object with the specified path, add it to the Pstat object, and push a clone of the Pstat into the vector;
else create both Node and Path objects with the specified values, add them to the Pstat object, and push a clone of the Pstat into the vector;
loop over number of instances specified by the entry {
push the clone of the Pstat object into the vector;
}
}
create a new entry in the restricted entries map for the process containing the vector;
}
· Class Resource_bb : An OAPI Blackboard derivative that permits access to a resource file through the Blackboard interface. Entries on the blackboard map to keyword/value pairs (the entry fields) in the resource file, and entry locking is supported at this level.
o Resource_bb(string resource file spec) (Constructor)
{
create new File_entry object using file spec;
save string file spec;
}
o Resource_bb(string resource file spec, Oresource reference) (Constructor)
{
if Oresource is loaded, purge it;
create new File_entry object using file spec;
save string file spec;
}
o ~Resource_bb() (Destructor)
{
delete File_entry object;
}
o post(Entry pointer) (Post a new keyword/value pair to the resource file)
{
validate argument;
get the Resource_key and Resource_value fields from the argument Entry, then save their string equivalents;
lock the resource file using the files blackboard;
load the resource file into an Oresource object;
call add_item on the Oresource object with the keyword/value pair;
open the resource file as a file output stream;
call report on the resource object with the file output stream to rewrite the file;
delete the lock on the resource file;
}
o erase(Entry pointer) (Remove a keyword/value pair from the resource file)
{
validate argument;
get the Resource_key field from the argument Entry, then save the string equivalent;
lock the resource file using the files blackboard;
load the resource file into an Oresource object;
call remove_item on the Oresource object with the keyword string;
open the resource file as a file output stream;
call report on the resource object with the file output stream to rewrite the file;
delete the lock on the resource file;
}
o replace(Entry pointer, Entry pointer) (Replace a keyword/value pair in the resource file with another)
{
validate arguments;
get the Resource_key and Resource_value fields from the argument Entry objects, then save their string equivalents;
lock the resource file using the files blackboard;
load the resource file into an Oresource object;
call remove_item on the Oresource object with the old keyword;
call add_item on the Oresource object with the new keyword/value pair;
open the resource file as a file output stream;
call report on the resource object with the file output stream to rewrite the file;
delete the lock on the resource file;
}
o search(Entry pointer, vector of Entry pointers reference) (Search for a keyword/value pair in the resource file)
{
validate arguments;
get the Resource_key field from the argument Entry, then save the string equivalent;
lock the resource file using the files blackboard;
load the resource file into an Oresource object;
if the search keyword looks like a class specification, call get_class on the Oresource object with the keyword;
else call get_value on the Oresource object with the keyword;
stuff any values found into new Resource_entry objects, then push into the vector argument;
delete the lock on the resource file;
return the number of values found;
}
o lock_entry(Entry pointer) (Lock a keyword/value record in the resource file)
{
validate argument;
create a new Opus_resource_lock object for the resource file and Entry argument;
call lock on the Opus_resource_lock object;
return the Opus_resource_lock object;
}
o str() (Get the blackboard name)
{
return a string describing the resource blackboard;
}
o test_driver() (Test the functionality of the class)
{
attempt to add an entry to the resource file;
attempt to replace that entry with another;
attempt to search for replaced entry;
attempt to lock the replaced entry;
attempt to delete the replaced entry;
report on success or failure;
}
· Class Resource_entry : An OAPI Entry that represents a single keyword/value pair on the resource blackboard. A Resource_entry object contain Resource_key and Resource_value fields.
o Resource_entry(string key, string value) (Constructor)
{
create new Resource_key object using key string;
push into container;
create new Resource_value object using value string;
push into container;
}
o Resource_entry(Resource_entry reference) (Constructor)
{
call base copy constructor;
}
o ~Resource_entry() (Destructor)
{
no-op;
}
o clone() (Create a new Resource_entry object identical to this object)
{
return new Resource_entry object using this copy constructor;
}
o str() (Get a string representation of the entry)
{
return string key and string value;
}
· Class Resource_key : An OAPI Field that represents the keyword in a Resource_entry.
o Resource_key(string key) (Constructor)
{
call assign on the base with the key name;
}
o Resource_key(Resource_key reference) (Constructor)
{
call base copy constructor;
}
o ~Resource_key() (Destructor)
{
no-op;
}
o clone() (Create a new Resource_key object identical to this object)
{
return new Resource_key object using this copy constructor;
}
o name() (Get the field name)
{
return name constant;
}
o has_class_spec() (Does the value look like a resource class?)
{
get unpadded string key;
if key == “*.*” or (the key is not null and the first character is ‘.’) or (the key is not null and the last character is ‘.’) then return true;
otherwise, return false;
}
· Class Resource_value : An OAPI Field that represents the value in a Resource_entry.
o Resource_value(string value) (Constructor)
{
call assign on the base with the value;
}
o Resource_value(Resource_value reference) (Constructor)
{
call base copy constructor;
}
o ~Resource_value() (Destructor)
{
no-op;
}
o clone() (Create a new Resource_value object identical to this object)
{
return new Resource_value object using this copy constructor;
}
o name() (Get the field name)
{
return name constant;
}
· Class Opus_resource_lock : An OAPI Opus_lock derivative that is returned by the lock_entry member function of Resource_bb. This class performs locking of single keyword/value pairs on the resource blackboard.
o Opus_resource_lock() (Constructor)
{
create new File_entry object based on the lock database name;
}
o Opus_resource_lock(string resource file name, Resource_entry pointer) (Constructor)
{
call assign with the resource file name & entry pointer;
create new File_entry object based on the lock database name;
}
o ~Opus_resource_lock() (Destructor)
{
if locked, call release;
delete lock database File_entry object;
}
o assign(string resource file name, Resource_entry pointer) (Assign the target entry for this lock)
{
validate arguments;
if locked, call release;
save string key name from Resource_entry using Resource_key;
}
o lock() (Lock the target entry)
{
loop over retry counter {
attempt to lock the lock database file;
load the lock database into an Oresource object;
add the target key string and resource file name to the Oresource object with add_item;
open the lock database file & rewrite the file;
unlock the lock database file;
break from loop;
}
}
o release() (Release the lock on the target entry)
{
if locked {
attempt to lock the lock database file;
load the lock database into an Oresource object;
call remove_item on the Oresource object to remove the target entry;
open the lock database file & rewrite the file;
unlock the lock database file;
}
}
o operator==() (Equality test for Opus_lock objects)
{
if object is this one, return true;
if object is not downcastable to Opus_resource_lock, return false;
else if the keys and resource file names match, return true
else return false;
}
· Class Ior_bb : A Resource_bb derivative designed to function as a CORBA IOR blackboard.
o Ior_bb() (Constructor)
{
construct base with IOR database file name;
}
o ~Ior_bb() (Destructor)
{
no-op;
}
o str() (Get string blackboard name)
{
return string constant for IOR blackboard;
}
o new_ior_entry() (string object name, string IOR)
{
strip any duplicate ‘/’ characters from the string object name;
add a trailing ‘/’ character to the string object name if not present;
construct and return a new Resource_entry based on the scrubbed object name and IOR;
}
o get_object(string object name) (Look up the object on the IOR blackboard, then convert the IOR to a CORBA object pointer)
{
call new_ior_entry with string object name to get search condition;
call search with the search condition;
if a match is found {
delete the condition;
get the IOR from the matching Resource_entry using a Resource_value object and get_field on the entry;
get ORB reference using ORB_Helper::instance;
call string_to_object on the ORB with the IOR;
delete any other matches on the blackboard;
return the CORBA object;
} else {
delete the condition;
return NIL CORBA object;
}
}
o post_object(string object name, CORBA object pointer) (Create a new entry on the IOR blackboard from a CORBA object)
{
get ORB reference using ORB_Helper::instance;
call object_to_string on ORB with CORBA object pointer to get IOR;
call new_ior_entry with the object name and IOR;
lock the entry using lock_entry;
post the entry with post;
delete the lock;
}
· Class Entry_event_mgr : Abstract base class for blackboard event management.
o Entry_event_mgr(BBID reference) (Constructor)
{
save BBID copy;
}
o ~Entry_event_mgr() (Destructor)
{
no-op;
}
o notify(Entry pointer, Entry pointer, EVENT_TYPE enumerator, integer event ID) (Pure virtual function for generating an event based on changes to an entry on the blackboard)
o get_unique_fields(Entry pointer, list of Field pointers reference) (Given an Entry, get a list of the unique fields)
{
call get_type on BBID;
loop over Entry size {
call get_Nth_field on Entry object;
call Bb_params_db::is_unique with the Field pointer and blackboard type string;
if Field is unique, push onto list;
else delete object;
}
}
o get_updated_fields(Entry pointer, Entry pointer, EVENT_TYPE enumerator, list of Field pointers reference) (Create a list of Field objects that constitute the changed elements of the Entry object given the event type)
{
switch on event type {
case CREATED:
call get_type on BBID;
loop over Entry size {
call get_Nth_field on Entry object;
call Bb_params_db::is_unique with the Field pointer and blackboard type string;
if Field is not unique, push onto list;
else delete object;
}
break from switch;
case MODIFIED:
call get_type on BBID;
loop over first Entry size {
call get_Nth_field on first Entry object;
call get_Nth_field on second Entry object;
call Bb_params_db::is_unique with the first Field pointer and blackboard type string;
if Field is not unique and Field objects are equal (type), push onto list;
else delete objects;
}
break from switch;
}
}
· Class COSEC_event_mgr : An Entry_event_mgr derivative that generates and pushes events onto a COS Event Channel.
o COSEC_event_mgr(BBID reference) (Constructor)
{
construct base with BBID;
initialize mutex;
}
o ~COSEC_event_mgr() (Destructor)
{
if connected, call disconnect_push_consumer on the PushConsumer object;
destroy the mutex;
}
o notify(Entry pointer, Entry pointer, EVENT_TYPE enumerator, integer event ID) (Generate an event based on changes to an entry on the blackboard)
{
lock mutex;
if not connected, call connect;
unlock the mutex;
create EntryEvent object;
set the id field of EntryEvent to the ID calling argument;
call get_unique_fields;
switch on the event type {
case CREATED:
set the eventType field of EntryEvent to CREATED;
call get_updated_fields;
break from switch;
case DELETED:
set the eventType field of EntryEvent to DELETED;
break from switch;
case MODIFIED:
set the eventType field of EntryEvent to MODIFIED;
call get_updated_fields;
break from switch;
}
call build_event_entry;
stuff EntryEvent into CORBA Any object;
push Any object onto the Event Channel using push on the consumer object;
}
o build_event_entry(list of unique Field pointers reference, list of modified Field pointers reference, CORBA Entry reference) (Place the unique and modified fields in the CORBA Entry)
{
set the size of the CORBA Entry using length to the number of unique and modified fields;
loop over the unique Field objects {
create a CORBA Field object;
set the Field id field to the return value of id called on the OAPI Field object;
set the Field len field to the return value of size called on the OAPI Field object;
set the Field uniqie field to true;
set the Field value field to the return value of str called on the OAPI Field object;
add the Field to the Entry;
}
loop over the modified Field objects {
create a CORBA Field object;
set the Field id field to the return value of id called on the OAPI Field object;
set the Field len field to the return value of size called on the OAPI Field object;
set the Field uniqie field to false;
set the Field value field to the return value of str called on the OAPI Field object;
add the Field to the Entry;
}
}
o connect() (Connect to the Event Channel)
{
if not connected {
call get_id on BBID to get blackboard name;
append “event/” to the blackboard name to get the event channel name;
call CORBA_obj_helper::get_object with the event channel name to get the EventChannel object;
call for_suppliers on the EventChannel object to get the SupplierAdmin object;
call obtain_push_consumer on the SupplierAdmin object to get the PushConsumer object;
call connect_push_supplier on the PushConsumer with a NIL PushSupplier object;
set connected to true;
}
}
· Class Event_id : Provides a unique tag service for events.
o Event_id() (Constructor)
{
initialize the id to zero;
}
o ~Event_id() (Destructor)
{
no-op;
}
o operator>(Event_id reference) (Perform greater-than comparison with another Event_id object)
{
return this id > that id;
}
o operator==(Event_id reference) (Perform equality comparison with another Event_id object)
{
return this id == that id;
}
o operator int() (Convert the id to type int)
{
return the id;
}
o operator++() (Increment the Event_id object)
{
increment the id;
if the id exceeds the maximum value of a signed 32-bit integer, reset id to zero;
return reference to this object;
}
· Class CORBA_pstat_bb : An OAPI Pstat_bb derivative that adapts PSTAT blackboard calls to CORBA Blackboard object calls.
o CORBA_pstat_bb(string blackboard id) (Constructor)
{
save blackboard id;
call connect;
}
o ~CORBA_pstat_bb() (Destructor)
{
no-op;
}
o post(Entry pointer) (Post a new entry on the blackboard)
{
validate argument;
create a CORBA Entry from the OAPI Entry using BB_Helper::create_corba_entry;
loop over retry counter {
call the post method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
o erase(Entry pointer) (Remove an entry from the blackboard)
{
validate argument;
create a CORBA Entry from the OAPI Entry using BB_Helper::create_corba_entry;
loop over retry counter {
call the erase method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
}
o replace(Entry pointer, Entry pointer) (Replace an entry on the blackboard with a modified version)
{
validate arguments;
create CORBA Entry’s from the OAPI Entry’s using BB_Helper::create_corba_entry;
loop over retry counter {
call the replace method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
}
o search(Entry pointer, vector of Entry pointers reference) (Search for an entry on the blackboard)
{
validate argument;
create a CORBA Entry from the OAPI Entry using BB_Helper::create_corba_entry;
loop over retry counter {
call the search method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
loop over search results {
create an OAPI Entry from the CORBA Entry using BB_Helper::create_oapi_entry;
push the OAPI Entry into the results vector;
}
return the size of the results vector;
}
o lock_entry(Entry pointer) (Lock an entry on the blackboard)
{
validate argument;
create a CORBA Entry from the OAPI Entry using BB_Helper::create_corba_entry;
loop over retry counter {
call the lockEntry method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
return a new CORBA_pstat_lock object using the EntryLock object returned by the CORBA Blackboard;
}
o str() (Get the blackboard name)
{
loop over retry counter {
call the getState method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
return the id field of the BBStateInfo object returned by the getState method;
}
o test_driver() (Test the functionality of the class)
{
create a new entry by calling new_pstat;
attempt to add an entry to the blackboard;
attempt to replace that entry with another;
attempt to search for replaced entry;
attempt to lock the replaced entry;
attempt to delete the replaced entry;
call appl_pstat;
report on success or failure;
}
o new_pstat() (Create a new PSTAT entry for use with the blackboard)
{
return a new CORBA_pstat object;
}
o appl_pstat() (Get the application’s PSTAT from the blackboard)
{
create a new CORBA_pstat object;
call search_mask on the PSTAT;
call search with the PSTAT;
if a single match is found, delete the search PSTAT and return the result;
else if no match is found, delete the search PSTAT and throw an exception;
else if more than one match is found, delete the search PSTAT and throw an exception;
}
o connect() (Obtain a reference to the CORBA Blackboard object)
{
call CORBA_obj_helper::get_object with the blackboard id;
}
· Class CORBA_osf_bb : An OAPI Osf_bb derivative that adapts OSF blackboard calls to CORBA Blackboard object calls.
o CORBA_osf_bb(string blackboard id) (Constructor)
{
save blackboard id;
call connect;
}
o ~CORBA_osf_bb() (Destructor)
{
no-op;
}
o post(Entry pointer) (Post a new entry on the blackboard)
{
validate argument;
create a CORBA Entry from the OAPI Entry using BB_Helper::create_corba_entry;
loop over retry counter {
call the post method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
o erase(Entry pointer) (Remove an entry from the blackboard)
{
validate argument;
create a CORBA Entry from the OAPI Entry using BB_Helper::create_corba_entry;
loop over retry counter {
call the erase method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
}
o replace(Entry pointer, Entry pointer) (Replace an entry on the blackboard with a modified version)
{
validate arguments;
create CORBA Entry’s from the OAPI Entry’s using BB_Helper::create_corba_entry;
loop over retry counter {
call the replace method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
}
o search(Entry pointer, vector of Entry pointers reference) (Search for an entry on the blackboard)
{
validate argument;
create a CORBA Entry from the OAPI Entry using BB_Helper::create_corba_entry;
loop over retry counter {
call the search method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
loop over search results {
create an OAPI Entry from the CORBA Entry using BB_Helper::create_oapi_entry;
push the OAPI Entry into the results vector;
}
return the size of the results vector;
}
o lock_entry(Entry pointer) (Lock an entry on the blackboard)
{
validate argument;
create a CORBA Entry from the OAPI Entry using BB_Helper::create_corba_entry;
loop over retry counter {
call the lockEntry method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
return a new CORBA_osf_lock object using the EntryLock object returned by the CORBA Blackboard;
}
o str() (Get the blackboard name)
{
loop over retry counter {
call the getState method on the CORBA Blackboard;
catch exceptions and retry if not exceeded retry limit, else throw exception;
break from loop;
}
return the id field of the BBStateInfo object returned by the getState method;
}
o test_driver() (Test the functionality of the class)
{
create a new entry by calling new_osf;
attempt to add an entry to the blackboard;
attempt to replace that entry with another;
attempt to search for replaced entry;
attempt to lock the replaced entry;
attempt to delete the replaced entry;
report on success or failure;
}
o new_osf() (Create a new OSF entry for use with the blackboard)
{
return a new CORBA_osf object;
}
o connect() (Obtain a reference to the CORBA Blackboard object)
{
call CORBA_obj_helper::get_object with the blackboard id;
}
· template Class CORBA_entry_lock : An OAPI Opus_lock derivative that adapts Opus_lock calls to CORBA EntryLock object calls.
o CORBA_entry_lock(EntryLock pointer, CORBA Blackboard pointer) (Constructor)
{
save Blackboard and EntryLock references;
call getState on CORBA Blackboard;
save blackboard id in BBStateInfo id field;
set locked to true;
}
o ~CORBA_entry_lock() (Destructor)
{
if locked, call unlockEntry on the CORBA Blackboard with the EntryLock object;
}
o lock() (Lock the target)
{
if locked, return;
call lockEntry on the CORBA Blackboard with the EntryLock object;
set locked to true;
}
o release() (Unlock the target)
{
if not locked, return;
call unlockEntry on the CORBA Blackboard with the EntryLock object;
set locked to false;
}
o operator==(Opus_lock pointer) (Equality comparison for Opus_lock objects)
{
if not a CORBA_entry_lock object, return false;
if blackboard id’s do not match, return false;
create OAPI Entry objects from the target CORBA Entry objects using BB_Helper::create_oapi_entry;
call BB_Helper::generate_id with the OAPI Entry objects;
if they match, return true;
else return false;
}
o get_locked_entry() (Get a copy of the locked Entry object)
{
if not locked, throw exception;
return call to BB_Helper::create_oapi_entry with the EntryLock lockedEntry field;
}
· Class CORBA_osf_lock : A typedef of CORBA_entry_lock with BB_Helper::OSF enumerator.
· Class CORBA_pstat_lock : A typedef of CORBA_entry_lock with BB_Helper::PSTAT enumerator.
· Class CORBA_osf : A typedef of File_osf.
· Class CORBA_pstat: A typedef of File_pstat.
· Class Pingable_impl : Derived from the skeleton abstract base class Pingable, this class provides an implementation for the Pingable interface. The single member function, ping, allows a client to determine the state of the object based on the success or failure of this call.
o ping() (A no-op)
{
return;
}
· Class Blackboard_impl : Derived from the skeleton abstract base class Blackboard, this class provides an implementation for the Blackboard interface that adapts to the OAPI Blackboard interface.
o Blackboard_impl(OAPI Blackboard pointer, string blackboard name, BB_Helper blackboard type enumerator) (Constructor)
{
save OAPI Blackboard reference;
call resetStats;
copy blackboard name to character array for getState member function;
create character array with blackboard type for getState member function using BB_Helper::get_enum_chars;
}
o ~Blackboard_impl() (Destructor)
{
delete character arrays created in constructor;
loop over lock cache, deleting each lock;
}
o getState() (A fat operation for obtaining information about the blackboard object including blackboard name, blackboard type, and a blackboard entry that contains the OAPI field names stored in the corresponding field values)
{
increment stats counter;
create a new BBStateInfo object;
set the BBStateInfo id element to the blackboard name;
set the BBStateInfo bbType element to the blackboard type;
get the blackboard entry template using BB_Helper::get_entry_info, then stuff into BBStateInfo entryInfo element;
return the BBStateInfo object;
}
o post(Entry reference) (Post a new entry on the blackboard)
{
increment stats counter;
create an OAPI Entry from the CORBA Entry using BB_Helper::create_oapi_entry;
call post on the OAPI Blackboard with this entry;
delete the OAPI Entry;
}
o erase(Entry reference) (Remove an entry from the blackboard)
{
increment stats counter;
create an OAPI Entry from the CORBA Entry using BB_Helper::create_oapi_entry;
call erase on the OAPI Blackboard with this entry;
delete the OAPI Entry;
}
o replace(old Entry reference, new Entry reference) (Modify an entry on the blackboard)
{
increment stats counter;
create OAPI Entry’s from the CORBA Entry’s using BB_Helper::create_oapi_entry;
call replace on the OAPI Blackboard with these entries;
delete the OAPI Entry’s;
}
o search(Entry reference, EntryList output reference) (Search for an entry that matches the criterion on the blackboard)
{
increment stats counter;
call search_common with the calling arguments;
add the number of search hits to the hits stats counter;
return the size of the EntryList output object;
}
o lockEntry(Entry reference) (Lock an entry on the blackboard)
{
increment stats counter;
create an OAPI Entry from the CORBA Entry using BB_Helper::create_oapi_entry;
call lock_entry on the OAPI Blackboard with this entry;
delete the OAPI Entry;
call get_locked_entry on the Opus_lock object;
call BB_Helper::generate_id to get a string ID for the locked object;
place the Opus_lock object in the lock cache, indexed by the lock ID;
create a new EntryLock object;
set the id element of the EntryLock object to the lock ID;
create a CORBA Entry from the locked OAPI Entry using BB_Helper::create_corba_entry;
set the lockedEntry element of the EntryLock object to the CORBA Entry;
return the lockedEntry object;
}
o unlockEntry(character string lock ID) (Unlock a locked entry on the blackboard)
{
increment stats counter;
locate the OAPI lock in the lock cache using the ID;
delete the OAPI lock;
remove the element from the lock cache;
}
o getAll(EntryList output reference) (Do a wildcard search of the blackboard)
{
increment stats counter;
create an OAPI Entry from the CORBA Entry using BB_Helper::create_oapi_entry;
return call to search_common;
}
o search_common(Entry reference, EntryList output reference) (The core search routine for search and getAll)
{
create an OAPI Entry from the CORBA Entry using BB_Helper::create_oapi_entry;
create new EntryList object;
call search on the OAPI Blackboard;
loop over search results {
create a CORBA Entry from the OAPI Entry using BB_Helper::create_corba_entry;
place CORBA Entry in EntryList;
}
return the value returned by the search call;
}
o ping() (Implement Pingable interface)
{
increment stats counter;
}
o doManagerTask(CORBA COMMAND object reference, CORBA Entry reference) (Complete PMG/OMG-specific blackboard tasks in a single call)
{
increment stats counter;
create an OAPI Entry from the CORBA Entry using BB_Helper::create_oapi_entry;
call lock_entry on the OAPI Blackboard to lock this entry;
call get_locked_entry on the Opus_lock object to get current state of Entry as locked;
if the COMMAND is to HALT, SUSPEND, RESUME, REINIT or MODIFY the entry {
get the appropriate updated entry by calling BB_Helper::create_updated_entry;
call replace on the OAPI Blackboard to replace the old with the new entry;
}
else if the COMMAND is CREATE, call post on the OAPI Blackboard to create the entry;
else if the COMMAND is DELETE, call erase on the OAPI Blackboard to delete the entry;
delete the lock;
return the value returned by the search call;
}
o resetStats() (Zero all of the counters)
{
set each counter to zero;
set baseline time to the current time;
}
o getStats() (Implement the Gaugable interface)
{
create new Statistics object;
set the baseline element of the Statistics structure to the current time minus the baseline;
create a new Gauges object;
copy each counter into a Gauges element;
copy the Gauges object into the Statistics stats element;
return the Statistics object;
}
· Class OpusEnv_impl : Derived from the skeleton abstract base class OpusEnv, this class provides an implementation for the OpusEnv interface that provides user context and process startup support for the managers.
o OpusEnv_impl(OAPI Blackboard pointer, string blackboard name, BB_Helper blackboard type enumerator) (Constructor)
{
call resetStats;
call OSFILE_stretch_file with “OPUS_HOME_DIR:” to get OPUS home directory path;
create new CORBA_pstat_bb object using OPUS home directory path;
create new Proc_restrictions object for OPUS_DEFINITIONS_DIR:pmg_restrictions.dat;
}
o ~OpusEnv_impl() (Destructor)
{
delete CORBA_pstat_bb object;
delete Proc_restrictions object;
}
o OPUS_HOME_DIR() (Returns the OPUS home directory path [the PSTAT blackboard object name])
{
increment stats counter;
return OPUS home directory path;
}
o getPathNames(ResourceNames output reference) (Get the list of path files in OPUS_DEFINITIONS_DIR)
{
increment stats counter;
return call to getNames with calling argument and “OPUS_DEFINITIONS_DIR:*.path”;
}
o getResourceNames(ResourceNames output reference) (Get the list of process resource files in OPUS_DEFINITIONS_DIR)
{
increment stats counter;
return call to getNames with calling argument and “OPUS_DEFINITIONS_DIR:*.resource”;
}
o getPipelineNames(ResourceNames output reference) (Get the list of pipeline files in OPUS_DEFINITIONS_DIR)
{
increment stats counter;
return call to getNames with calling argument and “OPUS_DEFINITIONS_DIR:*.pipeline”;
}
o getNames(ResourceNames output reference, character string file specification) (Return file system search results in ResourceNames object)
{
create new ResourceNames object;
conduct OSFILE search, placing each matching file specification into ResourceNames object;
return the number of matches;
}
o getPathValue(character string path name, character string keyword) (Retrieve the value of a keyword from a path file)
{
increment stats counter;
return call to getKeyValue with calling arguments;
}
o getResourceValue(character string resource name, character string keyword) (Retrieve the value of a keyword from a process resource file)
{
increment stats counter;
return call to getKeyValue with calling arguments;
}
o getKeyValue(character string resource file name, character string keyword) (Retrieve the value of a keyword from an OPUS resource file)
{
increment stats counter;
if resource file is not loaded already, load it into an Oresource object;
call get_value with the keyword on the Oresource object;
return the value;
}
o getPipeline(character string path name) (Retrieve the contents of a pipeline stage file as a Pipeline object for a path)
{
increment stats counter;
load path file into an Oresource object;
call get_value on the Oresource object to retrieve the value of “STAGE_FILE”;
load the pipeline stage file into an Oresource object;
create a new Pipeline object;
loop over number of stages as indicated by the “NSTAGE” keyword value {
create new StageInfo object;
call readStatusDefs for each of the status types, then set the corresponding element of the StageInfo object;
add the StageInfo object to the Pipeline object;
}
return the Pipeline object;
}
o readStatusDefs(integer stage number, character string status type, Oresource object reference) (Extract the status definition info from the Oresource object for the indicated stage)
{
create a new StatusDef object;
call get_class on the Oresource object with the stage number plus status type;
loop over the resulting map {
set the c element of the StatusDef object to the status character value;
set the description element of the StatusDef object to the value of the entry;
}
return the StatusDef object;
}
o ping() (Implement Pingable interface)
{
increment stats counter;
}
o getFilePath(character string file name) (Resolve any stretch in the file name)
{
increment stats counter;
create a new Ofile object with the file name;
return the result of calling resolve on the Ofile object;
}
o getNodeInfo(NodeList output reference) (Get a list of host names known to the system)
{
increment stats counter;
create a new NodeList object;
while gethostent call returns a host name {
create a new NodeInfo object;
set the nodeName element of the NodeInfo object to the returned host name;
}
return the NodeList object;
}
o getIOR(character string object name) (Get the CORBA object reference for the named object)
{
increment stats counter;
call CORBA_obj_helper::get_object with the object name;
call ORB_Helper::instance to get a reference to the ORB;
return the result of calling object_to_string on the ORB with the CORBA object;
}
o startProcesses(ProcList reference, NodeList reference) (Start one or more pipeline processes)
{
increment stats counter;
create new StartFailure object;
call are_runnable on the Proc_restrictions object with the calling arguments;
loop over the number of processes to be started {
if process is not runnable {
create new ProcFailInfo object;
set the procNum element of the ProcFailInfo object to the loop counter;
set the reason element of the ProcFailInfo object to RESTRICTED;
set the msg element of the ProcFailInfo object to an information message;
add the ProcFailInfo object to the StartFailure object;
} else {
construct the command line for odcl_broker;
spawn odcl_broker task;
}
}
if any ProcFailInfo objects were added to the StartFailure object, throw the StartFailure object;
}
o resetStats() (Zero all of the counters)
{
set each counter to zero;
set baseline time to the current time;
}
o getStats() (Implement the Gaugable interface)
{
create new Statistics object;
set the baseline element of the Statistics structure to the current time minus the baseline;
create a new Gauges object;
copy each counter into a Gauges element;
copy the Gauges object into the Statistics stats element;
return the Statistics object;
}
main {
load OPUS_DEFINITIONS_DIR:opus.env into an Oresource object;
get the value of “BB_TYPE” using get_value;
parse command-line arguments;
initialize the ORB, start the POA_manager;
create new COSEC_event_mgr object;
if blackboard type is OSF {
if BB_TYPE is CORBA, create new Mt_cache_bb<File_osf> object with a File_osf_bb object as the persistent store;
else create new File_bb_event_poller<File_osf> object with a File_osf_bb object as the decorated Blackboard object;
} else if the blackboard type is PSTAT {
if BB_TYPE is CORBA, create new Mt_cache_bb<File_pstat> object with a File_pstat_bb object as the persistent store;
else create new File_bb_event_poller<File_pstat> object with a File_pstat_bb object as the decorated Blackboard object;
}
create new Blackboard_impl servant object with the OAPI Blackboard;
implicitly activate the Blackboard object by calling _this on the Blackboard_impl object;
get the object IOR by calling object_to_string on the ORB;
create new Ior_bb object;
create new Entry object by calling new_ior_entry on the Ior_bb object with the IOR and blackboard name;
call search on the Ior_bb object with the Entry object;
if the search found an Entry, call replace on the Ior_bb object with the new Entry;
else call post on the Ior_bb object with the Entry;
create new Resource_bb object with “OPUS_DEFINITIONS_DIR:opus_corba_objs”;
create new Resource_key object using previous Entry Resource_key value plus “.STARTING”;
call search on the Resource_bb object;
if the search found an Entry, call erase on the Resource_bb object with the Entry;
call run on the ORB to pass it control of the thread;
}
· template Class File_bb_event_poller : An OAPI Blackboard derivative that decorates another OAPI Blackboard and that polls for changes on the decorated blackboard at regular intervals, generating events for those changes.
o File_bb_event_poller(Blackboard pointer, Entry_event_mgr pointer, integer polling interval) (Constructor)
{
create new Event_id object;
create new template parameter Entry object;
call search_fill_all on the Entry;
initialize die and event_id mutexes;
create a new thread with poller_run as the entry point and a pointer to this object as the argument;
}
o ~File_bb_event_poller() (Destructor)
{
lock die mutex;
set die to true;
unlock die mutex;
join the polling thread;
destroy the die and event_id mutexes;
}
o post(Entry pointer) (Post a new entry on the blackboard)
{
call post on the decorated Blackboard object;
}
o erase(Entry pointer) (Erase an existing entry on the blackboard)
{
call erase on the decorated Blackboard object;
}
o replace(Entry pointer, Entry pointer) (Modify an existing entry on the blackboard)
{
call replace on the decorated Blackboard object;
}
o search(Entry pointer, vector of Entry pointers reference) (Search for an entry or entries matching the Entry argument on the blackboard)
{
call search on the decorated Blackboard object;
lock the event_id mutex;
increment the Event_id object and save its integer value;
unlock the event_id mutex;
return the event ID value;
}
o lock_entry(Entry pointer) (Lock an entry on the blackboard)
{
call lock_entry on the decorated Blackboard object;
return the Opus_lock object;
}
o str() (Get the blackboard name)
{
call str on the decorated Blackboard object;
return the concatenated names of this blackboard and the decorated blackboard;
}
o test_driver() (Test the functionality of this object)
{
call test_driver on the decorated Blackboard object;
return the test result;
}
o poller_run(void pointer) (Polling thread entry point)
{
loop forever {
lock die mutex;
if die is true {
unlock die mutex;
break from loop;
}
unlock die mutex;
sleep for the polling interval;
call search on the decorated Blackboard with the wildcarded Entry;
loop over the results from the previous search {
if the Entry is found in the current search results {
call operator== on the Entry with the current search result to check if it was modified;
if modified {
lock event_id mutex;
increment Event_id object and save its integer value;
unlock event_id_mutex;
call notify on the Entry_event_mgr object with the Entry, event ID and the MODIFIED enumerator;
}
push current Entry value into holding vector;
remove the Entry from the current search results;
} else {
lock event_id mutex;
increment Event_id object and save its integer value;
unlock event_id mutex;
call notify on the Entry_event_mgr object with the Entry, event ID, and the DELETED enumerator;
}
}
copy the holding vector to the last time results vector;
loop over the current results vector {
lock event_id mutex;
increment Event_id object and save its integer value;
unlock event_id mutex;
call notify on the Entry_event_mgr object with the Entry, event ID, and the CREATED enumerator;
push the entry into the last time results vector;
}
}
}
main {
parse command-line arguments;
call SYS_derive_username to get user name;
initialize the ORB, start the POA_manager;
create new COSEC_event_mgr object;
create new OpusEnv_impl servant object;
implicitly activate the OpusEnv object by calling _this on the OpusEnv_impl object;
get the object IOR by calling object_to_string on the ORB;
create new Ior_bb object;
create new Entry object by calling new_ior_entry on the Ior_bb object with the IOR and user name;
call search on the Ior_bb object with the Entry object;
if the search found an Entry, call replace on the Ior_bb object with the new Entry;
else call post on the Ior_bb object with the Entry;
create new Resource_bb object with “OPUS_DEFINITIONS_DIR:opus_corba_objs”;
create new Resource_key object using previous Entry Resource_key value plus “.STARTING”;
call search on the Resource_bb object;
if the search found an Entry, call erase on the Resource_bb object with the Entry;
create new thread with ior_service as entry point;
call run on the ORB to pass it control of the thread;
}
ior_service {
create new int off the heap;
use address of the int to seed random number generator;
call rand to generate random number;
initialize semaphore and mutex;
loop over thread pool size {
create new thread with ior_thread as entry point, passing random number as argument;
}
open socket and bind to ephemeral port;
open file “.opus_env_server” in user login directory determined by value of “HOME” environment variable;
write bound port number and random number to the file;
close the file;
loop forever {
listen on socket for client connection requests;
lock mutex;
upon connection request, push socket file descriptor into queue;
unlock mutex;
raise semphore;
}
}
ior_thread {
wait on semphore {
lock mutex;
pop socket file descriptor off queue;
unlock mutex;
call read_line;
if random number does not match data sent over socket, drop connection;
else write IOR to socket;
close connection;
}
}
read_line {
read one character at a time from socket until EOF;
}
{
parse command-line arguments;
initialize the ORB, start the POA_manager;
create new EventChannel servant object;
implicitly activate the EventChannel object by calling _this;
create new Pingable_impl servant object;
implicitly activate the Pingable object by calling _this on the Pingable_impl object;
get the objects’ IORs by calling object_to_string on the ORB;
create new Ior_bb object;
create new Entry objects by calling new_ior_entry on the Ior_bb object with the IOR and EventChannel and Pingable names;
call search on the Ior_bb object with the Entry objects;
if the search found an Entry, call replace on the Ior_bb object with the new Entry;
else call post on the Ior_bb object with the Entry;
create new Resource_bb object with “OPUS_DEFINITIONS_DIR:opus_corba_objs”;
create new Resource_key object using previous Entry Resource_key value plus “.STARTING”;
call search on the Resource_bb object;
if the search found an Entry, call erase on the Resource_bb object with the Entry;
call run on the ORB to pass it control of the thread;
}
Location: Platform-specific branch of OPUS_DEFINITIONS_DIR
Purpose: Defines the commands used to start each server (configured by the user); also used by OPUS to store object-name-to-server-type mappings (maintained by OPUS).
File Protections: N/A
Template:
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! Name: opus_corba_objs.template
!
! Purpose: A
template from which a user's opus_corba_objs file can be
!
constructed.
!
! Modification History:
!
! Date OPR Who Reason
! -------- ------- ------------ -----------------------------------------
! 11/28/00 42874 WMiller Initial version
!
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!
! The command used to start each server type (CONTEXT, EVENT,
OSF, and
! PSTAT) must be configured by the user in the file
! OPUS_DEFINITIONS_DIR:opus_corba_objs. The server types map to
executables
! in the following way:
!
! CONTEXT -> opus_env_server
! EVENT ->
opus_event_service
! PSTAT ->
opus_bb_server (with -t PSTAT)
! OSF ->
opus_bb_server (with -t OSF)
!
!
! The command-line options for each server are listed below.
Note, however,
! that the token ^F can be used in place of arguments that
indicate the string
! object name. At startup, this token is replaced with the
requested string
! object name.
!
!
! opus_env_server [[-d OPUS_DEBUG_LEVEL] [-t thread_pool_size]
!
[[ORB_arg0] ... [ORB_argN]] ]
!
! where
!
! OPUS_DEBUG_LEVEL =
One of the enumerated message report levels of the
!
OAPI Msg class (e.g., ALL, INFO, ...) {default: INFO}
! thread_pool_size =
The number of threads to create for servicing clients
!
of the bootstrap server {default: 3}
! ORB_argN = An ACE/TAO ORB option {default:
ACE/TAO default ORB}
!
! opus_bb_server -i blackboard_ID -t blackboard_type [[-d
OPUS_DEBUG_LEVEL]
! [-r
polling_interval] [[ORB_arg0] ... [ORB_argN]] ]
!
! where
!
! blackboard_ID = The string name of the blackboard
! blackboard_type = Either PSTAT or OSF
! OPUS_DEBUG_LEVEL =
One of the enumerated message report levels of the
!
OAPI Msg class (e.g., ALL, INFO, ...) {default: INFO}
! polling_interval =
When BB_TYPE = FILE, the number of seconds in between
!
polling attempts {default: 5}
! ORB_argN = An ACE/TAO ORB option {default:
ACE/TAO default ORB}
!
! opus_event_service -i event_channel_ID [[-d OPUS_DEBUG_LEVEL]
!
[[ORB_arg0] ... [ORB_argN]] ]
!
! where
!
! event_channel_ID =
The string name of the event channel.
! OPUS_DEBUG_LEVEL =
One of the enumerated message report levels of the
!
OAPI Msg class (e.g., ALL, INFO, ...) {default: INFO}
! ORB_argN = An ACE/TAO ORB option {default:
ACE/TAO default ORB}
!
!
! For example, a simple configuration where rsh is used to start
each
! server type on a designated node is shown below:
!
CONTEXT = rsh -n
<node1> opus_env_server &
EVENT = rsh -n <node2> opus_event_service -i
^F &
OSF = rsh -n <node3> opus_bb_server -i ^F -t
OSF &
PSTAT = rsh -n <node4> opus_bb_server -i ^F -t
PSTAT &
!
! To use this configuration, substitute real node names for
<node1>...<node4>.
!
!
! *** NOTE: the file opus_corba_objs is accessed by the system
through the
! resource
blackboard. This blackboard discards any comments in
! the resource
file.
Location: OPUS_HOME_DIR
Purpose: Contains object-name-to-IOR mappings (maintained by OPUS).
File Protections: User read/write.
Template:
None
Location: OPUS_HOME_DIR
Purpose: Database of locked entries on the resource blackboard (maintained by OPUS).
File Protections: N/A
Template:
None
Location: user login directory
Purpose: Contains the port address on which the user context server is listening and a security token to be passed back by the managers for purposes of authentication (maintained by user context server, opus_env_server).
File Protections: User read/write.
Template:
None