SOM Language Neutrality: An OO COBOL Perspective

by Robert A. Pittman, Jr.

Information: (pittcode.txt)

''This article describes an object-oriented COBOL program using SOM-enabled classes written in C++. It illustrates the language neutrality of SOMobjects and introduces many of COBOL's new object-oriented language extensions.''

The introduction of object-oriented (OO) language extensions to the syntax of the COBOL programming language is an exciting development in object-oriented programming and design. With object-oriented COBOL, enterprises can adopt a phased approach to object technology and migrate to this new technology in a staged, orderly fashion.

Programmers with a sound foundation in COBOL can write object-oriented programs with minimal training. Indeed, the greatest obstacle programmers face is not learning new verbs and data types, but acquiring an OO mindset.

In the new IBM family of COBOL products, syntax is compatible across workstation, midrange, and host platforms. SOMobjects are also portable across these platforms, meaning that you can develop a COBOL program using SOM on a workstation and port it unchanged to a host or midrange environment.

SOM also adds the benefit of language independence; a SOM-enabled class can be developed in SOM's IDL (interface definition language), C++, Smalltalk, or COBOL. And, SOM-enabled classes can be used by programs coded in different languages. This assertion provides the basis for the program in this article.

OO COBOL Background
The language extensions introduced in IBM's family of COBOL products are based upon the proposed ANSI 1997 standards. Although a number of issues regarding these proposed standards (such as object persistence and exception) have yet to be resolved, there is a consensus about the proposed OO language extensions. This consensus is the basis of the new genre of object-oriented COBOL compilers introduced by IBM and others. As more of the proposed standards achieve consensus, compiler vendors will incorporate these standards into their products.

SOM and the IBM COBOL Family
IBM supports the Object Management Group's Common Object Request Broker Architecture (CORBA) for object interoperability. Backing up that support is IBM's SOM, an implementation of CORBA making objects portable across development languages. Both the OS/2 and MVS COBOL compilers feature Direct-to-SOM technology, so you can create language-neutral objects without extensive object-oriented programming experience.

By utilizing a compiler option, you can generate SOM IDL directly from a class definition coded in COBOL. You can then compile this IDL using the SOM compiler, which places it into a SOM interface repository (IR). This process is easy and requires no knowledge of IDL coding.

Development Environment
The COBOL code in this article was written using VisualAge for COBOL for OS/2 with the latest Corrective Service Diskette (CSD) installed on a system running OS/2 Warp. VisualAge for COBOL for OS/2 includes excellent visual programming tools, an interactive debugger, and advanced data utility functions. It also uses the WorkFrame program management tool.

Relevant compiler options used were TYPECHK, which performs typechecking of parameters against method interfaces, and PGMNAME(MIXED) which allows the use of mixed-case program, class, and method names.

The development machine was a personal computer with a 90 MHz Intel Pentium processor and 32 MB of RAM. For a full installation, VisualAge for COBOL for OS/2 requires 170 MB of disk space, a 486-class processor, and 24 MB of RAM.

