This chapter describes how to design and implement a CORBA server application, using the Basic University sample application as an example. The content of this chapter assumes that the design of the application to be implemented is complete and is expressed in OMG IDL. This chapter focuses on design and implementation choices that are oriented to the server application.
• , which helps provide context to the design and implementation considerations
• , which includes comprehensive discussions about the following topics:
• The Basic University sample application provides the student with the ability to browse course information from a central University database. Using the Basic sample application, the student can do the following:
• Browse course synopses from the database by specifying a search string. The server application then returns synopses for all courses that have a title, professor, or description containing the search string. (A course synopsis returned to the client application includes only the course number and title.)
Creates object references to the Registrar object Figure 3‑1 Basic University Sample ApplicationFor the purposes of explaining what happens when the Basic University sample application runs, the following separate groups of events are described:
• The following sequence shows a typical set of events that take place when the Basic client and server applications are started and the client application obtains an object reference to the Registrar object:
1. The Basic client and server applications are started, and the client application obtains a reference to the RegistrarFactory object from the FactoryFinder.
2. Using the reference to the RegistrarFactory object, the client application invokes the find_registrar() operation on the RegistrarFactory object.
3. The RegistrarFactory object is not in memory (because no previous request for that object has arrived in the server process), so the TP Framework invokes the Server::create_servant() operation in the Server object to instantiate it.
4. Once instantiated, the RegistrarFactory object’s find_registrar() operation is invoked. The RegistrarFactory object creates the Registrar object reference and returns it to the client application.The following sequence traces the events that may occur when the student browses a list of course synopses:
1. Using the object reference to the Registrar object, the client application invokes the get_courses_synopsis() operation, specifying:
• An integer, represented by the variable number_to_get, which specifies the size of the synopsis list to be returned.
2. The Registrar object is not in memory (because no previous request for that object has arrived in the server process), so the TP Framework invokes the Server::create_servant() operation, which is implemented in the Server object. This causes the Registrar object to be instantiated in the server machine’s memory.
3. The Registrar object receives the client request and creates an object reference to the CourseSynopsisEnumerator object. The CourseSynopsisEnumerator object is invoked by the Registrar object to fetch the course synopses from the database.To create the object reference CourseSynopsisEnumerator object, the Registrar object does the following:
a. Generates a unique ID for the CourseSynopsisEnumerator object.
b. Generates an object ID for the CourseSynopsisEnumerator object that is a concatenation of the unique ID generated in the preceding step and the search string specified by the client.
c. Gets the CourseSynopsisEnumerator object’s Interface Repository ID from the interface typecode.
d. Invokes the TP::create_object_reference() operation. This operation creates an object reference to the CourseSynopsisEnumerator object needed for the initial client request.
4. Using the object reference created in the preceding step, the Registrar object invokes the get_next_n() operation on the CourseSynopsisEnumerator object, passing the list size. The list size is represented by the parameter number_to_get, described in step 1.
5. The TP Framework invokes the Server::create_servant() operation on the Server object to instantiate the CourseSynopsisEnumerator object.
6. The TP Framework invokes the activate_object() operation on the CourseSynopsisEnumerator object. This operation does the following two things:
7. The CourseSynopsisEnumerator object returns the following information to the Registrar object:
• A course synopsis list, specified in the return value CourseSynopsisList, which is a sequence containing the first list of course synopses.
8. The Registrar object returns the CourseSynopsisEnumerator object reference to the client application, and also returns the following information obtained from that object:
• The number_remaining variable(If the number_remaining variable is 0, the Registrar object invokes the destroy() operation on the CourseSynopsisEnumerator object and returns a nil reference to the client application.)
9. The client application sends directly to the CourseSynopsisEnumerator object its next request to get the next batch of matching synopses.
10. The CourseSynopsisEnumerator object satisfies the client request, also returning the updated number_remaining variable.
11. When the client application is done with the CourseSynopsisEnumerator object, the client application invokes the destroy() operation on the CourseSynopsisEnumerator object. This causes the CourseSynopsisEnumerator object to invoke the TP::deactivateEnable() operation.
12. The TP Framework invokes the deactivate_object() operation on the CourseSynopsisEnumerator object. This causes the list of course synopses maintained by the CourseSynopsisEnumerator object to be erased from the server computer’s memory so that the CourseSynopsisEnumerator object’s servant can be reused for another client request.The following sequence shows a typical set of events that take place when the client application browses course details:
2. The client application invokes the get_course_details() operation on the Registrar object, passing the list of course numbers.
3. The Registrar object searches the database for matches on the course numbers, and then returns a list containing full details for each of the specified courses. The list is contained in the CourseDetailsList variable, which is a sequence of structs containing full course details.The Basic University sample application contains the University server application, which deals with several fundamental CORBA server application design issues. This section addresses the following topics:
• The Basic client application needs references to the following objects, which are managed by the University server application:
• The RegistrarFactory object
• The Registrar object
• The CourseSynopsisEnumerator object
The object reference for the RegistrarFactory object is generated in the Server object, which registers the RegistrarFactory object with the FactoryFinder. The client application then obtains a reference to the RegistrarFactory object from the FactoryFinder.There is only one RegistrarFactory object in the Basic University server application process. The object reference for the Registrar object is generated by the RegistrarFactory object and is returned when the client application invokes the find_registrar() operation. The object reference created for the Registrar object is always the same; this object reference does not contain a unique OID.There is only one Registrar object in the Basic University server application process. The object reference for the CourseSynopsisEnumerator object is generated by the Registrar object when the client application invokes the get_courses_synopsis() operation. In this way, the Registrar object is the factory for the CourseSynopsisEnumerator object. The design and use of the CourseSynopsisEnumerator object is described later in this chapter.There can be any number of CourseSynopsisEnumerator objects in the Basic University server application process.
• The Server object registers the RegistrarFactory object with the FactoryFinder. This is the recommended way to ensure that client applications can locate the factories they need to obtain references to the basic objects in the application.
• The object reference to the Registrar object is created by the RegistrarFactory object. This shows a very common and basic way to return object references to the client application; namely, that there is a factory dedicated to creating and returning references to the primary object that is required by the client application to execute business logic.
• The object reference to the CourseSynopsisEnumerator object is created outside a registered factory. In the University sample applications, this is a good design because of the way the CourseSynopsisEnumerator object is meant to be used; namely, its existence is specific to a particular client application operation. The CourseSynopsisEnumerator object returns a specific list and results that are not related to the results from other queries.
• Because the Registrar object creates, in one of its operations, an object reference to another object, the Registrar object is a factory. However, the Registrar object is not registered as a factory with the FactoryFinder; therefore, client applications cannot get a reference to the Registrar object from the FactoryFinder.Each of the three objects in the Basic sample application has its own state management requirements. This section discusses the object state management requirements for each.The RegistrarFactory object does not need to be unique for any particular client request. It makes sense to keep this object in memory and avoid the expense of activating and deactivating this object for each client invocation on it. Therefore, the RegistrarFactory object has the process activation policy.The Basic sample application is meant to be deployed in a small-scale environment. The Registrar object has many qualities similar to the RegistrarFactory object; namely, this object does not need to be unique for any particular client request. Also, it makes sense to avoid the expense of continually activating and deactivating this object for each invocation on it. Therefore, in the Basic sample application, the Registrar object has the process activation policy.The fundamental design problem for the University server application is how to handle a list of course synopses that is potentially too big to be returned to the client application in a single response. Therefore, the solution centers on the following:
• The University server application has the CourseSynopsisEnumerator object, which implements this solution. Although this object returns an initial batch of synopses when it is first invoked, this object retains an in-memory context so that the client application can get the remainder of the synopses in subsequent requests. To retain an in-memory context, the CourseSynopsisEnumerator object must be stateful; that is, this object stays in memory between client invocations on it.When the client is finished with the CourseSynopsisEnumerator object, this object needs a way to be flushed from memory. Therefore, the appropriate state management decision for the CourseSynopsisEnumerator object is to assign it the process activation policy and to implement the CORBA application-controlled deactivation feature.Application-controlled deactivation is implemented in the destroy() operation on that object.The following code example shows the destroy() operation on the CourseSynopsisEnumerator object:void CourseSynopsisEnumerator_i::destroy()
// When the client calls "destroy" on the enumerator,
// then this object needs to be "destructed".
// Do this by telling the TP framework that we're
// done with this object.
activation_policy ( process );
transaction_policy ( optional );
implements ( UniversityB::CourseSynopsisEnumerator );
activation_policy ( process );
transaction_policy ( optional );
implements ( UniversityB::Registrar );
activation_policy ( process );
transaction_policy ( optional );
implements ( UniversityB::RegistrarFactory );
};Handling durable state information refers specifically to reading durable state information from disk at some point during or after the object activation, and writing it, if necessary, at some point before or during deactivation. The following two objects in the Basic sample application handle durable state information:
• The Registrar object
• The CourseSynopsisEnumerator objectThe following two sections describe the design considerations for how these two objects handle durable state information.One of the operations on the Registrar object returns detailed course information to the client application. In a typical scenario, a student who has browsed dozens of course synopses may be interested in viewing detailed information on perhaps as few as two or three courses at one time.To implement this usage scenario efficiently, the Registrar object is defined to have the get_course_details() operation. This operation accepts an input parameter that specifies a list of course numbers. This operation then retrieves full course details from the database and returns the details to the client application. Because the object in which this operation is implemented is process-bound, this operation should avoid keeping any state data in memory after an invocation on that operation is complete.The Registrar object does not keep any durable state in memory. When the client application invokes the get_course_details() operation, this object simply fetches the relevant course information from the University database and sends it to the client. This object does not keep any course data in memory. No durable state handling is done via the activate_object() or deactivate_object() operations on this object.The CourseSynopsisEnumerator object handles course synopses, which this object retrieves from the University database. The design considerations, with regard to handling state, involve how to read state from disk. This object does not write any state to disk.There are three important aspects of how the CourseSynopsisEnumerator object works that influence the design choices for how this object reads its durable state:
• Read its durable state information when activated; namely, via the activate_object() operation on this object.
• Flush the course synopses from memory when deactivated; namely, via the deactivate_object() operation.Therefore, when the CourseSynopsisEnumerator object is activated, the activate_object() operation on this object does the following:
Note: If you implement the Tobj_ServantBase::activate_object() or Tobj_ServantBase::deactivate_object()operations on an object, remember to edit the implementation header file (that is, the application_i.h file) and add the definitions for those operations to the class definition template for the object’s interface.Note the following about the way in which the University sample applications use the University database:
• All of the University sample applications access the University database to manipulate course and student information. Typically this is a large part of the code you write in the implementation files. To make the University sample implementation files simpler, and to help you focus on CORBA features instead of database code, the samples have wrapped all the code that reads and writes to the database within a set of classes. The file samplesdb.h in the utils directory contains the definitions of these classes. These classes make all the necessary SQL calls to read and write the course and student records in the University database.
Note: For more information on the files you build into the Basic server application, see the Guide to the CORBA University Sample Applications.
• The CourseSynopsisEnumerator object uses a database cursor to find matching course synopses from the University database. Because database cursors cannot span transactions, the activate_object() operation on the CourseSynopsisEnumerator object reads all matching course synopses into memory. Note that the cursor is managed by an iterator class and is thus not visible to the CourseSynopsisEnumerator object. For more information about how the University sample applications use transactions, seeThis section describes why these two patterns are appropriate for the Basic sample application and how this application implements them.As mentioned in the section , this design pattern is appropriate in situations where you can have one process object that handles data entities needed by the client application. The data entities are encapsulated as CORBA structs that are manipulated by the process object and not by the client application.Adapting the Process-Entity design pattern to the Basic sample application allows the application to avoid implementing fine-grained objects. For example, the Registrar object is an efficient alternative to a similarly numerous set of course objects. The processing burden of managing a single, coarse-grained Registrar object is small relative to the potential overhead of managing hundreds or thousands of fine-grained course objects.For complete details about the Process-Entity design pattern, see the Design Patterns technical article.This design pattern is appropriate in situations where an object has generated an internal list of data that is potentially too large to return to the client application in a single response. Therefore, the object must return an initial batch of data to the client application in one response, and have the ability to return the remainder of the data in subsequent responses.A list-enumerator object must also simultaneously keep track of how much of the data has already been returned so that the object can return the correct subsequent batch. List-enumerator objects are always stateful (that is, they remain active and in memory between client invocations on them) and the server application has the ability to deactivate them when they are no longer needed.The list-enumerator design pattern is an excellent choice for the CourseSynopsisEnumerator object, and implementing this design pattern provides the following benefits:
• Each CourseSynopsisEnumerator object is unique, and its content is determined by the request that caused this object to be created. (In addition, each CourseSynopsisEnumerator object ID is also unique.) When the client invokes the get_courses_synopsis() operation on the Registrar object, the Registrar object returns the following:
• An object reference to a CourseSynopsisEnumerator object that can return the remainder of the synopses.Therefore, all subsequent invocations go to the correct CourseSynopsisEnumerator object. This is critical in the situation where the server process has multiple active instances of the CourseSynopsisEnumerator class.Because the get_courses_synopsis() operation returns a unique CourseSynopsisEnumerator object reference, client requests never collide; that is, a client request never mistakenly goes to the wrong CourseSynopsisEnumerator object.Although the Registrar object has the get_courses_synopsis() operation on it, the knowledge of the database query and the synopsis list is embedded entirely in the CourseSynopsisEnumerator object. In this situation, the Registrar object serves only as a means for the client to get the following:
• A reference to a CourseSynopsisEnumerator object that can return the remainder of the synopses.The Oracle Tuxedo system implements a performance efficiency in which data marshaling between two objects in the same server process is automatically disabled. This efficiency exists if the following circumstances exist:
• An example of this is when the Registrar object creates an object reference to the CourseSynopsisEnumerator object and causes that object to be instantiated. No data marshaling takes place in the requests and responses between those two objects.The preactivate object with state feature allows you to preactivate an object before a client application invokes that object. This feature can be particularly useful for creating iterator objects, such as the CourseSynopsisEnumerator object in the University samples.Preactivating an object with state centers around using the TP::create_active_object_reference() operation. Typically, objects are not created in a CORBA server application until a client issues an invocation on that object. However, by preactivating an object and using the TP::create_active_object_reference() operation to pass a reference to that object back to the client, your client application can invoke an object that is already active and populated with state.
1. Includes an invocation of the C++ new statement to create an object.
3. Invokes the TP::create_active_object_reference() operation to obtain a reference for the newly created object. This object reference can then be returned to the client application.Thus, the preactivated object is created in such a way that the TP Framework invokes neither the Server::create_servant() nor the Tobj_ServantBase::activate_object() operations for that object.
• Preactivated objects must have the process activation policy. Therefore, these objects can be deactivated only at the end of the process or by an invocation to the TP::deactivateEnable() operation on those objects.
• The object reference created by the TP::create_active_object_reference() operation is transient. This is because a preactivated object should exist only for the lifetime of the process in which it was created, and this object should not be reactivated again in another server process.If a client application invokes on a transient object reference after the process in which the object reference was created is shut down, the TP Framework returns the following exception:
• To prevent the situation in which a server has crashed, and a client application subsequently attempts to invoke the now-deleted object, add the TobjS::ActivateObjectFailed exception to the implementation of the Tobj_ServantBase::activate_object() operation to the object meant for preactivation. Then, if a client attempts to invoke such an object after a server crash, in which case the TP Framework invokes the Tobj_ServantBase::activate_object() operation on that object, the TP Framework returns the following exception to the client application: