An Introduction to DSOM

From EDM2
Jump to: navigation, search

Written by Gordon Zeglinski

An Introduction to DSOM

Well I'm back. Last month was a fun-filled adventure of re-installing OS/2, a lot of source code, and all my most used utilities. In fact, I still haven't fully recovered from the system crash. It was the worst HPFS crash I have ever seen (in fact, it was the ONLY HPFS crash I've seen). Somehow the dirty flag wasn't set, but the partition was toast.

Amazingly the good old CHKDSK /F:3 bit from a floppy boot managed to recover all of the important source code, but the partition's EAs were all fried. On the bright side, my main partition isn't fragmented anymore! To make matters worse, I've discovered that the shutdown crashes in INTERcomm can be attributed to bugs in C-Set++ and OS/2. So I had to spend a fair bit of time working around bugs. Yeesh! Now that the system is functional, it's back to SOM.

DSOM allows SOM objects to be created across processes including on different machines. The ability to create objects across a network is particularly interesting to myself as it has the possibility to revolutionize the way we implement network protocols. If you have ever written a TELNET, POP, SMTP or FTP client/server, you know most of the work involves processing and replying to commands. In DSOM, all this work could be replaced by procedure calls. Everything from mail to remote logons could be completely handled by distributed objects. Of course for this to become a reality, there has to be a common distributed OOP protocol. CORBA is attempting to be this standard. Only time will tell how successful CORBA will be.


There's two flavors of DSOM: Workstation DSOM and Workgroup DSOM. As their names imply, the first one is for single machine inter-process based objects and the second is for multi-machine network based objects. The "type" of DSOM you use is for the most part irrelevant to the way you define and use your objects.

DSOM differs from SOM in several manners. First, all DSOM objects have to be "defined" in the interface repository files. Next, all objects must be registered so that the appropriate "SOM server" and DLL can be started by the DSOM daemon. Given the previous difference, it follows that the SOMDD daemon must be running before you can use DSOM objects.

The client/server concept is used in DSOM. The client is the program that requests an object be made, and the server is the program that creates the object. When a client requests an object be instantiated, the request is sent off to the appropriate SOM daemon. The daemon the checks to see if the appropriate SOM server is running. If it isn't, the daemon will start the appropriate server. The interface repository provides the runtime information needed by the daemon, client, and server.

The client doesn't directly interface to the remote instance. Even in the case of the client and server running on the same machine, the two processes can't access each others data. Thus, the concept of a proxy is used. A proxy is the object the client interfaces to. For each remote instance created, a proxy instance is created in the client. By default, the proxy takes the requests coming from the client and sends it along to the server. One interesting feature of DSOM, is that you can define your own proxy. This feature can be used so that the proxy only sends a request to the server once. After getting the information from the server, the proxy can store it locally and use the local copy for subsequent method calls.

The server in its simplest form is nothing more than a loop that continuously calls a few DSOM procedures. It's very similar in concept and programming to the message loop of a PM program. DSOM provides a simple server executable that will load in the objects DLLs and process incoming requests. Thus, unless you needed some very specific type of behavior, you don't have to worry about the server code at all. If you needed a peer to peer interface, then both applications would need to run the server loop. In this case, each application would dedicate 1 thread to running the DSOM server loop. Other threads in each application would be used to send requests to each other.

A Sample DSOM Application

Let's look at a simple DSOM application. This application will be our first attempt at creating an OOP based TELNET. Of course any of the code that actually sends input to the OS will not be written because it's far too complex and has nothing to do with DSOM. We start by defining the IDL for the terminal object.

interface SomTerm : SOMObject{
	attribute char ForeGroundColor;
	attribute char BackGroundColor;

	void MoveCursor(in long XPos, in long YPos);
	void DisplayString(in string OutText);
	string GetLine();
	char GetCH();

   #ifdef __SOMIDL__
	implementation {
	   short CursorX;
	   short CursorY;

	   releaseorder:	_get_ForeGroundColor,_set_ForeGroundColor,\
				MoveCursor, DisplayString, GetLine, GetCH;

	   callstyle = idl;
	   dllname = "SomTerm.dll";
	  //# client should free result
	   GetLine: caller_owns_result;

	   somDefaultInit: override;

   #endif /* __SOMIDL__ */

The object SomTerm will be our terminal object. It will have the ability to get input from the user and display text to the user. There's no harm in making this a color terminal, so the attributes ForeGroundColor, and BackGroundColor are defined. We have covered various aspects of the IDL definition before, so we'll only cover new concepts here. In DSOM the concept of ownership becomes important. The function GetLine returns a string. Who is responsible for allocating and deallocating the storage need to hold the return string? The answer to this question varies depending upon what we tell DSOM to do. In this case, we use the line "GetLine: caller_owns_result;" to indicate the client program must explicitly free the memory.

Implementing the methods for a DSOM based object is pretty much the same as implementing the methods for a SOM object. The biggest difference, is that we have to be much more aware of memory ownership. The following code snippet is for the method GetLine.

SOM_Scope string	SOMLINK GetLine(SomTerm *somSelf,  Environment *ev){
	 SomTermData *somThis = SomTermGetData(somSelf);

	 /* Return statement to be customized: */
	 //{ string retVal;  return (retVal); }

	static char Buff[256];

   return Buff;

Notice that Buff is a static variable. What will the effects of this be on multiple client programs? We'll find the answer to this question later.

Our client program's code is a tad different than it was in previous columns. Specifically, the SOM initialization steps are different as is the method by which we create instances of objects. The following code snippet is the main routine from TERMTEST.CPP, which is the test client.

int main(int argc, char *argv[]){
	 Environment *ev;
	 SomTerm *TermObj;

	 /* local and DSOM initialization */
	 ev = SOM_CreateLocalEnvironment();

	 //create a remote instance of SomTerm

	 TermObj = (SomTerm *) SOMD_ObjectMgr->somdNewObject(ev, "SomTerm", NULL)
	 if (checkEv(ev)){			//something went wrong
	   SOMD_Uninit(ev);			//free up the DSOM resources
	   SOM_DestroyLocalEnvironment(ev); //free up the environment

	TermObj->DisplayString(ev,"Enter a String");
	char* Line=TermObj->GetLine(ev);
	ORBfree(Line);				   //free up the returned line

	return 0;

Our test client moves the cursor around, displays text, changes the text attributes and gets a line of text from the user.

Note: we must pass the environment pointer to the methods because we used the call style IDL instead of OIDL as we did in previous columns.

Running The Code

After unzipping the distribution files, run NMAKE (or whatever) to register the SomTerm class. Next run SOMDD from the directory in which the distribution files were unzipped. Finally, run TERMTEST from the same directory. Assuming your DSOM environment is setup properly, all should go well.

The first thing one notices is that all of the I/O occurs in the SOM server. This is obviously backwards to what we would need in a "real program". If one runs two (or more) copies of TERMTEST, one will notice that only 1 server process is created. Furthermore, calls to member functions are serialized. This serialization is both good and bad in that the static variable used in GetLine won't cause any side effects, but in some cases being able to run multiple requests simultaneously would be useful. Using a peer-to-peer model will solve some of these problems, and no doubt introduce new ones. But this is a topic for another day.

Wrapping Things Up

Well that's it for another fun-filled look at SOM. We have seen how to use DSOM to implement a multi-process DSOM application. This application illustrated that several default behaviors hinder the development of a SOM based TELNET replacement. To continue development of this idea, several goals have been described. Future articles will deal with attempts at meeting these goals, namely switching to a peer to peer model and working around the serialization of method calls.