Class Overview
The sample program in this article uses the valet parking application (see Rick Weaver's "IBM System Object Model—The Wave of the Future (and Now!)" article earlier in this issue). This application consists of Valet, Ticket, and Car classes. Other classes (Garage and TktBookS) are used "under the covers" by the aforementioned classes.


 * Note: This article's figures show just the specific lines of code being discussed. Here is the complete program.

Application Summary
As described in Weaver's article, this application reads an input file that instructs the program to either retrieve a car from the garage or park a car in it.

To park a car, you instantiate a car object and set its attributes to those specified on the input record. Next, pass the car object to the parkCar method of the valet object, which returns a ticket object. You can query this ticket object to determine in which slot the car was parked, then display an appropriate message to the system user.

To retrieve a car, you create a ticket object and set its slot number attribute to the value on the input record. Next, invoke the retrieveCar method of the valet object, passing it the ticket object just created. The retrieveCar method returns a car object (or an invalid object if there is no car in the slot). Then obtain the attributes of the returned car object and write a message to the system user.

Implementing in OO COBOL
Readers familiar with traditional COBOL programming should notice some differences in the code presented in this article. These differences arise due to the proposed ANSI 1997 standards on object-oriented extensions to the COBOL language.

One of the first things you will code in this exercise is the configuration section repository. The repository, shown in Figure 1, identifies the classes that the program will need from the interface repository. The client program includes the Car, Ticket, and Valet classes. The subject of a class statement indicates the name of the class as it will be referenced within the program. For example, the Car class in the repository will be referenced as Car in the program. REPOSITORY. CLASS Car   IS "Car" CLASS Ticket IS "Ticket" CLASS Valet IS "Valet". '''Figure 1. Configuration Section Repository'''

In the program's working-storage section, object references (a data type introduced in the proposed ANSI 1997 standard) specify the objects' handles and the classes they reference. Essentially, object references are pointers to the area of memory that has been allocated to an object.

Figure 2 lists the object handles defined in the working-storage section. Notice how they relate to the classes defined in the repository. 01 carObj    USAGE OBJECT REFERENCE Car. 01 ticketObj USAGE OBJECT REFERENCE Ticket. 01 valetObj  USAGE OBJECT REFERENCE Valet. '''Figure 2. Object Handles'''
 * Define the object handles.  *
 * Define the object handles.  *

Notice in Figure 3 that pointers are coded for each text attribute within the objects. These are needed to satisfy typechecking at compilation, when the parameters passed to methods are verified to be compatible with the methods' interfaces.

The valet, ticket, and car objects' attributes are strings. To set one of these attributes, the attribute's value is modified in working-storage and the appropriate pointer's values are set to the attribute's address. The pointer is then passed as a parameter to the _set method. When retrieving an attribute value, the _get method returns a pointer, the handling of which will be discussed later.

Figure 3 represents a definition of a string attribute in the working-storage section, with the associated pointer for the string. COMP-5 denotes a binary data item, in this case a halfword in length, and indicates that the native binary format of the environment platform is to represent internal data. Alternately, COMP means that the little-endian format is applied in the PC architecture, while the big-endian format is used on the AIX and MVS platforms. 01 WS-VN-POINTER  USAGE POINTER. 01 WS-VALET-NAME. 05   WS-VN-LL       PIC S9(4)   COMP-5. 05   WS-VN-NAME     PIC X(40). '''Figure 3. Work Areas for Valet Object's Attributes'''
 * Work areas for the valet object's attributes.  *
 * Work areas for the valet object's attributes.  *

In the procedure division in Figure 4, a call is made to somGetGlobalEnvironment, which returns a pointer to SOM's global environment variable. This pointer must be passed to the methods of the objects used, except when instantiating or destroying an object. 01 WS-ev               USAGE POINTER. . . .	CALL "somGetGlobalEnvironment"     RETURNING WS-ev. '''Figure 4. SOM Global Environment Variable'''
 * SOM global environment variable.                 *
 * SOM global environment variable.                 *
 * Obtain pointer to SOM's global environment variable. *
 * Obtain pointer to SOM's global environment variable. *

To create an object, invoke the somNew method (a new verb - introduced in the 1997 ANSI standard - which is inherited from SOMobject) on the class name of the object specified in the repository. This method returns the object handle and allocates memory for it.

In Figure 5, note the use of somNew on the Valet class, which returns a valet object. Refer to the repository (Figure 1) and the object reference (Figure 2) in the working-storage section, and observe their relationships. Subsequently, when you invoke the object, you invoke methods on its handle and not its class name. INVOKE Valet   "somNew"    RETURNING valetObj. '''Figure 5. Creating an Object'''
 * Create a valet object.                           *
 * Create a valet object.                           *

Notice in Figure 6 that you pass the pointer to the global environment variable as the first parameter when invoking methods on the objects. This parameter, as well as all others used for this exercise, is passed "by value" rather than "by reference" (the default). By value eliminates a level of indirection and is expected by the classes you are using, whereas by reference passes a pointer to the parameter, which is typically used in COBOL programming.

For compatibility, string attributes are null-terminated by moving a low-value (binary zero, X"00") to the position immediately following the last character. Figure 6 sets the valet object's name attribute. The prefix Z before the literal indicates that it will be null- terminated. Figure 6 also sets up a pointer to the attribute's address, to be passed to the method. The object's handle is invoked, rather than the class name, which was invoked during object creation. MOVE Z"Fritz von Hochsmeier" TO WS-VN-NAME.
 * Initialize fields and work areas. Valet name is      *
 * padded with low values for C++'s null character for  *
 * string termination.	                                 *
 * string termination.	                                 *

SET WS-VN-POINTER  TO ADDRESS OF WS-VN-NAME. INVOKE valetObj "_set_valetName" USING BY VALUE		WS-ev WS-VN-POINTER. '''Figure 6. Valet Object's Name Attribute'''
 * Set the valet object's name attribute, pass a    *
 * pointer to the name work area.	                 *
 * pointer to the name work area.	                 *

Because the _get methods return pointers to the attributes they are getting, and because COBOL does not not allow altering the address of an item defined in working-storage, I coded a variable in the linkage section that is used as a work area. The address of this variable is set to the pointer value returned by the _get method. The contents are then copied to the attribute defined in working-storage, and the length up to the null-termination character is determined. For simplicity's sake, I arbitrarily assigned a length of 100 bytes to the variable, allowing me to use it for all string attributes referenced in the program.

It may seem odd to set up a linkage section in a program that is executed directly and never called, but it is necessary to obtain the values of the objects' string attributes. Figure 7 gives the working-storage definition of the car object's color attribute. Figure 8 shows the generic work field to be used to obtain the value of a string attribute. Figure 9 determines the car object's color attribute. Notice how a pointer is returned. 01 WS-CC-POINTER		USAGE POINTER. 01 WS-CAR-COLOR. 05 WS-CC-LL	    	PIC 9(4)    COMP-5. 05 WS-COLOR	    	PIC X(20). 05 WS-COLOR-R		REDEFINES WS-COLOR. 10 WS-CCR-BYTE     PIC X			OCCURS 20 INDEXED BY WS-CCR-INDEX. '''Figure 7. Car Object's Color Attribute'''  LINKAGE SECTION. 01 LS-WORK-AREA	     PIC X(100).  '''Figure 8. Obtaining the Value of a String Attribute'''
 * Work areas for car object's color attribute.     *
 * Work areas for car object's color attribute.     *
 * Define a general work area.   *
 * Define a general work area.   *

 INVOKE carObj "_get_color" USING BY VALUE   WS-ev RETURNING	 WS-CC-POINTER PERFORM GET-COLOR  '''Figure 9. Obtaining the Car's Color Attribute'''
 * Get the color attribute of the car.      *
 * Get the color attribute of the car.      *

The code in Figure 10 uses the returned pointer to address the variable defined in the program's linkage section and copies the attribute to the working-storage variable. The length of the attribute up to its null-termination character is also found.  GET-COLOR. SET ADDRESS OF LS-WORK-AREA TO WS-CC-POINTER. MOVE LS-WORK-AREA	       TO WS-COLOR. PERFORM VARYING WS-CCR-INDEX FROM 1 BY 1 UNTIL WS-CCR-BYTE (WS-CCR-INDEX) < SPACE OR WS-CCR-INDEX > 20 SET WS-CC-LL TO WS-CCR-INDEX END-PERFORM.  '''Figure 10. Getting the Color Work Area'''
 * Gets the color work area from the value pointed to by  *
 * the pointer returned by the car object's get color     *
 * method.	                                          *
 * method.	                                          *

General agreement on exception handling has not yet been reached in the proposed ANSI 1997 standards, so the C++ techniques of "throwing" and "catching" exceptions can not be emulated. To verify that a valid car object was returned by the valet object's retrieveCar method, you call somIsObj, passing the handle of the car object. somIsObj returns a flag indicating whether the object is valid or invalid.

In Figure 11, somIsObj is called to see if an object returned by another method is valid. ValidFlag is subsequently tested to determine the outcome of the call to somIsObj.  CALL "somIsObj" 	USING BY VALUE carObj RETURNING theValidflag  '''Figure 11. Determining Whether a Car was Returned'''
 * See if a car was returned.      *
 * See if a car was returned.      *

To destroy an object, use somFree, a method inherited from SOMobject, as shown in Figure 12. It frees the memory allocated to the object, because (as in C++) there is no automatic garbage collection in object-oriented COBOL. somFree is invoked on the object's handle, rather than the class name Valet.  INVOKE valetObj "somFree".  '''Figure 12. Destroying an Object'''
 * The valet clocks out and closes the garage.      *
 * The valet clocks out and closes the garage.      *

Developing the Client Program
To develop the client program with VisualAge for COBOL for OS/2, first create .LIB files for the DLLs supplied for the classes used by executing the following command from an OS/2 command line: IMPLIB valet.lib valet.dll Next, open up the VisualAge COBOL Icon View and select the "Create New Project" icon (see Figure 13).



'''Figure 13. VisualAge COBOL - Icon View'''

From the "Create New Project" window, select "Create a default COBOL project" with a project name of valet and a target name of client.exe. Next, select the "Create New Text File with the COBOL Editor" button on the toolbar and enter the source code for the client program.

Then copy the VALET.IR file, all the .DLL and .LIB files, and the test file CAR.SCR into the valet project's subdirectory. You need the .LIB files to build the project and the .DLLs and test file for execution. You also need the VALET.IR file to satisfy class typechecking during compilation.

Next, open the "Tools Setup" option from the toolbar (Figure 14). From here, select "COBOL Compiler" from the Actions list (Figure 15), which presents a notebook of compiler options (Figure 16).



'''Figure 14. Valet - Icon View'''



'''Figure 15. Valet - Tools Setup'''



'''Figure 16. Compiler Options'''

'''In this notebook, select the "Link" tab and enter the names of the TICKET.LIB, VALET.LIB, and CAR.LIB files in the library/object file name(s) entry box. You also need the SOMTK.LIB file; this file is supplied with VisualAge for COBOL for OS/2. Then close the compiler options notebook.'''

In the "Tools Setup" menu, select the variables pull-down menu (Figure 17) and add a CARS variable with an associated string of car.scr, the name of the file containing your test data.



'''Figure 17. Valet - Tools Setup'''

'''After closing "Tools Setup," you build the executable file by selecting one of the "Build" options on the toolbar. When the project is built successfully, execute it by selecting the "Run Target" option on the toolbar (Figure 18).'''



'''Figure 18. Valet - Icon View'''

'''If you want, you can package the project's executables and requisite runtime files by selecting the "package" option in the "Project" pull-down menu. This process creates an installable copy of the project that you can move to a target machine.'''

An Exciting New Dimension for COBOL
The code presented in this article (part of a complete OO COBOL client program (pittcode.txt) illustrates some of the techniques used in developing a COBOL client program with VisualAge for COBOL for OS/2. In this example, the client program used SOM-enabled classes. I used only minimal VisualAge COBOL functionality. Coding a class definition in object-oriented COBOL is another exercise beyond the scope of the topic at hand.

When comparing this COBOL client program to a functionally identical C++ client program, note that COBOL has maintained its verbosity. A comparable C++ client program was developed for Weaver's article, "IBM System Object Model—The Wave of the Future (and Now!)." Although the COBOL source code is verbose, the executable file is not. When dynamically linking the referenced classes as DLLs, the executable file was around 17 KB, with no testing logic. This size is certainly reasonable for the functionality provided.

The introduction of object-oriented extensions to the COBOL language adds an exciting dimension to the world's most widely used programming language. These extensions ensure the survival of its widespread usage, paving its way into the new generation of application development.