Distributed SOM (DSOM): Difference between revisions
Line 835: | Line 835: | ||
====Inquiring about a remote object's implementation==== | ====Inquiring about a remote object's implementation==== | ||
A client may wish to inquire about the (server) implementation of a remote object. All objects in a server, including the "server object", share the same implementation definition. This is common when using the somdFindServersByClass call, where a sequence of server proxies is returned, and some choice must be made about which to use. | |||
When a proxy is obtained by a client, the client can inquire about the underlying server implementation by obtaining its corresponding ImplementationDef. An ImplementationDef object contains a set of attributes that describe a server implementation. To get the ImplementationDef associated with a remote object, the get_implementation method (implemented on SOMDObject and inherited by SOMDClientProxy) can be called. | |||
For example, if a program has a proxy for a remote server object, it can get the ImplementationDef for the server with method calls similar to the following: | |||
<PRE> | |||
ImplementationDef implDef; | |||
SOMDServer server; | |||
... | |||
implDef = _get_implementation(server, &ev); | |||
</PRE> | |||
Once the ImplementationDef has been obtained, the application can access its attributes using the _get_impl_xxx methods. | |||
The ImplementationDef class is discussed further in section 6.6, "Configuring DSOM." | |||
===Destroying remote objects=== | ===Destroying remote objects=== | ||
There are several ways of destroying objects or their proxies in DSOM, just as there are several ways to create objects. Remote objects can be asked to destroy themselves, or, the SOMDObjectMgr and the SOMDServer can participate in the deletion. | |||
====Destroying objects via a proxy==== | ====Destroying objects via a proxy==== | ||
DSOM provides means for deleting remote objects via their proxies. For example, if somFree is invoked on a proxy, the somFree call gets forwarded directly to the target object, just like any other target method call. For example, | |||
_somFree(car); | |||
frees the remote car. Note that, by default, invoking somFree on the proxy does not free the proxy, only the remote object. However, the following call can be issued as part of a client-program initialization, so that invoking somFree on a proxy frees both the remote object and the proxy: | |||
__set_somd21somFree(SOMD_ObjectMgr, ev, TRUE); | |||
All subsequent invocations of somFree on a proxy object will result in both the remote object and the proxy being freed. | |||
To be explicit about whether the proxy or the remote object is being deleted, the methods somdTargetFree and somdProxyFree, defined on proxies, can be used: | |||
_somdTargetFree(car, &ev); | |||
frees the remote "Car" (but not the proxy) and | |||
_somdProxyFree(car, &ev); | |||
frees the proxy (but not the remote "Car"). | |||
;Note: CORBA specifies a third method for deleting object references. (Proxies are a specialized type of object reference.) The method | |||
_release(car, &ev); | |||
deletes the proxy (but not the target object). | |||
====Destroying objects via the DSOM Object Manager==== | ====Destroying objects via the DSOM Object Manager==== | ||
Having created a remote object with somdNewObject or somdCreateObj, the remote object and its local proxy may be destroyed by invoking the method somdDestroyObject on the DSOM Object Manager using the proxy as an argument. For example, | |||
<PRE> | |||
/* create the car */ | |||
car = _somdNewObject(SOMD_ObjectMgr, &ev, "Car", ""); | |||
... | |||
/* destroy the car (and its proxy) */ | |||
_somdDestroyObject(SOMD_ObjectMgr, &ev, car); | |||
</PRE> | |||
If the client does not want to destroy the remote object, but is finished working with it, the somdReleaseObject method should be used instead, e.g., | |||
_somdReleaseObject(SOMD_ObjectMgr, &ev, car); | |||
This deletes the local proxy, but not the remote object. | |||
Both somdDestroyObject and somdReleaseObject are defined on the ObjectMgr, so that the Object Manager is aware of the client's actions, in case it wants to do any bookkeeping. | |||
The object passed to either the somdDestroyObject method or the somdReleaseObject method can be either a local SOM object or a DSOM proxy object. When a local SOM object is passed, somdDestroyObject has the same behavior as somFree. If a local SOM object is passed to somdReleaseObject, however, this has no effect. | |||
====Destroying objects via a server object ==== | ====Destroying objects via a server object ==== | ||
The somdDestroyObject method described above sends a request to delete a remote object to the object's server. It does so to ensure that the server has an opportunity to participate in, if not perform, the deletion. The method defined on the SOMDServer class for destroying objects is somdDeleteObj. If the client has a proxy for the server object, it can also invoke somdDeleteObj directly, instead of calling somdDestroyObject. | |||
Destroying objects via the server object, rather than asking the object itself (as in somFree or somdTargetFree), allows the server object do any clean-up that is needed. For simple applications, this may not be necessary, but for applications that provide their own application-tailored server objects, it may be critical. See, for example, the persistent server example in section 6.4, entitled "Basic Server Programming." | |||
===Creating remote objects using user-defined metaclasses=== | ===Creating remote objects using user-defined metaclasses=== | ||
An application may wish to define its own constructor methods for a particular class, via a user-supplied metaclass. In this case, the somdNewObject method should not be used, since it simply calls the default constructor method, somNew, defined by SOMClass. | |||
Instead, the application can obtain a proxy to the actual class object in the server process. It can do so via the somdGetClassObj method, invoked on the SOMDServer proxy returned by one of the somdFindServerXxx methods. The application-defined constructor method can then be invoked on the proxy for the remote class object. | |||
;Note: The same issues apply to destructor methods. If the application defines its own destructor methods, they can be called via the class object returned by somdGetClassObj, as opposed to calling somdDestroyObject. | |||
The following example creates a new object in a remote server using an application-defined constructor method, "makeCar", which is assumed to have been defined in the metaclass of "Car", named "MetaCar". | |||
<PRE> | |||
#include <somd.h> | |||
#include <Car.h> | |||
main( ) | |||
{ | |||
Environment ev; | |||
SOMDServer server; | |||
Car car; | |||
MetaCar carClass; | |||
SOM_InitEnvironment(&ev); | |||
SOMD_Init(&ev); | |||
/* find a Car server */ | |||
server = _somdFindAnyServerByClass(SOMD_ObjectMgr, &ev, "Car"); | |||
/* get the class object for Car */ | |||
carClass = (MetaCar) _somdGetClassObj(server, &ev, "Car"); | |||
/* create the car object */ | |||
car = _makeCar(carClass, &ev, "Red", "Toyota", "2-door"); | |||
... | |||
} | |||
</PRE> | |||
===Saving and restoring references to objects=== | ===Saving and restoring references to objects=== | ||
A proxy is a kind of "object reference". An object reference contains information that is used to identify a target object. | |||
To enable clients to save references to remote objects (in a file system, for example) or exchange references to remote objects (with other application processes), DSOM must be able to externalize proxies. To "externalize a proxy" means to create a string ID for a proxy that can be used by any process to identify the remote target object. DSOM must also support the translation of string IDs back into proxies. | |||
The DSOM Object Manager defines two methods for converting between proxies and their string IDs: somdGetIdFromObject and somdGetObjectFromId. | |||
Here is an example client program that creates a remote "Car" object. It generates a string ID corresponding to the proxy, and saves the string ID to a file for later use. | |||
<PRE> | |||
#include <stdio.h> | |||
#include <somd.h> | |||
#include <Car.h> | |||
main( ) | |||
{ | |||
Environment ev; | |||
Car car; | |||
string somdObjectId; | |||
FILE* file; | |||
SOM_InitEnvironment(&ev); | |||
SOMD_Init(&ev); | |||
/* create a remote Car object */ | |||
car = _somdNewObject(SOMD_ObjectMgr, &ev, "Car", ""); | |||
/* save the reference to the object */ | |||
somdObjectId = _somdGetIdFromObject(SOMD_ObjectMgr, &ev, car); | |||
file = fopen("/u/joe/mycar", "w"); | |||
fprintf(file, "%s", somdObjectId); | |||
... | |||
</PRE> | |||
Next is an example client program that retrieves the string ID and regenerates a valid proxy for the original remote "Car" object (assuming the remote "Car" object can still be found in the server). | |||
<PRE> | |||
... | |||
Environment ev; | |||
Car car; | |||
char buffer[256]; | |||
string somdObjectId; | |||
FILE* file; | |||
... | |||
/* restore proxy from its string form */ | |||
file = fopen("/u/joe/mycar", "r"); | |||
somdObjectId = (string) buffer; | |||
fscanf(file, "%s", somdObjectId); | |||
car = _somdGetObjectFromId(SOMD_ObjectMgr, &ev, somdObjectId); | |||
... | |||
</PRE> | |||
Once the proxy has been regenerated, methods can be invoked on the proxy and they will be forwarded to the remote target object, as always. | |||
;Note: The somdGetIdFromObject and somdGetObjectFromId methods directly correspond to the CORBA methods ORB_object_to_string and ORB_string_to_object, defined on the ORB class. | |||
====Finding existing objects==== | ====Finding existing objects==== | ||
The SOMDObjectMgr and SOMDServer classes support the methods described above, which allow clients to create objects in servers. However, it is also likely that clients will want to find and use objects that have already been created, usually by the servers that implement them. For example, a print service will create printer objects, and must then export them to clients. In that case, the calls to somdNewObject or somdCreateObj would be replaced with other "lookup" calls on some directory (server) object which would take an object name or identifier and return a proxy to a corresponding remote object. Likewise, the server that owns the object would register the exported object in the directory. | |||
It is important to understand that DSOM does not provide a directory service such as the one described. But such a directory object could be implemented by the application, where a table or collection object maps object names to proxies. The string IDs for the proxies in the directory object could be saved using a file (as above) or a persistent object (via the Persistence Framework of SOMobject Developer Toolkit). A directory server implemented using DSOM could be used to share the directory among processes. | |||
Upon a lookup call, the directory server could find the corresponding proxy (or its string ID) in the directory, and return it to the caller. | |||
===Finding server objects=== | ===Finding server objects=== | ||
The DSOM Object Manager can be used to find server object proxies using the somdFindServerXxx methods. However, it is important to point out that an application can also augment those services, by managing server proxies itself. Server proxies can be maintained in an application-specific directory, stored in a file, or passed from process to process, just as any other proxies. | |||
===Invoking methods on remote objects=== | ===Invoking methods on remote objects=== | ||
As described earlier, DSOM proxies are local representatives of remote objects, and as such, they can be treated like the target objects themselves. Method calls are invoked in exactly the same manner as if the object is local. This is true both for method calls using the static bindings (as most of our examples have shown), as well as for dynamic dispatching calls, where SOM facilities (such as the somDispatch method) are used to construct method calls at run time. | |||
CORBA 1.1 also defines a dynamic invocation interface that is implemented by DSOM. It is described later in section 6.9, "Advanced Topics". | |||
The DSOM run time is responsible for transporting any input method argument values supplied by the caller (defined by legal IDL types) to the target object in a remote call. Likewise, the DSOM run time transports the return value and any output argument values back to the caller following the method call. | |||
Note: DSOM uses the Interface Repository (IR) to discover the "signature" of a method (that is, the method's prototype). It is important that the contents of the IR match the method bindings used by the application program (i.e. the same IDL file is used to update the IR and to generate bindings). | |||
DSOM can make remote invocations only of methods whose parameter types are among the following IDL types: basic types (short, long, unsigned short, unsigned long, float, double, char, boolean, octet), enum, struct, union, sequence, string, array, any, and object. The members of a struct, union, sequence, or array and the value of an any, must also be from the above list of supported DSOM types. | |||
In addition to the preceding types, DSOM also supports method parameters of type pointer to one of the above types (for example, long*) Pointers to pointers are not supported, however, and pointers embedded within one of the above types (for example, a pointer within a struct) are not supported The "void *" type is also not supported. Currently, DSOM has the limitation that NULL pointer values cannot be returned as inout or out method arguments although it is expected that this limitation will be addressed in a future release. | |||
Types declared as SOMFOREIGN types are not currently supported by DSOM. Because the SOM somI is declared as a SOMFOREIGN type, this implies that any method having a parameter of type somId cannot be invoked remotely using DSOM. This restriction includes the SOM methods: somRespondsTo, somSupportsMethod, somGetMethodDescriptor, somGetMethodIndex, and somGetNthMethodInfo. | |||
When a method parameter is an object type (that is, an instance of SOMObject or some class derived from SOMObject), a client program making a remote invocation of that method must pass an object reference for that parameter, rather than passing a local SOMObject, unless the client program is also DSOM server program, in which case DSOM will automatically convert the local object into an object reference. | |||
Methods having the procedure SOM IDL modifier cannot be invoked remotely using DSOM. This is because these "methods" are called directly, rather than via the normal method resolution mechanisms on which DSOM relies. | |||
====Determining memory allocation and ownership==== | ====Determining memory allocation and ownership==== | ||
When a method is invoked that returns a result of type string, sequence, or array, DSOM will allocate memory in the client's address space for the result. Ownership of this memory becomes the responsibility of the client program. When the client program has finished using it, the client should free the memory using the ORBfree function, rather than using free or SOMFree (This is because the memory has been allocated by DSOM using special memory management techniques; therefore, the client should ask DSOM to also free the memory.) | |||
When invoking a method using DSOM, the client program is responsible for providing storage for all in arguments and for all inout/out arguments, with the following exceptions: DSOM will allocate storage for a string or for the _buffer field of a sequence when used as an out argument, and will allocate storage for the_value field of an any when used as an inout or out argument. This storage becomes the responsibility of the client program and should later be freed using ORBfree. For a string or sequence used as an inout argument, the out result is constrained to be no larger than the size of the in argument allocated by the client. | |||
===Passing object references in method calls=== | ===Passing object references in method calls=== | ||
When pointers to objects are returned as method output values (as in the previous examples), DSOM automatically converts the object pointers (in the server) to object proxies in the client. | |||
Likewise, when a client passes object (proxy) pointers as input arguments to a method, DSOM automatically converts the proxy argument in the client to an appropriate object reference in the server. | |||
;Note: If the proxy is for an object that is in the same server as the target object, DSOM gives the object reference to the server object for resolution to a SOM object pointer. Otherwise, DSOM leaves the proxy alone, since the proxy must refer to an object in some process other than the target's server. | |||
===Memory management=== | ===Memory management=== | ||
DSOM programs must manage four different kinds of memory resources: objects, object references, Environment structures, and blocks of memory. There are different techniques for allocating and releasing each kind of resource. | |||
;Objects and object references | |||
Creating and destroying remote objects was discussed previously in this section (see "Creating remote objects" and "Destroying remote objects"). Creating and destroying local objects is described in section 3.2, "Using SOM Classes - the Basics," in Chapter 3, "Using SOM Classes in Client Programs." Object references are typically created automatically by DSOM as needed by the client program. They are also released in the client program by using either the release method or the somdProxyFree method. (The two methods are equivalent.) | |||
;Environment structures | |||
When a client invokes a method and the method returns an exception in the Environment structure, it is the client's responsibility to free the exception. This is done by calling either exception_free or somdExceptionFree on the Envirnmen structure in which the exception was returned. (The two functions are equivalent.) A similar function, somExceptionFree, is available for SOM programmers however DSOM programmers can use somdExceptionFree to free all exceptions (regardless of whether they were returned from a local or remote method call). | |||
;Blocks of memory | |||
For allocating and releasing blocks of memory within a client program, SOM provides the SOMMalloc and SOMFree functions (analogous to the C "mallo" and "free" functions). The "Memory Management" section of Chapter 3 describes these functions. To release memory allocated by DSOM in response to a remote method call, however, DSOM client programs should use the ORBfree function | |||
For example, when a method is invoked that returns a result of type string, sequence, or array, DSOM will allocate memory for the result in the client's address space. Ownership of this memory becomes the responsibility of the client program. When finished using this memory, the client program should free it using the ORBfree function, rather than free or SOMFree. This is because the memory has been allocated by DSOM using special memory-management techniques; therefore, the client should ask DSOM to also free the memory. If the storage is freed using SOMFree rather than ORBfree, then memory leaks will result. | |||
The differences between the SOMFree and ORBfree functions are twofold: | |||
1.First, SOMFree should only be used to free memory not allocated by DSO(for example, memory the client program allocated itself using SOMMalloc), while ORBfree should be used to free memory allocated by DSOM in response to a remote method call. | |||
2.Second, SOMFee only frees a single block of memory (in the same way that the C "free" function does), while ORBfree will free an entire data structure, including any allocated blocks of memory within in. For example, if a remote method call returns a sequence of structs, and each struct contains a string, ORBfree will free, with a single call, not only the sequence's "_buffer" member, but also each struct and all the strings within the structs. Freeing a similar data structure using SOMFree would require multiple calls (one for each call to SOMMalloc used to build the data structure). | |||
Some programmers may wish to use a single function to free blocks of memory, regardless of whether they were allocated locally or by DSOM in response to a remote method call. For these programmers, DSOM provides a function, SOMD_NoORBfree, which can be called just after calling SOMD_Init in the client program. (It requires no arguments and returns no value.) This function specifies that the client program will free all memory blocks using SOMFree, rather than ORBfree. In response to this call, DSOM will not keep track of the memory it allocates for the client. Instead, it assumes that the client program will be responsible for walking all data structures returned from remote method calls, while calling SOMFree for each block of memory within. | |||
====Memory management for method parameters==== | ====Memory management for method parameters==== | ||
For each method, five SOM IDL modifiers are available to specify the method's memory-management policy (that is, whether the caller or the object owns the parameters' memory after the method is invoked). These modifiers are memory_management, caller_owns_result, caller_owns_parameters, object_owns_result, and object_owns_parameters. For a complete description of these modifiers and their meanings, see the section entitled "Implementation Statements" in Chapter 4, "SOM IDL and the SOM Compiler." | |||
Note that the memory-management policy for a particular parameter applies to the parameter and all the memory embedded within it (for example, if a struct is owned by the caller, then so are all the struct's members). Also note that the "object-owned" memory-management policy, specified by the object_owns_result and object_owns_parameters modifiers, is not supported by DSM for metods invoked using the Dynamic Invocation Interface (DII). (This is because the "object-owned" policy is not CORBA-compliant, and because it precludes reusing Request objects to invoke a method multiple times.) | |||
====The CORBA policy for parameter memory management==== | ====The CORBA policy for parameter memory management==== | ||
When a class contains the SOM IDL modifier memory_management = corba, this signifies that all methods introduced by the class follow the CORBA specification for parameter memory management, except where a particular method has an explicit modifier (object_owns_result or object_owns_parameters) that indicates otherwise. The remainder of this section describes the CORBA specification for parameter memory management. | |||
Caller frees parameters and return results | |||
The CORBA memory-management policy specifies that the caller of a method is responsible for freeing all parameters and the return result after the method call is complete. This applies regardless of whether the parameter was allocated by the caller or the object (or, in the case of a remote method call, by DSOM). In other words, the CORBA policy asserts that parameters are uniformly "caller-owned". | |||
Allocation responsibilities | |||
Whether the parameter or return result should be allocated by the caller or by the object depends on the type of the parameter and its mode ("in", "inout", "out", or "return"). In general, the caller is responsible for allocating storage for most parameters and return results. More specifically, CORBA requires that storage for all "in" arguments, for all "inout" or "out" arguments, and for all "return" results must be provided by the client program, with certain exceptions as itemized below. | |||
The object is responsible for allocating storage as follows: | |||
*for strings when used as "out" arguments or as "return" results | |||
*for the "_buffer" field of sequences when used as "out" arguments or as "return" results, | |||
*for the "_value" field of anys when used as "inout" or "out" arguments or as "return" results, | |||
*for pointer types when used as "inout" or "out" arguments or as "return" results, | |||
*for arrays when used as "return" results, and | |||
*for objects when used as "inout" or "out" arguments or as "return" results. | |||
;Note: For "inout" strings and sequences, the "out" result is constrained to be no larger than the size of the "in" argument allocated by the client. | |||
Ownership of memory allocated in the above cases becomes the responsibility of the client program. For remote method calls, when a remote object allocates memory for a parameter or "return" value, DSOM subsequently allocates memory in the client's address space for the parameter or result. For a parameter/result that is an object (rather than a block of memory) DSOM automatically creates an object reference (a proxy object) in the client's address space. In each case, the memory or the proxy object becomes the responsibility of the client program and should later be freed by the client, using ORBfree for blocks of memory or release for proxy objects. | |||
====The 'somdReleaseResources' method and object-owned parameters==== | ====The 'somdReleaseResources' method and object-owned parameters==== | ||
As stated earlier, the CORBA policy asserts that method parameters and return results are uniformly caller-owned. This means the method caller has the responsibility for freeing memory after invoking a method, regardless of whether the memory was allocated by the caller or the object. | |||
A class implementor can designate certain method parameters and results as object-owned, however, by using the object_owns_result and object_owns_parameters SOM IDL modifiers. These modifiers signify that the object, rather than the caller, is responsible for freeing the memory associated with the parameter/result. For "in" parameters, the object can free the memory any time after receiving it; for "inout" and "out" parameters, and for return results, the object will free the memory sometime before the object is destroyed. (See the section entitled "Implementation statements" in Chapter 4, "SOM IDL and the SOM Compiler," for more information on these modifiers.) | |||
When a DSOM client program makes a remote method invocation, via a proxy, and the method being invoked has an object-owned parameter or return result, then the client-side memory associated with the parameter/result will be owned by the caller's proxy, and the server-side memory will be owned by the remote object. The memory owned by the caller's proxy will be freed when the proxy is released by the client program. (The time at which the server-side memory will be freed depends on the implementation of the remote object.) | |||
A DSOM client can also instruct a proxy object to free all memory that it owns on behalf of the client without releasing the proxy (assuming that the client program is finished using the object-owned memory), by invoking the somdReleaseResource method on the proxy object. Calling somdReleaseResources can prevent unused memory from accumulating in a proxy. | |||
For example, consider a client program repeatedly invoking a remote method "get_string", which returns a string that is designated (in SOM IDL) as "object-owned". The proxy on which the method is invoked will store the memory associated with all the returned strings, even if the strings are not unique, until the proxy is released. If the client program only uses the last result returned from "get_string", then unused memory accumulates in the proxy. The client program can prevent this by invoking somdReleaseResources on the proxy object periodicall (for example, each time it finishes using the result of the last "get_string" call). | |||
===Writing clients that are also servers=== | ===Writing clients that are also servers=== | ||
In many applications, processes may need to play both client and server roles. That is, objects in the process may make requests of remote objects on other servers, but may also implement and export objects, requiring that it be able to respond to incoming requests. Details of how to write programs in this peer-to-peer style are explained in section 6.9, "Advanced Topics". | |||
===Compiling and linking clients=== | ===Compiling and linking clients=== | ||
All client programs must include the header file "somd.h" (or for C++, "somd.xh") in addition to any "<className>.h" (or "<className>.xh") header files they require from application classes. All DSOM client programs must link to the SOMobjects Toolkit library: "libsomtk.a" on AIX and "somtk.lib" on OS/2. For more information, see the topic "Compiling and linking" in Chapter 3, "Using SOM Classes in Client Programs." | |||
==Basic Server Programming== | ==Basic Server Programming== |
Revision as of 04:29, 9 May 2021
Reprint Courtesy of International Business Machines Corporation, © International Business Machines Corporation
Note: The SOMobject Base Toolkit provides the capability for implementing Workstation Distributed System Object Module (DSOM) (distribution among processes on a single machine). Implementing an application that is distributed across a network of machines requires Workgroup DSOM, which is available only in the full-capability SOMobjects Developer Toolkit.
The following subjects are discussed in this section:
- A Simple DSOM Example
- Basic Client Programming
- Basic Server Programming
- Implementing Classes
- Configuring DSOM Applications
- Running DSOM Applications
- DSOM as a CORBA-compliant Object Request Broker
- Advanced Topics
- Error Reporting and Troubleshooting
- Limitations
What is Distributed SOM?
Whereas the power of SOM technology comes from the fact that SOM insulates the client of an object from the object's implementation, the power of DSOM lies in the fact that DSOM insulates the client of an object from the object's location.
Distributed SOM (or DSOM) provides a framework that allows application programs to access objects across address spaces. That is, application programs can access objects in other processes, even on different machines. Both the location and implementation of an object are hidden from a client, and the client accesses the object (by way of method calls) in the same manner regardless of its location.
DSOM currently supports two types of distribution:
- distribution among processes on the same machine (referred to as Workstation DSOM)
- and distribution among a network of machines (referred to as Workgroup DSOM).
DSOM runs on the AIX (Release 3.2.5 and above) and OS/2 (Release 2.0 and above) operating systems. A Workstation DSOM application can run on a machine in either environment using core capabilities of the SOMobjects system. Under the full capability SOMobjects Developer Toolkit, Workgroup DSOM supports distribution across local area networks comprised of both OS/2 and AIX systems. Future releases of DSOM may support large, enterprise-wide networks.
Support for TCP/IP and NewWare IPX/SPX is provided on AIX, OS/2, and Windows. NetBIOS support is provided for OS/2 and Windows. DSOM communications is extensible in that an application can provide its own transport (see Appendix C of the SOMobjects Base Toolkit Users Guide).
DSOM can be viewed in two ways:
- As a System Object Model extension that allows a program to invoke methods on SOM objects in other processes.
- As an Object Request Broker (ORB); that is, a standardized "transport" for distributed object interaction. In this respect, DSOM complies with the Common Object Request Broker Architecture (CORBA) specification, published by the Object Management Group (OMG) and x/Open.
This chapter describes DSOM from both perspectives.
DSOM features
Here is a quick summary of some of DSOM's more important features:
- Uses the standard SOM Compiler, Interface Repository, language bindings, and class libraries. Thus, DSOM provides a growth path for non-distributed SOM applications.
- Allows an application program to access a mix of local and remote objects. The fact that an object is remote is transparent to the program.
- Provides run-time services for creating, destroying, identifying, locating, and dispatching methods on remote objects. These services can be overridden or augmented to suit the application.
- Uses existing interprocess communication (IPC) facilities for Workstation communication, and common local area network (LAN) transport facilities for Workgroup communications. Support for TCP/IP, Netware IPX/SPX, and NetBios is provided. DSOM communications is extensible in that an application can provide its own transport.
- Provides support for writing multi-threaded servers and event-driven programs.
- Provides a default object server program, which can be easily used to create SOM objects and make those objects accessible to one or more client programs. If the default server program is used, SOM class libraries are loaded upon demand, so no server programming or compiling is necessary.
- Complies with the CORBA 1.1 specification, which is important for application portability.
When to use DSOM
DSOM should be used for those applications that require sharing of objects among multiple programs. The object actually exists in only one process (this process is known as the object's server); the other processes (known as clients) access the object through remote method invocations, made transparent by DSOM.
DSOM should also be used for applications that require objects to be isolated from the main program. This is usually done in cases where reliability is a concern, either to protect the object from failures in other parts of the application, or to protect the application from an object.
Some distributed applications may have special performance, reliability,or cooperative processing requirements, to which the SOM Replication framework is better suited. The Replication framework is oriented toward "groupware" (multi-party cooperative processing) applications, and has facilities for fault tolerance and recovery. The Replication framework is distinct from DSOM in that it maintains a complete replica of an object in each participant's address space, while DSOM establishes remote connections to shared objects. The Replication Framework is available only in the full-capability SOM objects Developer Toolkit.
Chapter Outline
Briefly, this section covers the following subjects:
- Tutorial example
- Programming DSOM applications
- Configuring DSOM applications
- Running DSOM applications
- DSOM and CORBA
- Advanced topics
- Error reporting and troubleshooting
Tutorial example
First, a complete example shows how an existing SOM class implementation (a "stack") can be used with DSOM to create a distributed "Stack" application. Using the "Stack" example as a backdrop, the basic DSOM interfaces are introduced.
Programming DSOM applications
All DSOM applications involve three kinds of programming:
- Client programming: writing code that uses objects;
- Server programming: writing code that implements and manages objects.
- Implementing classes: writing code that implements objects.
Three sections ("Basic Client Programming", "Basic Server Programming", and Implementing Classes") describe how to create DSOM applications from these three points of view. In turn, the structure and services of the relevant DSOM run-time environment are explained.
Note: The three sections are presented in the order above to aid in their explanation. However, the actual programming tasks are likely to be performed in the opposite order.
Additional examples are provided in these sections to illustrate DSOM services.
Configuring DSOM applications
The section "Configuring DSOM Applications" explains what is necessary to set up a DSOM application, once the application has been built.
Running DSOM applications
The section "Running DSOM Applications" explains what is necessary to run a DSOM application, once it has been built and configured.
DSOM and CORBA
Those readers interested in using DSOM as a CORBA-compliant ORB should read the section entitled "DSOM as a CORBA compliant Object Broker." That section answers the question: How are CORBA concepts implemented in DSOM?
Advanced topics
The section on "Advanced Topics" covers the following:
- Peer versus client/server processes" demonstrates how peer-to-peer object interactions are supported in DSOM.
- Dynamic Invocation Interface" details DSOM support for the CORBA dynamic invocation interface to dynamically build and invoke methods on remote objects.
- Creating user-supplied proxy classes" describes how to override proxy generation by the DSOM run time and, instead, install a proxy object supplied by the user.
- Customizing the default base proxy class"discusses how the SOMDClientProxy class can be subclassed to define a customized base class that DSOM will use during dynamic proxy-class generation.
- Sockets class" describes how DSOM uses Sockets subclasses.
Error reporting and troubleshooting
The section on "Error Reporting and Troubleshooting" discusses facilities to aid in problem diagnosis.
A Simple DSOM Example
A sample "Stack" application is presented in this section as a tutorial introduction to DSOM. It demonstrates that, for simple examples like a "Stack", after very little work, the class can be used to implement distributed objects that are accessed remotely. The example first presents the "Stack" application components and the steps that the implementer must perform before the application can be run, and then describes the run time activity that results from executing the application. This run-time scenario introduces several of the key architectural components of the DSOM run-time environment.
The source code for this example is provided with the DSOM samples in the SOMobjects Developer Toolkit.
The "Stack" interface
The example starts with the assumption that the class implementer has successfully built a SOM class library DLL, called "stack.dll", in the manner described in Section 5.6, "Creating a SOM Class Library," of Chapter 5, "Implementing Classes in SOM." The DLL implements the following IDL interface.
#include <somobj.idl> interface Stack: SOMObject { const long stackSize = 10; exception STACK_OVERFLOW{}; exception STACK_UNDERFLOW{}; boolean full(); boolean empty(); long top() raises(STACK_UNDERFLOW); long pop() raises(STACK_UNDERFLOW); void push(in long element) raises(STACK_OVERFLOW); #ifdef __SOMIDL__ implementation { releaseorder: full, empty, top, pop, push; somDefaultInit: override; long stackTop; // top of stack index long stackValues[stackSize]; // stack elements dllname = "stack.dll"; }; #endif };
This DLL could have been built without the knowledge that it would ever be accessed remotely (that is, built following the procedures in Chapter 5). Note, however, that some DLLs may require changes in the way their classes pass arguments and manage memory, in order to be used by remote clients (see the topic "Implementation Constraints" in section 6.5, "Implementing Classes").
The "Stack" class implementation
#define Stack_Class_Source #include <stack.ih> SOM_Scope boolean SOMLINK full(Stack somSelf, Environment *ev) { StackData *somThis = StackGetData(somSelf); StackMethodDebug("Stack","full"); /* Return TRUE if stack is full. */ return (_stackTop == stackSize); } SOM_Scope boolean SOMLINK empty(Stack somSelf, Environment *ev) { StackData *somThis = StackGetData(somSelf); StackMethodDebug("Stack","empty"); /* Return TRUE if stack is empty.*/ return (_stackTop == 0); } SOM_Scope long SOMLINK top(Stack somSelf, Environment *ev) { StackData *somThis = StackGetData(somSelf); StackMethodDebug("Stack","top"); if (_stackTop > 0) { /* Return top element in stack without removing it from * the stack. */ return (_stackValues[_stackTop-1]); } else { somSetException(ev, USER_EXCEPTION, ex_STACK_UNDERFLOW, NULL); return (-1L); } } SOM_Scope long SOMLINK pop(Stack somSelf, Environment *ev) { StackData *somThis = StackGetData(somSelf); StackMethodDebug("Stack","pop"); if (_stackTop > 0) { /* Return top element in stack and remove it from the * stack. */ _stackTop--; return (_stackValues[_stackTop]); } else { somSetException(ev, USER_EXCEPTION, ex_STACK_UNDERFLOW, NULL); return (-1L); } } SOM_Scope void SOMLINK push(Stack somSelf, Environment *ev, long el) { StackData *somThis = StackGetData(somSelf); StackMethodDebug("Stack","push"); if (_stackTop < stackSize) { /* Add element to top of the stack. */ _stackValues[_stackTop] = el; _stackTop++; } else { somSetException(ev, USER_EXCEPTION, ex_STACK_OVERFLOW, NULL); } } SOM_Scope void SOMLINK somDefaultInit(Stack somSelf, somInitCtrl* ctrl) { StackData *somThis; somInitCtrl globalCtrl; somBooleanVector myMask; StackMethodDebug("Stack","somDefaultInit"); Stack_BeginInitializer_somDefaultInit; Stack_Init_SOMObject_somDefaultInit(somSelf, ctrl); /* stackTop is index into stackValues for next pushed * stack element. * stackValues[0..(stackSize-1)] holds stack elements. */ _stackTop = 0; }
Client program using a local stack
A simple client program written to use a local "Stack" object is displayed below. This C program is shown so that the differences between a local and remote client program can be highlighted.
#include <stack.h> boolean OperationOK(Environment *ev); int main(int argc, char *argv[]) { Environment ev; Stack stk; long num = 100; SOM_InitEnvironment(&ev); /* The StackNewClass invocation is optional and unnecessary * in the client program when the class object is created in * the SOMInitModule function that is invoked during DLL * initialization. */ StackNewClass(Stack_MajorVersion, Stack_MinorVersion); stk = StackNew(); /* Verify successful object creation */ if ( stk != NULL ) { while ( !_full(stk, &ev) ) { _push(stk, &ev, num); somPrintf("Top: %d\n", _top(stk, &ev)); num += 100; } /* Test stack overflow exception */ _push(stk, &ev, num); OperationOK(&ev); while ( !_empty(stk, &ev) ) { somPrintf("Pop: %d\n", _pop(stk, &ev)); } /* Test stack underflow exception */ somPrintf("Top Underflow: %d\n", _top(stk, &ev)); OperationOK(&ev); somPrintf("Pop Underflow: %d\n", _pop(stk, &ev)); OperationOK(&ev); _push(stk, &ev, -10000); somPrintf("Top: %d\n", _top(stk, &ev)); somPrintf("Pop: %d\n", _top(stk, &ev)); _somFree(stk); } SOM_UninitEnvironment(&ev); return(0); } boolean OperationOK(Environment *ev) { char *exID; switch (ev->_major) { case SYSTEM_EXCEPTION: exID = somExceptionId(ev); somPrintf("System exception: %s\n", exID); somdExceptionFree(ev); return (FALSE); case USER_EXCEPTION: exID = somExceptionId(ev); somPrintf("User exception: %s\n", exID); somdExceptionFree(ev); return (FALSE); case NO_EXCEPTION: return (TRUE); default: somPrintf("Invalid exception type in Environment.\n"); somdExceptionFree(ev); return (FALSE); } }
Client program using a remote stack
The preceding program has been rewritten below showing how DSOM can be used to create and access a "Stack" object somewhere in the system. The exact location of the object does not matter to the application; it just wants a "Stack" object. Note that the stack operations of the two programs are identical. The main differences lie in stack creation and destruction, as highlighted below. (Also see "Memory management" later for more information on allocating and freeing memory.)
#include <somd.h> #include <stack.h> int main(int argc, char *argv]) { Environment ev; Stack stk; long num = 100; SOM_InitEnvironment(&ev); SOMD_Init(&ev); /* The StackNewClass invocation is optional and unnecessary * in the client program when the class object is created in * the SOMInitModule function that is invoked during DLL * initialization. */ StackNewClass (Stack_MajorVersion, Stack_MinorVersion); stk = _somdNewObject(SOMD_ObjectMgr, &ev, "Stack", ""); /* Verify successful object creation */ if ( OperationOK(&ev) ) { while ( !_full(stk, &ev) ) { _push(stk, &ev, num); somPrintf("Top: %d\n", _top(stk, &ev)); num += 100; } /* Test stack overflow exception */ _push(stk, &ev, num); OperationOK(&ev); while ( !_empty(stk, &ev) ) { somPrintf("Pop: %d\n", _pop(stk, &ev)); } /* Test stack underflow exception */ somPrintf("Top Underflow: %d\n", _top(stk, &ev)); OperationOK(&ev); somPrintf("Pop Underflow: %d\n", _pop(stk, &ev)); OperationOK(&ev); _push(stk, &ev, -10000); somPrintf("Top: %d\n", _top(stk, &ev)); somPrintf("Pop: %d\n", _top(stk, &ev)); _somdDestroyObject(SOMD_ObjectMgr, &ev, stk); if ( OperationOK(&ev) ) { somPrintf("Stack test successfully completed.\n"); } } SOMD_Uninit(&ev); SOM_UninitEnvironment(&ev); return(0); } boolean OperationOK(Environment *ev) { char *exID; switch (ev->_major) { case SYSTEM_EXCEPTION: exID = somExceptionId(ev); somPrintf("System exception: %s\n", exID); somdExceptionFree(ev); return (FALSE); case USER_EXCEPTION: exID = somExceptionId(ev); somPrintf("User exception: %s\n", exID); somdExceptionFree(ev); return (FALSE); case NO_EXCEPTION: return (TRUE); default: somPrintf("Invalid exception type in Environment.\n"); somdExceptionFree(ev); return (FALSE); } }
Let's step through the differences.
First, every DSOM program must include the file <somd.h> (when using C++, <somd.xh>). This file defines constants, global variables, and run-time interfaces used by DSOM. Usually, this file is sufficient to establish all necessary DSOM definitions.
Next, DSOM requires its own initialization call.
SOMD_Init(&ev);
The call to SOMD_Init initializes the DSOM run-time environment SOMD_Init must be called before any DSOM run-time calls are made. A side effect of calling SOMD_Init is that a run-time object, called the DSOM Object Manager, is created, and a pointer to it is stored in the global variable, SOMD_ObjectMgr, for programming convenience. The DSOM Object Manager provides basic run-time support for clients to find, create, destroy, and identify objects. The Object Manager is discussed in detail in the section entitled "Basic Client Programming."
Next, the local stack creation statement,
stk = StackNew();
was replaced by
stk = _somdNewObject(SOMD_ObjectMgr, &ev, "Stack", "");
The call to somdNewObject asks the DSOM Object Manager (SOMD_ObjectMgr) to create a "Stack" object, wherever it can find an implementation of "Stack". (There are other methods with which one can request specific servers.) If no object could be created, NULL is returned, and an exception is raised. Otherwise, the object returned is a "Stack" proxy.
Note: On AIX, the following call may be needed before the somdNewObject call, if the "Stack" class implementation has been linked directly with the program executable (vs. using a dynamic link library, or DLL). This call will properly initialize the class for use by DSOM (this initialization is done in SOMInitModulefor DLLs):
StackNewClass(Stack_MajorVersion, Stack_MinorVersion);
A proxy is an object that is a local representative for a remote target object. A proxy inherits the target object's interface, so it responds to the same methods. Operations invoked on the proxy are not executed locally, but are forwarded to the "real" target object for execution. The client program always has a proxy for each remote target object on which it operates.
From this point on, the client program treats the "Stack" proxy exactly as it would treat a local "Stack". The "Stack" proxy takes responsibility for forwarding requests to, and yielding results from, the remote "Stack". For example,
_push(stk,&ev,num);
causes a message representing the method call to be sent to the server process containing the remote object. The DSOM run-time in the server process decodes the message and invokes the method on the target object. The result (in this case, just an indication of completion) is then returned to the client process in a message. The DSOM run time in the client process decodes the result message and returns any result data to the caller.
At the end of the original client program, the local "Stack" was destroyed by the statement,
_somFree(stk);
whereas, in the client program above, the "Stack" proxy and the remote "Stack" are destroyed by the statement,
_somdDestroyObject(SOMD_ObjectMgr, &ev, stk);
If the client only wants to release its use of the remote object (freeing the proxy) without destroying the remote object, it can call the somdReleaseObject method instead of somdDestroyObject.
Finally, the client must shut down DSOM, so that any operating system resources acquired by DSOM for communications or process management can be returned:
SOMD_Uninit(&ev);
This call must be made at the end of every DSOM program.
Using specific servers
In DSOM, the process that manages a target object is called the object's server. Servers are implemented as programs that use SOM classes. Server implementations are registered with DSOM in an Implementation Repository. The Implementation Repository is a database queried by clients in order to find desired servers, and queried by DSOM in order to activate those servers upon demand.
The example above placed no constraints on the DSOM Object Manager as to where the remote "Stack" object should be created. The somdNewObject call creates a remote object of a specified class in an arbitrary server that implements that class. However, the DSOM Object Manager provides methods for finding specific servers.
For example, the client program above can be modified slightly to find a specific server named "StackServer", which has already been registered in DSOM's Implementation Repository. (Note that the programmer knew or discovered that the "StackServer" server implementation supports the "Stack" class.) The highlighted lines below show the changes that were made:
#include <somd.h> #include <stack.h> int main(int argc, char *argv[]) { Stack stk; Environment e; SOMDServer server; SOM_InitEnvironment(&e); SOMD_Init(&e); server = _somdFindServerByName(SOMD_ObjectMgr, &e, "StackServer"); stk = _somdCreateObj(server, &e, "Stack", ""); _push(stk,&e,100); _push(stk,&e,200); _pop(stk,&e); if (!_empty(stk,&e)) somPrintf("Top: %d\n", _top(stk,&e)); _somdDeleteObj(server, &e, stk); _somdReleaseObject(SOMD_ObjectMgr, &e, stk); _somdReleaseObject(SOMD_ObjectMgr, &e, server); SOMD_Uninit(&e); SOM_UninitEnvironment(&e); return(0); }
This version of the program replaces the somdNewObject operation with calls to somdFindServerByName and somdCreateObj. The somdFindServerByName method consults the Implementation Repository to find the DSOM server implementation whose name is "StackServer", and creates a server proxy, which provides a connection to that server. Every DSOM server process has a server object that defines methods to assist in the creation and management of objects in that server. Server objects must be instances of SOMDServer or one of its subclasses. The somdFindServerByName returns a proxy to the SOMDServer object in the named server.
Once the client has the server proxy, it can create and destroy objects in that server. The somdCreateObj call creates an object of the class "Stack" in the server named "StackServer".
To free the remote "Stack" object, the example shows a somdDeleteObj request on the stack object's server. Next, somdReleaseObject requests are made on the DSOM Object Manager, to free the stack proxy and the server proxy in the client. (Note that these three calls are equivalent to the somdDestroyObject call in the previous example.)
A note on finding existing objects
The two examples above show how clients can create a remote, transient object for their exclusive use. Clients may want to find and use objects that already exist. In that case, the calls to somdNewObject or somdCreateObj would be replaced with other "lookup" calls on some directory object that would take an object name or identifier and return a proxy to the remote object.
Such a directory object could be implemented by the application as a persistent SOM object, using DSOM to share it among processes.
The basic mechanisms that DSOM provides for naming and locating objects will be discussed in section "Basic Client Programming."
"Stack" server implementation
A server consists of three parts. First, a "main" program, when run, provides an address space for the objects it manages, and one or more process "threads" that can execute method calls. (Windows and AIX currently do not have multi-thread support, while OS/2 and AIX 4.1 do.) Second, a server object, derived from the SOMDServer class, provides methods used to manage objects in the server process. Third, one or more class libraries provide object implementations. Usually these libraries are constructed as dynamically linked libraries (DLLs), so they can be loaded and linked by a server program dynamically.
In this simple example, we can use the default DSOM server program, which is already compiled and linked. The default server behaves as a simple server, in that it simply receives and executes requests continuously. The default server creates its server object from the class, SOMDServer. The default server will load any class libraries it needs upon demand.
The "Stack" class library, "stack.dll", can be used without modification in the distributed application. This is possible because the "Stack" class is "well formed"; in other words, there are no methods that implicitly assume the client and the object are in the same address space.
Thus, by using the default server and the existing class library, a simple "Stack" server can be provided without any additional programming!
An application may require more functionality in the server program or the server object than the default implementations provide. A discussion on how to implement server programs and server objects is found later in this chapter, in section 6.4, "Basic Server Programming".
Compiling the application
DSOM programs and class libraries are compiled and linked like any other SOM program or library. The header file "somd.h" (or for C++, "somd.xh") should be included in any source program that uses DSOM services. DSOM run-time calls can be resolved by linking with the SOMobjects Toolkit library: "libsomtk.a" on AIX and "somtk.lib" on OS/2 or Windows. (The DSOM DLL(s) - "somd.dll", for AIX or OS/2, or "somd1.dll" and "somd1.dll" for Windows - will be loaded at run time.) For more information, see "Compiling and linking" in Chapter 3, "Using SOM classes in Client Programs," and the same topic in Chapter 5, "Implementing Classes in SOM."
Installing the implementation
Before the application can be run, certain environment variables must be set and the stack class and server implementations must be registered in the SOM Interface Repository and DSOM Implementation Repository.
Setting environment variables
Several environment variables are used by SOM and DSOM. These variables need to be set before registering the DSOM application in the Interface and Implementation Repositories.
For this example, the following environment variables could be set as shown. A full description of the environment variables and how to set them is given in section 6.6, "Configuring DSOM."
On AIX (in the syntax of the default shell, /bin/ksh):
export HOSTNAME=machine3 export SOMIR=$SOMBASE/etc/som.ir:/u/myuserid/my.ir export SOMDDIR=/u/myuserid/somddir export LIBPATH=$LIBPATH:$SOMBASE/lib:/u/myuserid/lib
On OS/2:
set USER=pat set HOSTNAME=machine3 set SOMDDIR=c:\somddir rem *** The following variables are set in CONFIG.SYS by rem *** the install program on OS/2, assuming "c:\som" is the rem *** value of %SOMBASE% supplied by the user. set SOMIR=c:\som\etc\som.ir;som.ir set LIBPATH=.;c:\som\lib;<previous LIBPATH>
On Windows:
set USER=pat set HOSTNAME=machine3 set SOMDDIR=c:\somddir rem *** The following variables are usually set in AUTOEXEC.BAT rem *** by the install program on Windows, assuming "c:\som" rem *** is the value of %SOMBASE% supplied by the user. set SOMIR=c:\som\etc\som.ir:som.ir set PATH=.;c:\som\lib;<previous PATH>
USER identifies the user of a DSOM client application. DSOM sends the USER ID with every remote method call, in case the remote object wishes to perform any access control checking. This is discussed later in the section "Basic Server Programming." (Note that USER is usually set automatically by AIX when a user logs in.)
HOSTNAME identifies the name of each machine running DSOM.
SOMIR gives a list of files that together constitute the Interface Repository. The IR is used by DSOM to guide the construction and interpretation of request messages. For DSOM, it is preferable to use full pathnames in the list of IR files, since the IR will be shared by several programs that may not all reside in the same directory.
SOMDDIR gives the name of a directory used to store DSOM configuration files, including the Implementation Repository.
LIBPATH (on AIX and OS/2) or PATH (on Windows) gives a list of directories where DLLs can be found.
Registering the class in the Interface Repository
Before an object can be accessed remotely by DSOM, it is necessary to register the class's interface and implementation in the Interface Repository (IR). DSOM uses the interface information when transforming local method calls on proxies into request messages transmitted to remote objects.
DSOM servers also consult the IR to find the name of the DLL for a dynamically loaded class. The DLL name for the "Stack" class must be specified using the dllname="stack.dll" modifier in the implementation statement of the "Stack" IDL. The Interface Repository is described in detail in Chapter 7, "The Interface Repository Framework."
The IDL specification of "Stack" is compiled into the Interface Repository using the following command:
sc -u -sir stack.idl (on AIX or OS/2) somc -u -sir stack.idl (on Windows)
When a class has not been compiled into the Interface Repository, DSOM will generate a run-time error when an attempt is made to invoke a method from that class. The error indicates that the method's descriptor was not found in the IR.
Registering the server in the Implementation Repository
It is necessary to register a description of a server's implementation in the Implementation Repository. DSOM uses this information to assist clients in finding servers, and in activating server processes upon demand.
For this example, where the default server is used, we need only to identify the server's name, and the class that the server implements. This is accomplished using the regimpl utility discussed in section 6.6, "Configuring DSOM Applications". The following commands define a default server, named "StackServer", which supports the Stack class:
regimpl -A -i StackServer regimpl -a -i StackServer -c Stack
Running the application
This section discusses:
- Starting the DSOM daemon
- Running the client
- Starting the DSOM daemon
Before running a DSOM application, the DSOM daemon, somdd, must be started.
- On AIX or OS/2, the daemon can be started manually from the command line, or it could be started automatically from a start-up script run at boot time. It may be run in the background with the commands somdd& on AIX and start somdd on OS/2. (The somdd program requires no parameters. An optional -q parameter can be used to set "quiet" mode, to suppress messages.)
- On Windows, the daemon can be started with the DSOM Daemon icon in the SOM icon group or started in Windows from the Run option of the file menu. The DSOM Daemon icon will change colors to indicate the daemon is ready, after which client and server programs can be started.
The somdd daemon is responsible for establishing a "binding" (that is, a connection) between a client process and a server. It will activate the desired server automatically, if necessary. The server can also be started manually prior to starting the client program, using the command dsom start stackServer.
- Running the client
Once the DSOM daemon is running, the application may be started. This is accomplished by running the client program. If the StackServer is not running, it will be started automatically by the DSOM daemon when the client attempts to invoke a method on one of its objects. After the client program ends, the server will continue to run, accepting connections from new clients. To terminate the server, use the command dsom stop stackServer.
"Stack" example run-time scenario
The following scenario steps through the actions taken by the DSOM run time in response to each line of code in the second "Stack" client program presented above. The illustration following the scenario shows the processes, and the objects within them, that participate in these actions.
- Initialize an environment for error passing:
SOM_InitEnvironment(&e);
- Initialize DSOM:
SOMD_Init(&e);
This causes the creation of the DSOM Object Manager (with SOMDObjectMgr interface). The global variable SOMD_ObjectMgr points to this object.
- Initialize "Stack" class object:
StackNewClass(Stack_MajorVersion, Stack_MinorVersion);
- Find the "StackServer" implementation and assign its proxy to the variable server:
server = _somdFindServerByName(SOMD_ObjectMgr, &e, "StackServer");
This causes the creation of the server proxy object in the client process. Proxy objects are shown as shaded circles. Note that the "real" server object in the server process is not created at this time. In fact, the server process has not yet been started.
- Ask the server object to create a "Stack" and assign "Stack" proxy to variable stack.
stk = _somdCreateObj(server, &e, "Stack", "");
This causes somdd, the DSOM daemon (which is already running) to activate the stack server process (by starting the "generic" server program). The stack server process, upon activation, creates the "real" SOMDServer object in the server process. The SOMDServer object works with the DSOM run time to create a local "Stack" object and return a "Stack" proxy to the client. (The details of this procedure are deferred until section 6.4, "Basic Server Programming".)
- Ask the "Stack" proxy to push 100 onto the remote stack:
_push(stk,&e,100);
This causes a message representing the method call to be marshalled and sent to the server process. In the server process, DSOM demarshals the message and, with the help of the SOMDServer, locates the target "Stack" object upon which it invokes the method ("push"). The result (which is void in this case) is then passed back to the client process in a message.
- Invoke more "Stack" operations on the remote stack, via the proxy:
_push(stk,&e,200); _pop(stk,&e); if (!_empty(stk,&e)) t = _top(stk,&e);
- Explicitly destroy both the remote stack, the stack proxy, and the server proxy:
_somdDeleteObj(server, &e, stk); _somdReleaseObject(SOMD_ObjectMgr, &e, stk); _somdReleaseObject(SOMD_ObjectMgr, &e, server);
- Free the error-passing environment:
SOM_UninitEnvironment(&e);
This scenario has introduced the key processes in a DSOM application: client, server, and somdd. Also introduced are the key objects that comprise the DSOM run-time environment: the SOMD_ObjectMgr in the client process and the SOMD_ServerObject in the server process.
Summary
This example has introduced the key concepts of building, installing, and running a DSOM application. It has also introduced some of the key components that comprise the DSOM application run-time environment.
The following sections, "Basic Client Programming," "Basic Server Programming," and "Implementing Classes," provide more detail on how to use, manage, and implement remote objects, respectively.
Basic Client Programming
For the most part, client programming in DSOM is exactly the same as client programming in SOM, since DSOM transparently hides the fact that an object is remote when the client accesses the object.
However, a client application writer also needs to know how to create, locate, use, save, and destroy remote objects. (This is not done using the usual SOM bindings.) The DSOM run-time environment provides these services to client programs primarily through the DSOM Object Manager. These run-time services will be detailed in this section. Examples of how an application developer uses these services are provided throughout the section.
DSOM Object Manager
DSOM defines a DSOM Object Manager, which provides services needed by clients to create, find and use objects in the DSOM run-time environment.
The DSOM Object Manager is derived from an abstract, generic "object manager" class, called ObjectMgr. This abstract ObjectMgr class defines a basic set of methods that support object creation, location (with implicit activation), and destruction.
As an abstract class, ObjectMgr defines only an interface; there is no implementation associated with ObjectMgr. Consequently, an application should not create instances of the ObjectMgr class.
An abstract Object Manager class was defined under the expectation that applications will often need simultaneous access to objects implemented and controlled by a variety of object systems. Such object systems may include other ORBs (in addition to DSOM), persistent object managers, object-oriented databases, and so forth. It is likely that each object system will provide the same sort of basic services for object creation, location, and activation, but each using a different interface.
Thus, the ObjectMgr abstract class defines a simple and "universal" interface that can be mapped to any object system. The application would only have to understand a single, common ObjectMgr interface. Under this scheme, specific object managers are defined by subclassing the ObjectMgr class and overriding the ObjectMgr methods to map them into the object system-specific programming interfaces.
DSOM's Object Manager, SOMDObjectMgr, is defined as a specific class of ObjectMgr. It defines methods for:
- Finding servers that implement particular kinds of objects
- Creating objects in servers
- Obtaining object identifiers (string IDs)
- Finding objects, given their identifiers
- Releasing and destroying objects
These functions will be discussed in the remainder of this section.
- Note
- The OMG has standardized an "object lifecycle" service, which includes support for creating and destroying distributed objects. The DSOM Object Manager may be augmented in the future with an OMG-compliant lifecycle service.
Initializing a client program
A client application must declare and initialize the DSOM run time before attempting to create or access a remote object. The SOMD_Init procedure initializes all of the DSOM run time, including the SOMDObjectMgr object. The global variable, SOMD_ObjectMgr is initialized to point to the local DSOM Object Manager.
A client application must also initialize all application classes used by the program. For each class, the corresponding <className>NewClass call should be made.
Note: In non-distributed SOM programs, the <className>New macro (and the new operator provided for each class by the SOM C++ bindings) implicitly calls the procedure <className>NewClass when creating a new object. This is not currently possible in DSOM because, when creating remote objects, DSOM uses a generic method that is not class-specific.
This was shown in the "Stack" example in section 6.2. In a similar example of an application that uses "Car" and "Driver" objects, the initialization code might look like this:
#include <somd.h> /* needed by all clients */ #include <Car.h> /* needed to access remote Car */ #include <Driver.h> /* needed to access remote Driver */ main() { Environment ev; /* ev used for error passing */ SOM_InitEnvironment(&ev); /* Do DSOM initialization */ SOMD_Init(&ev); /* Initialize application classes */ CarNewClass(Car_MajorVersion, Car_MinorVersion); DriverNewClass(Driver_MajorVersion, Driver_MinorVersion); ... }
As shown, client programs should include the "somd.h" file (or, for C++ programs, the "somd.xh" file) in order to define the DSOM run#time interfaces.
Note also that, since Environments are used for passing error results between a method and its caller, an Environment variable (ev) must be declared and initialized for this purpose.
The calls to "CarNewClass" and "DriverNewClass" are required if the client will be creating or accessing Cars and Drivers. The procedures "CarNewClass" and "DriverNewClass" create class objects for the classes "Car" and "Driver". When a DSOM Object Manager method like somdNewObject is invoked to create a "Car", it expects the "Car" class object to exist. If the class does not yet exist, the "ClassNotFound" exception will be returned.
Exiting a client program
At the end of a client program, the SOMD_Uninit procedure must be called to free DSOM run-time objects, and to release system resources such as semaphores, shared memory segments, and so on. SOMD_Uninit should be called even if the client program terminates unsuccessfully; otherwise, system resources will not be released.
For example, the exit code in the client program might look like this:
... SOMD_Uninit(&e); SOM_UninitEnvironment(&e); }
Note also the SOM_UninitEnvironment call, which frees any memory associated with the specified Environment structure.
- Note
- When a Windows DSOM client receives a WM-QUIT message while processing a remote method invocation, DSOM will clean up and terminate the client without returning to the client's WinProc or WinMain.
Creating remote objects
Distributed objects can be created in several different ways in DSOM.
- The client can create an object on any server that implements that class of object.
- The client can find a specific server upon which to create an object.
- A server can create an object and register a reference to the object in some well-known directory. (An object reference contains information that reliably identifies a particular object.)
The first two cases are discussed immediately below. The last case is discussed near the end of this section.
Creating an object in an arbitrary server
Following is an example of how to create a new remote object in the case where the client does not care in which server the object is created. In this situation, the client defers these decisions to the DSOM Object Manager (SOMD_ObjectMgr) by using the somdNewObject method call, which has this IDL definition:
// (from file om.idl) SOMObject somdNewObject(in Identifier objclass, in string hints); // Returns a new object of the named class. This is a "basic" // creation method, where the decisions about where and how to // create the object are mostly left up to the Object Manager. // However, the Object Manager may optionally define creation // "hints" which the client may specify in this call.
Here is the example of how a remote "Car" would be created using somdNewObject:
#include <somd.h> #include <Car.h> main() { Environment ev; Car car; SOM_InitEnvironment(&ev); SOMD_Init(&ev); /* create the class object */ CarNewClass(Car_MajorVersion, Car_MinorVersion); /* create a Car object on some server, let the Object Manager choose which one */ car = _somdNewObject(SOMD_ObjectMgr, &ev, "Car", ""); ... }
The main argument to the somdNewObject method call is a string specifying the name of the class of the desired object. The last argument is a string that may contain "hints" for the Object Manager when choosing a server. In this example, the client is providing no hints. (Currently, the DSOM Object Manager simply passes the hints to the server object in a somdCreateObj call.)
Proxy objects
As far as the client program is concerned, when a remote object is created, a pointer to the object is returned. However, what is actually returned is a pointer to a proxy object, which is a local representative for the remote target object.
Proxies are responsible for ensuring that operations invoked on it get forwarded to the "real" target object that it represents. The DSOM run time creates proxy objects automatically, wherever an object is returned as a result of some remote operation. The client program will always have a proxy for each remote target object on which it operates. Proxies are described further in the sections entitled "DSOM as a CORBA-compliant Object Request Broker" and "Advanced Topics".
In the example above, a pointer to a "Car" proxy is returned and put in the variable "car". Any subsequent methods invoked on "car" will be forwarded and executed on the corresponding remote "Car" object.
Proxy objects inherit behavior from the SOMDClientProxy class.
Servers and server objects
In DSOM, the process that manages a target object is called the object's server. Servers are implemented as programs that use SOM classes. The example above placed no constraints on the DSOM Object Manager as to which server should create the remote "Car" object. However, if the client desires more control over distribution of objects, the DSOM Object Manager provides methods for finding specific servers.
Server implementations are registered with DSOM in an Implementation Repository. Server implementations are described by a unique ID, a unique (user-friendly) name, the program name that implements the server, the classes that are implemented by the server, the machine on which the server is located, whether the server is multi-threaded, and so forth. (See section 6.6 for more information on registering server implementations.) A client can ask the DSOM Object Manager to find a particular server:
- By name
- By ID
- By a class it supports
When a client asks for a "server", it is given (a proxy to) a server object that provides interfaces for managing the objects in the server. There is one server object per server process. All server objects are instances of the SOMDServer class, or its subclasses. The default method provided by SOMDServer for creating objects is:
// (from file somdserv.idl) SOMObject somdCreateObj(in Identifier objclass, in string hints); // Creates an object of the specified class. This method // may optionally define creation "hints" which the client // may specify in this call. (Hints are ignored by default.)
Section 6.4 explains how to create application-specific server objects, derived from SOMDServer, which override SOMDServer methods and introduce their own methods for object management.
Creating an object in a specific server
The following example demonstrates how a client application creates a new object in a remote server chosen by the client. The DSOM Object Manager method somdFindServerByName is used to find and create a proxy to the server object for the server implementation named "myCarServer". The method somdCreateObj is then invoked on the server object to create the remote "Car". A proxy to the remote "Car" is returned. (The "Stack" client presented in the previous section used the same methods to create a remote "Stack".)
/* find a specific Car server */ server = _somdFindServerByName(SOMD_ObjectMgr, &ev, "myCarSe rver"); /* create a remote Car object on that server */ car = _somdCreateObj(server, &ev, "Car", ""); ... }
- Note
- If the specified server does not provide any implementation of the desired class, a NULL pointer will be returned and a "ClassNotFound" exception will be raised.
Three other methods can be invoked on the DSOM Object Manager to find server implementations: somdFindServer, somdFindServersByClass, and somdFindAnyServerByClass. The IDL declarations of these methods follow:
SOMDServer somdFindServer(in ImplId serverid); sequence<SOMDServer> somdFindServersByClass(in Identifier objclass); SOMDServer somdFindAnyServerByClass(in Identifier objclass);
The somdFindServer method is similar to the somdFindServerByName method, except that the server's implementation ID (of type ImplId) is used to identify the server instead of the server's user-friendly name (or "alias"). The implementation ID is a unique string generated by the Implementation Repository during server registration. (See section 6.6 for more details.)
The somdFindServersByClass method, given a class name, returns a sequence of all servers that support the given class. The client program may then choose which server to use, based on the server's name, program, or other implementation attributes (e.g., the server is multi-threaded). (See the topic below, "Inquiring about a remote object's implementation.")
Finally, the somdFindAnyServerByClass method simply selects any one of the server implementations registered in the Implementation Repository that supports the given class, and returns a server proxy for that server.
Once the server proxy is obtained, methods like somdCreateObj, shown in the example above, can be invoked upon it to create new objects.
Inquiring about a remote object's implementation
A client may wish to inquire about the (server) implementation of a remote object. All objects in a server, including the "server object", share the same implementation definition. This is common when using the somdFindServersByClass call, where a sequence of server proxies is returned, and some choice must be made about which to use.
When a proxy is obtained by a client, the client can inquire about the underlying server implementation by obtaining its corresponding ImplementationDef. An ImplementationDef object contains a set of attributes that describe a server implementation. To get the ImplementationDef associated with a remote object, the get_implementation method (implemented on SOMDObject and inherited by SOMDClientProxy) can be called.
For example, if a program has a proxy for a remote server object, it can get the ImplementationDef for the server with method calls similar to the following:
ImplementationDef implDef; SOMDServer server; ... implDef = _get_implementation(server, &ev);
Once the ImplementationDef has been obtained, the application can access its attributes using the _get_impl_xxx methods.
The ImplementationDef class is discussed further in section 6.6, "Configuring DSOM."
Destroying remote objects
There are several ways of destroying objects or their proxies in DSOM, just as there are several ways to create objects. Remote objects can be asked to destroy themselves, or, the SOMDObjectMgr and the SOMDServer can participate in the deletion.
Destroying objects via a proxy
DSOM provides means for deleting remote objects via their proxies. For example, if somFree is invoked on a proxy, the somFree call gets forwarded directly to the target object, just like any other target method call. For example,
_somFree(car);
frees the remote car. Note that, by default, invoking somFree on the proxy does not free the proxy, only the remote object. However, the following call can be issued as part of a client-program initialization, so that invoking somFree on a proxy frees both the remote object and the proxy:
__set_somd21somFree(SOMD_ObjectMgr, ev, TRUE);
All subsequent invocations of somFree on a proxy object will result in both the remote object and the proxy being freed.
To be explicit about whether the proxy or the remote object is being deleted, the methods somdTargetFree and somdProxyFree, defined on proxies, can be used:
_somdTargetFree(car, &ev);
frees the remote "Car" (but not the proxy) and
_somdProxyFree(car, &ev);
frees the proxy (but not the remote "Car").
- Note
- CORBA specifies a third method for deleting object references. (Proxies are a specialized type of object reference.) The method
_release(car, &ev);
deletes the proxy (but not the target object).
Destroying objects via the DSOM Object Manager
Having created a remote object with somdNewObject or somdCreateObj, the remote object and its local proxy may be destroyed by invoking the method somdDestroyObject on the DSOM Object Manager using the proxy as an argument. For example,
/* create the car */ car = _somdNewObject(SOMD_ObjectMgr, &ev, "Car", ""); ... /* destroy the car (and its proxy) */ _somdDestroyObject(SOMD_ObjectMgr, &ev, car);
If the client does not want to destroy the remote object, but is finished working with it, the somdReleaseObject method should be used instead, e.g.,
_somdReleaseObject(SOMD_ObjectMgr, &ev, car);
This deletes the local proxy, but not the remote object.
Both somdDestroyObject and somdReleaseObject are defined on the ObjectMgr, so that the Object Manager is aware of the client's actions, in case it wants to do any bookkeeping.
The object passed to either the somdDestroyObject method or the somdReleaseObject method can be either a local SOM object or a DSOM proxy object. When a local SOM object is passed, somdDestroyObject has the same behavior as somFree. If a local SOM object is passed to somdReleaseObject, however, this has no effect.
Destroying objects via a server object
The somdDestroyObject method described above sends a request to delete a remote object to the object's server. It does so to ensure that the server has an opportunity to participate in, if not perform, the deletion. The method defined on the SOMDServer class for destroying objects is somdDeleteObj. If the client has a proxy for the server object, it can also invoke somdDeleteObj directly, instead of calling somdDestroyObject.
Destroying objects via the server object, rather than asking the object itself (as in somFree or somdTargetFree), allows the server object do any clean-up that is needed. For simple applications, this may not be necessary, but for applications that provide their own application-tailored server objects, it may be critical. See, for example, the persistent server example in section 6.4, entitled "Basic Server Programming."
Creating remote objects using user-defined metaclasses
An application may wish to define its own constructor methods for a particular class, via a user-supplied metaclass. In this case, the somdNewObject method should not be used, since it simply calls the default constructor method, somNew, defined by SOMClass.
Instead, the application can obtain a proxy to the actual class object in the server process. It can do so via the somdGetClassObj method, invoked on the SOMDServer proxy returned by one of the somdFindServerXxx methods. The application-defined constructor method can then be invoked on the proxy for the remote class object.
- Note
- The same issues apply to destructor methods. If the application defines its own destructor methods, they can be called via the class object returned by somdGetClassObj, as opposed to calling somdDestroyObject.
The following example creates a new object in a remote server using an application-defined constructor method, "makeCar", which is assumed to have been defined in the metaclass of "Car", named "MetaCar".
#include <somd.h> #include <Car.h> main( ) { Environment ev; SOMDServer server; Car car; MetaCar carClass; SOM_InitEnvironment(&ev); SOMD_Init(&ev); /* find a Car server */ server = _somdFindAnyServerByClass(SOMD_ObjectMgr, &ev, "Car"); /* get the class object for Car */ carClass = (MetaCar) _somdGetClassObj(server, &ev, "Car"); /* create the car object */ car = _makeCar(carClass, &ev, "Red", "Toyota", "2-door"); ... }
Saving and restoring references to objects
A proxy is a kind of "object reference". An object reference contains information that is used to identify a target object.
To enable clients to save references to remote objects (in a file system, for example) or exchange references to remote objects (with other application processes), DSOM must be able to externalize proxies. To "externalize a proxy" means to create a string ID for a proxy that can be used by any process to identify the remote target object. DSOM must also support the translation of string IDs back into proxies.
The DSOM Object Manager defines two methods for converting between proxies and their string IDs: somdGetIdFromObject and somdGetObjectFromId.
Here is an example client program that creates a remote "Car" object. It generates a string ID corresponding to the proxy, and saves the string ID to a file for later use.
#include <stdio.h> #include <somd.h> #include <Car.h> main( ) { Environment ev; Car car; string somdObjectId; FILE* file; SOM_InitEnvironment(&ev); SOMD_Init(&ev); /* create a remote Car object */ car = _somdNewObject(SOMD_ObjectMgr, &ev, "Car", ""); /* save the reference to the object */ somdObjectId = _somdGetIdFromObject(SOMD_ObjectMgr, &ev, car); file = fopen("/u/joe/mycar", "w"); fprintf(file, "%s", somdObjectId); ...
Next is an example client program that retrieves the string ID and regenerates a valid proxy for the original remote "Car" object (assuming the remote "Car" object can still be found in the server).
... Environment ev; Car car; char buffer[256]; string somdObjectId; FILE* file; ... /* restore proxy from its string form */ file = fopen("/u/joe/mycar", "r"); somdObjectId = (string) buffer; fscanf(file, "%s", somdObjectId); car = _somdGetObjectFromId(SOMD_ObjectMgr, &ev, somdObjectId); ...
Once the proxy has been regenerated, methods can be invoked on the proxy and they will be forwarded to the remote target object, as always.
- Note
- The somdGetIdFromObject and somdGetObjectFromId methods directly correspond to the CORBA methods ORB_object_to_string and ORB_string_to_object, defined on the ORB class.
Finding existing objects
The SOMDObjectMgr and SOMDServer classes support the methods described above, which allow clients to create objects in servers. However, it is also likely that clients will want to find and use objects that have already been created, usually by the servers that implement them. For example, a print service will create printer objects, and must then export them to clients. In that case, the calls to somdNewObject or somdCreateObj would be replaced with other "lookup" calls on some directory (server) object which would take an object name or identifier and return a proxy to a corresponding remote object. Likewise, the server that owns the object would register the exported object in the directory.
It is important to understand that DSOM does not provide a directory service such as the one described. But such a directory object could be implemented by the application, where a table or collection object maps object names to proxies. The string IDs for the proxies in the directory object could be saved using a file (as above) or a persistent object (via the Persistence Framework of SOMobject Developer Toolkit). A directory server implemented using DSOM could be used to share the directory among processes.
Upon a lookup call, the directory server could find the corresponding proxy (or its string ID) in the directory, and return it to the caller.
Finding server objects
The DSOM Object Manager can be used to find server object proxies using the somdFindServerXxx methods. However, it is important to point out that an application can also augment those services, by managing server proxies itself. Server proxies can be maintained in an application-specific directory, stored in a file, or passed from process to process, just as any other proxies.
Invoking methods on remote objects
As described earlier, DSOM proxies are local representatives of remote objects, and as such, they can be treated like the target objects themselves. Method calls are invoked in exactly the same manner as if the object is local. This is true both for method calls using the static bindings (as most of our examples have shown), as well as for dynamic dispatching calls, where SOM facilities (such as the somDispatch method) are used to construct method calls at run time.
CORBA 1.1 also defines a dynamic invocation interface that is implemented by DSOM. It is described later in section 6.9, "Advanced Topics".
The DSOM run time is responsible for transporting any input method argument values supplied by the caller (defined by legal IDL types) to the target object in a remote call. Likewise, the DSOM run time transports the return value and any output argument values back to the caller following the method call.
Note: DSOM uses the Interface Repository (IR) to discover the "signature" of a method (that is, the method's prototype). It is important that the contents of the IR match the method bindings used by the application program (i.e. the same IDL file is used to update the IR and to generate bindings).
DSOM can make remote invocations only of methods whose parameter types are among the following IDL types: basic types (short, long, unsigned short, unsigned long, float, double, char, boolean, octet), enum, struct, union, sequence, string, array, any, and object. The members of a struct, union, sequence, or array and the value of an any, must also be from the above list of supported DSOM types.
In addition to the preceding types, DSOM also supports method parameters of type pointer to one of the above types (for example, long*) Pointers to pointers are not supported, however, and pointers embedded within one of the above types (for example, a pointer within a struct) are not supported The "void *" type is also not supported. Currently, DSOM has the limitation that NULL pointer values cannot be returned as inout or out method arguments although it is expected that this limitation will be addressed in a future release.
Types declared as SOMFOREIGN types are not currently supported by DSOM. Because the SOM somI is declared as a SOMFOREIGN type, this implies that any method having a parameter of type somId cannot be invoked remotely using DSOM. This restriction includes the SOM methods: somRespondsTo, somSupportsMethod, somGetMethodDescriptor, somGetMethodIndex, and somGetNthMethodInfo.
When a method parameter is an object type (that is, an instance of SOMObject or some class derived from SOMObject), a client program making a remote invocation of that method must pass an object reference for that parameter, rather than passing a local SOMObject, unless the client program is also DSOM server program, in which case DSOM will automatically convert the local object into an object reference.
Methods having the procedure SOM IDL modifier cannot be invoked remotely using DSOM. This is because these "methods" are called directly, rather than via the normal method resolution mechanisms on which DSOM relies.
Determining memory allocation and ownership
When a method is invoked that returns a result of type string, sequence, or array, DSOM will allocate memory in the client's address space for the result. Ownership of this memory becomes the responsibility of the client program. When the client program has finished using it, the client should free the memory using the ORBfree function, rather than using free or SOMFree (This is because the memory has been allocated by DSOM using special memory management techniques; therefore, the client should ask DSOM to also free the memory.)
When invoking a method using DSOM, the client program is responsible for providing storage for all in arguments and for all inout/out arguments, with the following exceptions: DSOM will allocate storage for a string or for the _buffer field of a sequence when used as an out argument, and will allocate storage for the_value field of an any when used as an inout or out argument. This storage becomes the responsibility of the client program and should later be freed using ORBfree. For a string or sequence used as an inout argument, the out result is constrained to be no larger than the size of the in argument allocated by the client.
Passing object references in method calls
When pointers to objects are returned as method output values (as in the previous examples), DSOM automatically converts the object pointers (in the server) to object proxies in the client.
Likewise, when a client passes object (proxy) pointers as input arguments to a method, DSOM automatically converts the proxy argument in the client to an appropriate object reference in the server.
- Note
- If the proxy is for an object that is in the same server as the target object, DSOM gives the object reference to the server object for resolution to a SOM object pointer. Otherwise, DSOM leaves the proxy alone, since the proxy must refer to an object in some process other than the target's server.
Memory management
DSOM programs must manage four different kinds of memory resources: objects, object references, Environment structures, and blocks of memory. There are different techniques for allocating and releasing each kind of resource.
- Objects and object references
Creating and destroying remote objects was discussed previously in this section (see "Creating remote objects" and "Destroying remote objects"). Creating and destroying local objects is described in section 3.2, "Using SOM Classes - the Basics," in Chapter 3, "Using SOM Classes in Client Programs." Object references are typically created automatically by DSOM as needed by the client program. They are also released in the client program by using either the release method or the somdProxyFree method. (The two methods are equivalent.)
- Environment structures
When a client invokes a method and the method returns an exception in the Environment structure, it is the client's responsibility to free the exception. This is done by calling either exception_free or somdExceptionFree on the Envirnmen structure in which the exception was returned. (The two functions are equivalent.) A similar function, somExceptionFree, is available for SOM programmers however DSOM programmers can use somdExceptionFree to free all exceptions (regardless of whether they were returned from a local or remote method call).
- Blocks of memory
For allocating and releasing blocks of memory within a client program, SOM provides the SOMMalloc and SOMFree functions (analogous to the C "mallo" and "free" functions). The "Memory Management" section of Chapter 3 describes these functions. To release memory allocated by DSOM in response to a remote method call, however, DSOM client programs should use the ORBfree function
For example, when a method is invoked that returns a result of type string, sequence, or array, DSOM will allocate memory for the result in the client's address space. Ownership of this memory becomes the responsibility of the client program. When finished using this memory, the client program should free it using the ORBfree function, rather than free or SOMFree. This is because the memory has been allocated by DSOM using special memory-management techniques; therefore, the client should ask DSOM to also free the memory. If the storage is freed using SOMFree rather than ORBfree, then memory leaks will result.
The differences between the SOMFree and ORBfree functions are twofold:
1.First, SOMFree should only be used to free memory not allocated by DSO(for example, memory the client program allocated itself using SOMMalloc), while ORBfree should be used to free memory allocated by DSOM in response to a remote method call.
2.Second, SOMFee only frees a single block of memory (in the same way that the C "free" function does), while ORBfree will free an entire data structure, including any allocated blocks of memory within in. For example, if a remote method call returns a sequence of structs, and each struct contains a string, ORBfree will free, with a single call, not only the sequence's "_buffer" member, but also each struct and all the strings within the structs. Freeing a similar data structure using SOMFree would require multiple calls (one for each call to SOMMalloc used to build the data structure).
Some programmers may wish to use a single function to free blocks of memory, regardless of whether they were allocated locally or by DSOM in response to a remote method call. For these programmers, DSOM provides a function, SOMD_NoORBfree, which can be called just after calling SOMD_Init in the client program. (It requires no arguments and returns no value.) This function specifies that the client program will free all memory blocks using SOMFree, rather than ORBfree. In response to this call, DSOM will not keep track of the memory it allocates for the client. Instead, it assumes that the client program will be responsible for walking all data structures returned from remote method calls, while calling SOMFree for each block of memory within.
Memory management for method parameters
For each method, five SOM IDL modifiers are available to specify the method's memory-management policy (that is, whether the caller or the object owns the parameters' memory after the method is invoked). These modifiers are memory_management, caller_owns_result, caller_owns_parameters, object_owns_result, and object_owns_parameters. For a complete description of these modifiers and their meanings, see the section entitled "Implementation Statements" in Chapter 4, "SOM IDL and the SOM Compiler."
Note that the memory-management policy for a particular parameter applies to the parameter and all the memory embedded within it (for example, if a struct is owned by the caller, then so are all the struct's members). Also note that the "object-owned" memory-management policy, specified by the object_owns_result and object_owns_parameters modifiers, is not supported by DSM for metods invoked using the Dynamic Invocation Interface (DII). (This is because the "object-owned" policy is not CORBA-compliant, and because it precludes reusing Request objects to invoke a method multiple times.)
The CORBA policy for parameter memory management
When a class contains the SOM IDL modifier memory_management = corba, this signifies that all methods introduced by the class follow the CORBA specification for parameter memory management, except where a particular method has an explicit modifier (object_owns_result or object_owns_parameters) that indicates otherwise. The remainder of this section describes the CORBA specification for parameter memory management.
Caller frees parameters and return results
The CORBA memory-management policy specifies that the caller of a method is responsible for freeing all parameters and the return result after the method call is complete. This applies regardless of whether the parameter was allocated by the caller or the object (or, in the case of a remote method call, by DSOM). In other words, the CORBA policy asserts that parameters are uniformly "caller-owned".
Allocation responsibilities
Whether the parameter or return result should be allocated by the caller or by the object depends on the type of the parameter and its mode ("in", "inout", "out", or "return"). In general, the caller is responsible for allocating storage for most parameters and return results. More specifically, CORBA requires that storage for all "in" arguments, for all "inout" or "out" arguments, and for all "return" results must be provided by the client program, with certain exceptions as itemized below.
The object is responsible for allocating storage as follows:
- for strings when used as "out" arguments or as "return" results
- for the "_buffer" field of sequences when used as "out" arguments or as "return" results,
- for the "_value" field of anys when used as "inout" or "out" arguments or as "return" results,
- for pointer types when used as "inout" or "out" arguments or as "return" results,
- for arrays when used as "return" results, and
- for objects when used as "inout" or "out" arguments or as "return" results.
- Note
- For "inout" strings and sequences, the "out" result is constrained to be no larger than the size of the "in" argument allocated by the client.
Ownership of memory allocated in the above cases becomes the responsibility of the client program. For remote method calls, when a remote object allocates memory for a parameter or "return" value, DSOM subsequently allocates memory in the client's address space for the parameter or result. For a parameter/result that is an object (rather than a block of memory) DSOM automatically creates an object reference (a proxy object) in the client's address space. In each case, the memory or the proxy object becomes the responsibility of the client program and should later be freed by the client, using ORBfree for blocks of memory or release for proxy objects.
The 'somdReleaseResources' method and object-owned parameters
As stated earlier, the CORBA policy asserts that method parameters and return results are uniformly caller-owned. This means the method caller has the responsibility for freeing memory after invoking a method, regardless of whether the memory was allocated by the caller or the object.
A class implementor can designate certain method parameters and results as object-owned, however, by using the object_owns_result and object_owns_parameters SOM IDL modifiers. These modifiers signify that the object, rather than the caller, is responsible for freeing the memory associated with the parameter/result. For "in" parameters, the object can free the memory any time after receiving it; for "inout" and "out" parameters, and for return results, the object will free the memory sometime before the object is destroyed. (See the section entitled "Implementation statements" in Chapter 4, "SOM IDL and the SOM Compiler," for more information on these modifiers.)
When a DSOM client program makes a remote method invocation, via a proxy, and the method being invoked has an object-owned parameter or return result, then the client-side memory associated with the parameter/result will be owned by the caller's proxy, and the server-side memory will be owned by the remote object. The memory owned by the caller's proxy will be freed when the proxy is released by the client program. (The time at which the server-side memory will be freed depends on the implementation of the remote object.)
A DSOM client can also instruct a proxy object to free all memory that it owns on behalf of the client without releasing the proxy (assuming that the client program is finished using the object-owned memory), by invoking the somdReleaseResource method on the proxy object. Calling somdReleaseResources can prevent unused memory from accumulating in a proxy.
For example, consider a client program repeatedly invoking a remote method "get_string", which returns a string that is designated (in SOM IDL) as "object-owned". The proxy on which the method is invoked will store the memory associated with all the returned strings, even if the strings are not unique, until the proxy is released. If the client program only uses the last result returned from "get_string", then unused memory accumulates in the proxy. The client program can prevent this by invoking somdReleaseResources on the proxy object periodicall (for example, each time it finishes using the result of the last "get_string" call).
Writing clients that are also servers
In many applications, processes may need to play both client and server roles. That is, objects in the process may make requests of remote objects on other servers, but may also implement and export objects, requiring that it be able to respond to incoming requests. Details of how to write programs in this peer-to-peer style are explained in section 6.9, "Advanced Topics".
Compiling and linking clients
All client programs must include the header file "somd.h" (or for C++, "somd.xh") in addition to any "<className>.h" (or "<className>.xh") header files they require from application classes. All DSOM client programs must link to the SOMobjects Toolkit library: "libsomtk.a" on AIX and "somtk.lib" on OS/2. For more information, see the topic "Compiling and linking" in Chapter 3, "Using SOM Classes in Client Programs."
Basic Server Programming
Server run-time objects Server implementation definition SOM Object Adapter (SOMOA) Server object Server activation Initializing a server program Initializing the DSOM run-time environment Initializing the server's ImplementationDef Initializing the SOM Object Adapter When initialization fails Processing requests Exiting a server program Managing objects in the server Object references, ReferenceData, and the ReferenceData table Simple SOM object references SOMDServer (default server-object class) Creation and destruction of SOM objects Mapping objects to object references Hints on the use of create vs. create_constant Mapping object references to objects Dispatching a method Example: Writing a persistent object server Identifying the source of a request Compiling and linking servers
Implementing Classes
Using SOM class libraries Role of DSOM generic server program Role of SOM Object Adapter Role of SOMDServer Implementation constraints Using other object implementations Wrapping a printer API Parameter memory management Building and registering class libraries
Configuring DSOM Applications
Preparing the environment Registering class interfaces Registering servers and classes The 'regimpl', 'pregimpl', 'wregimpl' registration utilities Registration steps Using 'regimpl' Command line interface to 'regimpl' Registration steps using 'pregimpl' or 'wregimpl' Programmatic interface to the Implementation Repository The 'dsom' server manager utility Interpretation of 'dsom' messages Verifying the DSOM environment with 'somdchk' Freeing interprocess communication resources on AIX Using 'somdclean' Using 'cleanipc'
Running DSOM Applications
Running the DSOM daemon (somdd) Running DSOM servers
DSOM as a CORBA-compliant Object Request Broker
Mapping OMG CORBA terminology onto DSOM Object Request Broker run-time interfaces Object references and proxy objects Creation of remote objects Interface definition language C language mapping Dynamic Invocation Interface (DII) Implementations and servers Object Adapters Extensions and limitations
Advanced Topics
Peer vs. client/server processes Multi-threaded DSOM programs Event-driven DSOM programs using EMan Sample server using EMan Dynamic Invocation Interface The NamedValue structure The NVList class Creating argument lists Building a Request Initiating a Request Example code Creating user-supplied proxies Customizing the default base proxy class Sockets class
Error Reporting and Troubleshooting
Error codes Troubleshooting hints Checking the DSOM setup Analyzing problem conditions