Object-Oriented Programming Using SOM and DSOM:Distributing Your Objects:A Distributed Calendar

From EDM2
Jump to: navigation, search

A Distributed Calendar

To illustrate some of the concepts we have discussed in this chapter, we will develop a simple calendar system allowing you to record activities for a day. The following list is a summary of the functions:

  • Allows a user to enter or delete activities
  • Provides a graphical user interface (GUI) for user's actions
  • Supports multiple users that are located across a network
  • Allows sharing of any user's calendar

Since the purpose of this example is to illustrate the DSOM Framework, and not so much of the calendar functions, we will limit our calendar capability to support only hours, omitting minutes. Also, our calendar will support only a week's events. Nevertheless, one should easily be able to extend this example to a more realistic implementation.

What the User Sees

In this example, the client program is called PLANNER. When you invoke PLANNER, you can supply an optional parameter that specifies which calendar you want to work with. If you do not specify the parameter, it will default to my (Chris) calendar. The screen for my calendar is displayed in Figure 5.6.

The title of the screen indicates whose calendar it is. For example, if you type:

> PLANNER Kevin

This will invoke PLANNER with Kevin's calendar, and the title will show "Kevin Weekly Calendar".

SOM-DSOM-Figure 5 6.png

Figure 5.6 Chris weekly calendar

Use the Start and End spin button control to select. the start and end time for an activity, and type a description for the activity in the Description field. The Item menu allows you to Add or Delete an activity from your calendar. When you click on Add, the current activity will be added to the list. To delete an activity from the list, click on the activity to highlight it. Then select Delete from the Item menu and the activity will be deleted.

The Day menu lets you select what day of the week you want to work with. You can select either Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, or Saturday. Initially, it is set to Monday. When you select a different day, the description text on the screen will be updated. If there are any activities for that day, they will be displayed. The Day menu also contains a Quit button that you can select to exit t.he PLANNER program.

Calendar Sharing

Multiple users can work with a particular calendar. For example, I can add activities to my calendar. Someone else can look at my calendar entries as well as make changes to them. Changes are local to each calendar. That is, if I add an activity to my calendar, the activity will only be added to my calendar and not to another person's calendar.

High-Level Design

To design our calendar application, we need to decide what our classes look like. Our classes need to capture information, like the start and end time for an activity, as well as the description of an activity. The classes also need to keep track of the activities for a particular day, and maintain activity lists for an entire week. Finally, the classes need to support multiple calendar users and allow sharing between them.

We also need to design the GUI front end. It is important to recognize that the GUI front end is the client of the classes that we used to model our calendar. The GUI client is not the calendar object. The GUI client will perform tasks such as creating the calendar object and exporting the corresponding proxy, so that the calendar object can be shared between multiple clients (users).

Our calendar is modeled using three classes: a work item class (Workltem), a day class (Day), and a calendar directory class (CalendarDir). The Workltem class is used to encapsulate the start time, the end time, and the description of an activity. The Day class is used to encapsulate the list of activities for a particular day. Individual activities can be added to, or deleted from, a Day object. The CalendarDir class is used to encapsulate a week so we can support up to seven days of activities.

The three classes are packaged into a DLL called calendar.dll. We will use the generic DSOM server program (somdsvr). To support different calendars for different users, we need to register multiple calendar servers. For example, we will have a server with alias "Chris" for my calendar, and another server with alias "Kevin" for Kevin's calendar.

Recall that the generic server program handles requests in a synchronous manner, using a FIFO queue. This ensures that when multiple updates are made to a calendar server, the requests will be queued and processed sequentially.

The GUI client is built using the IBM User Interface Class Library. Although it is not necessary to understand GUI in order to understand SOM, we believe future applications will make use of both the SOM Frameworks and the User Interface Frameworks. Therefore, it is included here for those who are interested in seeing how the two can be used together.

Implementation Details

This section will provide the details of the design and implementation of each of the components in our calendar application.

The Work.Item Class

The IDL for the Workltem class is given in Figure 5.7. It introduces one new method and overrides two methods.

  • The mkEntry method allocates storage for the strings startTime, endTask, and then makes a copy of the input value. It is necessary to explicitly allocate memory for the strings so that the object will retain the values even after the caller falls out of scope.
  • The somInit method is overridden to initialize the attributes to NULL.
  • The somUninit method is overridden to free any storage that is allocated for the attributes when the object is destroyed.
#ifndef workitem_idl
#define workltem_idl

#include <somobj.idl>
interface Workltem : SOMObject
{
   attribute string startl1me;
   attribute string endlime;
   attribute string task;

   void mkEntry(in string slime. in string el1me, in string taskDesc);
 
  #ifdef _SOMIDL_
  implementation
  {
        releaseorder : _get_startl1me, _set_startl1me,
                       _get_endTime. _set_endTime,
                       _get_task, _set_task,
                       mkEntry;
 
        somInit: override;
        somUninit: override;
        dllname = "calendar.di!";
   };
   #end if
};
#endif

Figure 5.7 The Workltem IDL

The C++ code that implements the methods for the Workltem class is presented in Figure 5.8.

The Day Class

Figure 5.9 gives the IDL for the Day class. The Day class is used to capture the list of todo's for the day. It introduces two new methods and overrides two others.

  • The book method adds a Workltem object to the sequence workList. The sequence workList is a bounded sequence with a maximum of 50 Workltem objects. We will restrict ourself to working a maximum of 50 tasks per day. Should you desire more, you can use an unbounded sequence and modify the implementation to manage storage.
/*
* This file was generated by the SOM Compiler and Emitter Framework.
* Generated using:
*SOM Emitter emitxtm: somc/smmain.c
*/

#define Workltem_Class_Source
#include <Workitem.xih>

SOM_Scope void SOMLINK mkEntry(Workltem *somSelf, Environment *ev,
                               string slime, string elime, string taskDesc)
{
WorkltemData •somThis = WorkltemGetData(somSelf);
Work Item Method Debug("Work Item", "mkEntry");

  if (somThis->startlime) SOMFree(somThis->startTime);
  somThis->startTime = (string)SOMMalloc(strlen(sTime)+ 1 );
  strcpy (somThis->startlime, slime):

  if (somThis-> endlime) SOMFree(somThis->endlime);
  somThis->endTime = (string)SOMMalloc(strlen(eTime)+ 1 );
  strcpy (somThis->endTime, eTime);

  if (somThis->task) SOMFree(somThis->task);
  somThis->task = (string)SOMMalloc(strlen(taskDesc)+ 1 );
  strcpy (somThis->task, taskDesc);
}

SOM_Scope void SOMLINK somlnit(Workltem 'somSelf)
{
  WorkltemData •somThis = WorkltemGetData(somSelf);
  WorkltemMethodDebug("Workltem","somlnif);
 
  Workltem_parent_SOMObject_somlnit(somSelf);
  
  somThis->startTime =NULL;
  somThis->endTime =NULL;
  somThis->task = NULL;
}

SOM_Scope void SOMLINK somUninit(Workltem •somSelf)
{
   WorkltemData •somThis = WorkltemGetData(somSelf),
   WorkltemMethodDebug("Workltem", "somUninir);
 
   if (somThis->startlime) SOMFree(somThis->startTime);
   if (somThis->endlime) SOMFree(somThis->endlime);
   if (somThis->task) SOMFree(somThis->task);
 
   Workltem_parent_ SOMObject_somUninit(somSelf);
}

Figure 5.8 The Workitem class implementation

  • The removeltem method removes a Workltem object from the sequence workLisl. It searches the sequence for a Workltem object that has the same start'IYme, endTime, and task values as the input parameters. If one is found, it will be removed from the sequence.
  • The somlnit method is overridden to initialize the sequence workList. It allocates storage for the sequence and sets the length and maximum field of the sequence.
  • The somUninit method is overridden to free the storage that is allocated for the sequence workList when the object is destroyed.

The C++ code that implements the methods for the Day class is shown in Figure 5.10.

The CalendarDir Class

The IDL for the CalendarDir class is given in Figure 5.11. The CalendarDir class maintains a directory for a week of activities. It introduces two new methods and overrides two methods.

  • The add.Day method adds a Day object to the sequence weekList. The sequence

weekList is a bounded sequence with a total of seven elements, one for each day of the week.

  • The getDay method returns a Day object for the requested day.
  • The somlnit method is overridden to initialize the sequence weekList. It allocates

storage for the sequence, and sets the length and maximum field of the sequence.

  • The somUninit method is overridden to free the storage that is allocated for

the sequence weekList when the object is destroyed.

The C++ code that implements the methods for the CalendarDir class is given in Figure 5.12.

#ifndef day_idl
#define day_ldl

#include <somobj.idl>

interface Workltem;
interface Day : SOMObject
{
   const unsigned long MAXITEM =SO;  //today's date
   attribute long date;              //list of todo's
   attribute sequence<Workltem,MAXITEM> worklist;

  short book(in Workltem entry);
  short removeltem(in string start, in string end, in string desc):

#ifdef _ SOMIDL_
implementation
{
  releaseorder : _get_date, _set_date,
                 _get_worklist, _set_worklist,
                 book, removeltem;

  somInit: override;
  somUninit: override;
  dllname = "calendar.di!":
 };
 #endif
} ;
#endif

Figure 5.9 The Day IDL

The GUI Client The PLANNER program is the client that uses the above classes in a distributed fashion. The source files that make up the PLANNER program are planwin.hpp andplanwin.cpp. The listing for planwin.hpp is given in Figure 5.13.

The class DayPlannerWindow is derived from lhe IFrameWindow, ICommandHandler, and ISelectHandler classes from the IBM User Interface Class Library. The DayPlannerWindow class handles the creation of the main window and the processing of user events. It also handles the creation and management of calendar objects. A summary of what each member function does is given below:


/*
* This file was generated by the SOM Compiler and Emitter Framework.
* Generated using:
* SOM Emitter emitxtm: somc/smmafn.c
*/

#define Day_Class_Source
#Include <day.xih>
#include <workitem.xh>
#include <Slring.h>

SOM_Scope short SOMLINK book(Day *somSelf, Environment *ev,
                              Workltem* entry)
{
     DayData *somThis = DayGetData(somSelf);
     short rc;
     DayMethodDebug("Day", "book");
 
     if (sequencelength (som This->worklist) < sequenceMaximum( som This->worklist))
     {
      sequenceElement(somThis->workllst, sequencelength(somThis->worklist)) =entry;
sequencelength(somThis->workllst)++;
      rc= O;
     }
     else
     {
      rc = -1 ;
     }
     return rc;
}

SOM_Scope short SOMLINK removeltem(Day *somSelf, Environment *ev,
                                   string start, string end, string desc)
{
         DayData *somThis = DayGetData(somSelf);
         short i;
         Workltem *item;
         DayMethodDebug("Day","removeItem");
 
         for (i=O; i<sequenceLength(somThis->worklist); i++)
         {
          item = sequenceElement(somThls->workList,i) ;

          if ( (strcmp(start, item-> get_startTime(ev)) == 0) &&
          (strcmp(end, item-> _get_endTime(ev)) = 0) &&
          (strcmp(desc, item->_get_task(ev)) == 0))
{
          // Found item. Delete it from the list.
          sequencelength{som This->worklist)-;
          for (i; I< sequenceLength(somThis->worklist); i++)
          {
             sequenceElement(somThls->worklist,i) = sequenceEfement(somThis->worklist, i+ 1 );
          }
          return 0; 
     }
}
return -1;    // item not found
}

SOM_ Scope void SOMLINK somlnit(Day •somSelf)
{
     DayData ·somThis = DayGetData(somSelf);
     DayMethodDebug("Day", "somlnir);
 
     Day_parent_SOMObject_somlnit(somSelf);
 
     sequenceMaximum(somThis->workList) = MAXITEM;
     sequencel ength(somThis->Worklist) = 0;
     somThis->worklist_buffer =(WorkItem**) SOMMalloc(sizeof(Workltem *) * MAXITEM); 
}


SOM_Scope void SOMLINK somUninit(Day *somSelf)
{
     DayData *somThis = DayGetData(somSelf);
     DayMethodDebug("Day", "somUninit");
 
     if (somThls->worklist._buffer)
       SOMFree(somThis->worklist._buffer);
 
     Day_parent_SOMObject_somUninit(somSelf);
}

#ifndef caldir_idl
#define caldir_idl

#include <somobj.idl>

interface Day;
interface CalendarDir : SOMObject
{
  const unsigned long MAXDAY = 7;
  attribute sequence<Day, MAXDAY> weekUst;
 
  long addDay(in short daynum, in Day entry);
  Day getDay(in short daynum);

  #ifdef _SOMIDL_
  implementation
  {
     releaseorder : _get_weeklist, _set_weeklist,
                    addDay, getDay;
 
     somInit: override;
     somUninit: override;
     dllname = "calendar.dll";
   };
   #end if
};
#end if

Figure 5.10 The Day class implementation

  • The constructor constructs the frame window. It initializes the title bar using the name of the calendar server and creates the menu bar. It calls the setupClient function to set up the client window. It then calls the setupData function to set up the DSOM environment. Finally, it calculates the size of the main window, sets focus to the main window, and displays it.
  • The command function processes commands from the menu bar. The case MI_ADD handles the Add request. It adds the values from the input fields to the list box and calls the book function to add the item to the current activity list. The case MI_DEL handles the Delete request. It deletes the selected item from the list box as well as from the current activity list.
/*
* This file was generated by the SOM Compiler and Emitter Framework.
* Generated using:
* SOM Emitter emitxtm: somc/smmain.e
* /

#define CalendarDir_Class_Source
#include <caldir.xih>

SOM_ Scope long SOMLINK addDay(CalendarDir ·somSelf, Environment ·ev,
                               short daynum, Day* entry)
{
  CalendarDirData *somThis = CalendarDirGetData(somSelf);
  long rc;
  CalendarDirMethodDebug(•CalendarDir","addDay");
 
  if (sequeneelength(somThis->weeklist) < sequeneeMaximum(somThis->weeklist))
  {
    sequeneeElement(somThis->weeklist, sequencelength(somThis->weeklist)) =entry;
    sequeneelength(somThis->weeklist)++;
    rc= 0L;
  }
else
 {
  rc = -1L;
 }
return rc;
}
SOM_Scope Day* SOMLINK getDay(CalendarDir *somSelf, Environment *ev, short daynum)
{
   CalendarDirData *somThis = CalendarDirGetData(somSelf);
   CalendarDirMethodDebug ("CalendarDir", "getDay");

   return ( sequeneeElement(somThis->weeklist, daynum) );
}

SOM_Scope void SOMLINK somlnit(CalendarDir ·somSelf
{

  CalendarDirData •somThis = CalendarDlrGetData(somSelf);
  CalendarDirMethodDebug("CalendarDir", "somlnir);
 
CalendarDir _parent_SOMObject_somlnit(somSelf);

sequenceMaximum(somThis->weekList) = MAXDAY;
sequencelength(somThis->weekList) = 0;
somThis->weekList._buffer = (Day**) SOMMalloc(sizeof (Day*)* MAXDAY);
}

SOM_Scope void SOMLINK somUninit(CalendarDir ·somSelf)
{
  CalendarOrrData ·somThis = CalendarDirGetData(somSelf);
  CalendarDi rMethod Debug("CalendarDi r'', "som U ninit") ;

  if (somThis->weekList._buffer)
       SOMFree(som This->weekList. _buffer);
 
  CalendarDir _parent_SOMObject_somUninit(somSelf);
}

Figure 5.12 The CalendarDir class implementation

The cases MI_ SUN, MI_MON, etc., handle the selection of the day of the week. For instance, if Sunday is selected, ihe static field on the screen is updated to reflect the current day, and the showltems function is called to display the list of activities for Sunday.

The case MI_QUIT closes the application.

  • The selected function handles the selected command from the list box. It sets the list box cursor to the selected item.
  • The setupClient function creates a multi-cell canvas control as the client window.

It also creates the spin-button controls, the static text controls, the entry field control, and the list box control, placing them on the multi-cell canvas.

  • The setupData function initializes the DSOM environment and locates the specified calendar server. It then calls the findProxy function to determine if there is already an existing object reference to CalendarDir object that is managed by this server. If an object reference already exists, it will be used.

Otherwise, a new CalendarDir object is created, and its reference is converted into a string ID. The string ID is written to a file to allow the sharing of the CalendarDir object.

A simple one-to-one mapping convention is used to identify the object reference for a calendar server. For example, if "PLANNER Kevin" is invoked for the first time, a new CalendarDir object will be created for the Kevin calendar server. The reference to this object is converted to a string ID and stored in the file "Kevin". Later, when "PLANNER Kevin" is invoked again (say, by another user), the findProxy function will retrieve the string ID from the file "Kevin" and convert it back to the object reference so the second user can access Kevin's calendar.

  • The findProxy function checks to see if there is already a string ID for the CalendarDir object that is managed by this server. If there is an existing string ID, it will be restored back into the object reference.
  • The showltems function displays the list of activities for a particular day.
  • The book function creates a new Workltem object and adds it to the activitiy list for the day.
  • The destructor performs cleanup. If this client has created a file to contain the string ID for the CalendarDir object, we assume it is the last remaining client and we call the somd.DestroyObject method to destroy all the local proxies and the remote objects. The file is also erased.

If this client is not the one that created a file, we assume that there are existing clients out there that are still working with the calendar objects.

Therefore, we will not destroy the remote objects. Instead we call the somdReleaseObject method to release the local proxies.

  #ifndef PLANWIN_HPP
  #define PLANWIN_HPP
  
  #include <iframe.hpp>
  #include <icmdhdr.hpp>
  #include <imcelcv.hpp>
  #include <ilistbox.hpp>
  #include <iselhdr.hpp>
  #include <istrlng.hpp>
  #include <istattxt.hpp>
  #include <imenubar.hpp>
  #include <ispinbt.hpp>
  #include <ientryfd.hpp>

  #include <somd.xh>
  #include "week.h"
  #include "workltem.xh"
  #include "day.xh"
  #include "caldir.xh"

  {
     public:
     DayPlannerWindow (unsigned long windowld, char *name);
     - DayPlannerWindow();
 
     protected:
      virtual Boolean command(ICommandEvent& cmdevt);
      virtual Boolean selected(IControlEvent& evt);
 
     private:
      setupClient();
      setupData();
      short findProxy();
      showllems(short day);
      book(char •start, char ·end, char *desc);

  IEntryField *descT;
  IStaticText *weekday, *start, *end, •desc;
  ISpinButton *startT, *endT;
 
  IMultiCellCanvas *mc;
  IMenuBar *menuBar;
  short menuFlag;
  short createFile;

  IListBox *listBox;
  IListBox::Cursor *cursor;
 
  // DSOM related variables
  SOMDServer ·server:
  char *serverName;
  Environment *ev;
  CalendarDir *curDirEntry;
  Day *curDay;

};
#endif

Figure 5.13 The DayPlannerWindow header file

The listing for the source file planwin.cpp is given in Figure 5.14.

#include <ifont.hpp>
#include <ititle.hpp>
#include <iostream.h>       
#include <fstream.h>        
#include <imsgbox.hpp>     

#include <somd.xh>
#include "planwin.hpp"

void main(int argc, char *argv[], char *envp[])
{
  IString filename;
  if (argc == 2)
  {
     filename = argv[1];
  }
  else
  {
     filename = "Chris";   // default to my calendar
  }
  DayPlannerWindow mainWindow(WND_MAIN, filename);
  IApplication::current().run(); 
}

DayPlannerWindow :: DayPlannerWindow(unsigned long windowId, 
                                     char *name)
  : IFrameWindow (IFrameWindow::defaultStyle(), windowId)
  , serverName(name)
  , createFile(0)
{
   IString title(" Weekly Calendar");

   ICommandHandler::handleEventsFor(this);

   title = serverName + title;
   ITitle(this, title);

   menuBar = new IMenuBar(WND_MAIN,this);
   menuBar->setAutoDeleteObject();  // automatic delete when window is closed
   menuBar->checkItem(MI_BASE);
   menuFlag = MI_BASE;

   setupClient();
   setupData();

   if ( IWindow::desktopWindow()->size() > ISize(1000,700) )
   {
      sizeTo(ISize(460,330));
   }
   else  // VGA
   {
      sizeTo(ISize(360,240));
   } 

   setFocus();
   show();
}

DayPlannerWindow :: setupClient()
{
  mc = new IMultiCellCanvas(WND_CANVAS, this, this);
  mc->setAutoDeleteObject();
  setClient(mc);

  weekday = new IStaticText(WND_TEXT, mc, mc);
  weekday->setAutoDeleteObject();
  weekday->setText("Monday");

  start = new IStaticText(WND_TEXT, mc, mc);
  start->setAutoDeleteObject();
  start->setText("Start  ");

  startT = new ISpinButton(WND_START,mc,mc);
  startT->setAutoDeleteObject();
  startT->setInputType(ISpinButton::numeric);
  startT->setRange(IRange(1,24));
  startT->setCurrent(8);
  startT->setLimit(2);
  startT->readOnly;

  end = new IStaticText(WND_TEXT, mc, mc);
  end->setAutoDeleteObject();
  end->setText("End    ");

  endT = new ISpinButton(WND_END,mc,mc);
  endT->setAutoDeleteObject();
  endT->setInputType(ISpinButton::numeric);
  endT->setRange(IRange(1,24));
  endT->setCurrent(6);
  endT->setLimit(2);
  endT->readOnly;

  desc = new IStaticText(WND_TEXT, mc, mc);
  desc->setAutoDeleteObject();
  desc->setText("Description");

  descT = new IEntryField(WND_DESC, mc, mc);
  descT->setAutoDeleteObject();

  listBox = new IListBox(WND_LISTBOX,mc,mc,
                         IRectangle(),
                         IListBox::defaultStyle() |
                         IControl::tabStop);
  listBox->setAutoDeleteObject();

  cursor = new IListBox::Cursor(*listBox);
  ISelectHandler::handleEventsFor(listBox);

  mc->addToCell(weekday,2,2);
  mc->addToCell(start,  2,4);
  mc->addToCell(startT, 2,5);
  mc->addToCell(end,    4,4);
  mc->addToCell(endT,   4,5);
  mc->addToCell(desc,   6,4);
  mc->addToCell(descT,  6,5);
  mc->addToCell(listBox,2,7,5,1);

  mc->setRowHeight(3,2,true);
  mc->setRowHeight(6,2,true);
  mc->setRowHeight(8,2,true);
  mc->setColumnWidth(3,2,true);
  mc->setColumnWidth(5,2,true);
  mc->setColumnWidth(7,2,true);
}

DayPlannerWindow :: setupData()
{
  short i, found;
  Day   *day;
  
  string objectId;

  ev = SOM_CreateLocalEnvironment();            
  SOMD_Init(ev);
                                              
  WorkItemNewClass(0,0);
  DayNewClass(0,0);
  CalendarDirNewClass(0,0);

  server = SOMD_ObjectMgr->somdFindServerByName(ev,serverName);   
  found = findProxy();
  
  if (!found)
  {
    //************************************************************
    // Create a new CalendarDir object and add 7 Days to the list
    //************************************************************
    curDirEntry = (CalendarDir *) server->somdCreateObj(ev,
                                              "CalendarDir",
                                              NULL);        

    //************************************************************
    // Convert proxy to a string and save to a file with the same
    // name as the current server Name.
    //************************************************************
    objectId = SOMD_ObjectMgr->somdGetIdFromObject(ev,curDirEntry); 

    ofstream  outfile(serverName);
    outfile << objectId;
    outfile.close();

    createFile = 1;     // remember this client created the file
                                                
    for (i=0; i<7; i++ )
    {
       day = (Day *) server->somdCreateObj(ev, "Day", NULL);
       day->_set_date(ev,i);
       curDirEntry->addDay(ev, i, day);
    }
  }

  // Set current day to Monday and show any existing activities
  curDay = curDirEntry->getDay(ev,1);
  showItems(1);
}

short DayPlannerWindow :: findProxy()
{                                                            
  ifstream infile(serverName);
  string   objectId;
  char     buffer[256];
  short    found = 0;
                                                             
  if (infile)    // proxy exist
  {                                                          
    //***************************************
    // restore proxy from its string form
    //***************************************
    objectId = (string) buffer;                              
    infile >> objectId;          
    curDirEntry = (CalendarDir *) SOMD_ObjectMgr->somdGetObjectFromId(ev, 
                                                 objectId);           
    return 1;
  }
  return 0;
}

DayPlannerWindow :: ~DayPlannerWindow()
{
  short i,j;
  _IDL_SEQUENCE_WorkItem alist;

  if (createFile)     // this client writes proxy to file
  {
     //**************************************
     // perform clean up: delete the file
     //**************************************
     IString buffer("erase ");
     buffer = buffer + serverName;
     system(buffer);

     //**************************************
     // Destroy each Day object
     //**************************************
     for (i=0; i<7; i++ ) 
     {
        curDay = curDirEntry->getDay(ev,i);
        alist = curDay->_get_workList(ev);
        if (sequenceLength(alist) > 0 && sequenceLength(alist) < 50)   
        {
           //*********************************************
           // Destroy each WorkItem from the Day object
           //*********************************************
           for (j=0; j < sequenceLength(alist) ; j++)
           {
              SOMD_ObjectMgr->somdDestroyObject(ev,
                                                sequenceElement(alist,j));
           }
        }
        SOMD_ObjectMgr->somdDestroyObject(ev,curDay);
     } 

     // Destroy CalendarDir object
     SOMD_ObjectMgr->somdDestroyObject(ev, curDirEntry);
  } 
  else
  {
     //**************************************                                     
     // Release the proxy for each Day object
     //**************************************
     for (i=0; i<7; i++ )
     {
        curDay = curDirEntry->getDay(ev,i);
        alist = curDay->_get_workList(ev);
        if (sequenceLength(alist) > 0 && sequenceLength(alist) < 50)
        {
           //**************************************************
           // Release proxy for each WorkItem from Day object
           //*****************************************************
           for (j=0; j < sequenceLength(alist) ; j++)
           {
             SOMD_ObjectMgr->somdReleaseObject(ev,
                                               sequenceElement(alist,j));
           }
        }
        SOMD_ObjectMgr->somdReleaseObject(ev,curDay);
     }

     // Release proxy for CalendarDir object
     SOMD_ObjectMgr->somdReleaseObject(ev,curDirEntry);
  }

  // release server proxy                            
  SOMD_ObjectMgr->somdReleaseObject(ev, server);

  SOMD_Uninit(ev);                   
  SOM_DestroyLocalEnvironment(ev);
}

Boolean DayPlannerWindow :: command(ICommandEvent & cmdEvent)
{                                                                           
  IMessageBox msgbox(this);       
       
  switch (cmdEvent.commandId()) 
  {
    case MI_ADD:
      if ( !(descT->text().size()) ) 
      {
         msgbox.show("Enter a description", IMessageBox::okButton);
      } 
      else
      {
        IString str;                            
        IString pad("0");
        IString trial(":00 ");
        IString blank(" ");
        IString sstr(startT->value());
        IString estr(endT->value());

        if ( startT->value() < 10 )
        {
          sstr = pad + sstr;
        }
        if ( endT->value() < 10 )
        {
          estr = pad + estr;
        }
 
        sstr = sstr + trial;
        estr = estr + trial;

        str = sstr + estr + descT->text();
        listBox->addAscending(str);

        book( sstr, estr, descT->text() );
      }
      return true;
      break;                          
      
    case MI_DEL:
      if ( cursor->isValid() )
      {
        IString item, startTime, endTime, task;

        item = listBox->elementAt(*cursor);

        startTime = item.subString(1,6);
        endTime   = item.subString(7,6);
        task      = item.subString(13);
        curDay->removeItem(ev, startTime, endTime, task);

        listBox->removeAt(*cursor);
      }
      return true;
      break;
     
    case MI_SUN:
      weekday->setText("Sunday");
      showItems(0);
      return true;

    case MI_MON:
      weekday->setText("Monday");
      showItems(1);
      return true;

    case MI_TUE: 
      weekday->setText("Tuesday");
      showItems(2);
      return true;
                   
    case MI_WED:
      weekday->setText("Wednesday");
      showItems(3);
      return true;

    case MI_THU:
      weekday->setText("Thursday");
      showItems(4);
      return true;
                        
    case MI_FRI:
      weekday->setText("Friday");
      showItems(5);
      return true;
                        
    case MI_SAT:
      weekday->setText("Saturday");
      showItems(6);
      return true;

    case MI_QUIT:
      close();
      return true;
      break;
  }
  return false;
}

Boolean DayPlannerWindow :: selected(IControlEvent & evt)
{
  cursor->setToFirst();
  return true;
}

DayPlannerWindow :: showItems(short day)
{
  _IDL_SEQUENCE_WorkItem alist;
  short                  i;
  IString                str;
  long                   date;

  listBox->removeAll();             // clear list
  
  menuBar->uncheckItem(menuFlag);   // uncheck previous day
  menuBar->checkItem(MI_BASE+day);  // check selected day
  menuFlag = MI_BASE + day;

  curDay = curDirEntry->getDay(ev,day);
  alist = curDay->_get_workList(ev);

  if (sequenceLength(alist) > 0 && sequenceLength(alist) < 50) 
  {
    for (i=0; i < sequenceLength(alist) ; i++)
    {
      str = "";
      str = str +
            sequenceElement(alist,i)->_get_startTime(ev) +
            sequenceElement(alist,i)->_get_endTime(ev) +
            sequenceElement(alist,i)->_get_task(ev);

      listBox->addAscending(str);
    }
  }
}

DayPlannerWindow :: book(char *start, char * end, char *task)
{
   WorkItem *item;

   item = (WorkItem *) server->somdCreateObj(ev,
                                             "WorkItem",
                                             NULL);
   item->mkEntry(ev, start, end, task);
   curDay->book(ev, item);
}

Figure 5.14 The DayPlannerWindow implementation

Note that in the member function, showltems and lhe destructor, there is an "if' statement to make sure that the length of the sequence is valid before proceeding. Normally, this is not necessary. However, at the time of writing this book, DSOM does not marshall empty sequences properly. AB a result, the length of the sequence will show some huge number when the sequence is empty. Our example provides a quick patch for the problem. Another way is to add an element to the sequence right after the object is created. This ensures that the sequence will never be empty when marshalling occurs. This defect has been reported and should be fixed in future Corrective Service Distribution.

The Makefile

The source files that contain the classes Workltem, Day, and CalendarDir are workitem.cpp, day.cpp, and caldir.cpp. The source files initfunc.cpp and calendar.def contain the initialization function and the module definition file. They are not listed here, but they are included on the diskette. These five files are needed to build the calendar DLL.

The source files planwin.hpp, planwin.cpp, week.re, and week.h are needed to build the GUI client. The first two files are listed above. The file week.re contains the resources for the application, and the file week.h contains constant definitions. They are included on the diskette.

The makefile for building the calendar application is given in Figure 5.15. It includes dependencies to build the Interface Repository and the Implementation Repository. The three classes are compiled into the Interface Repository file that is designated by the SOMIR environment variable. Two calendar servers, "Chris" and "Kevin", are registered with the Implementation Repository that is designated by the SOMDDIR environment variable.

MAKEFILE

Figure 5.15 The distributed PLANNER makefile

.SUFFIXES:
.SUFFIXES: .idl .xih .xh .cpp .obj .def 
#
# Need /Ge- to build DLL
#
OBJS  = workitem.obj day.obj caldir.obj initfunc.obj
UILIB = dde4muii.lib dde4cci.lib dde4mbsi.lib os2386.lib

all: calendar.dll planner.exe som.ir somdimpl

#
# mysvr.cpp contains a sample server. You can use it
# instead of the generic dsom server
#
tstsvr: mysvr.exe myimpl

.cpp.obj:
        icc /c+ /Ge- -I. $<
.idl.xh:
         sc -sxh  $*.idl
.idl.xih:
         sc -sxih  $*.idl
.idl.cpp:
         sc -sxc $*.idl
.idl.def:
         sc -sdef $*.idl

initfunc.obj: initfunc.cpp

clnimpl:
        -regimpl -D -i Chris
        -regimpl -D -i Kevin

workitem.obj: workitem.xih workitem.xh workitem.cpp
workitem.xih: workitem.idl
workitem.xh:  workitem.idl
workitem.cpp: workitem.xih

day.obj: day.xih day.xh day.cpp       
day.xih: day.idl
day.xh:  day.idl                            
day.cpp: day.xih                            

caldir.obj: caldir.xih caldir.xh caldir.cpp       
caldir.xih: caldir.idl                            
caldir.xh:  caldir.idl                            
caldir.cpp: caldir.xih                            

initfunc.obj: initfunc.cpp

#
# Build the DLL
#
calendar.dll: $(OBJS) calendar.def
        icc @<< 
        /Fe"calendar.dll" $(OBJS) calendar.def somtk.lib
<<
        implib calendar.lib calendar.def

#
# Build the executable
#
planwin.obj:  planwin.cpp planwin.hpp week.h
        icc /c+ /Gd+ /Gm+ /Si+ -I. planwin.cpp
planner.exe: planwin.obj week.res
        icc /Fe"planner.exe" planwin.obj /B" /pm:pm /noi" \
        $(UILIB) somtk.lib calendar.lib planner.def
        rc week.res planner.exe

#
# Build the test server.
#
mysvr.obj: mysvr.cpp
        icc /c+ /Gd+ /Gm+ /Si+ mysvr.cpp
mysvr.exe: mysvr.obj
        icc /Fe"mysvr.exe" mysvr.obj somtk.lib
#
# You can use "mySvr" instead of "Chris" or "Kevin"
#
myimpl: mysvr.cpp
        -regimpl -A -i mySvr -p mysvr.exe
        -regimpl -a -i mySvr -c WorkItem -c Day -c CalendarDir
        -regimpl -L -i mySvr

week.res: week.rc week.h
          rc -r week.rc

#
# Put the IDL descriptions into the Interface Repository
#
som.ir: workitem.idl day.idl caldir.idl
        sc -sir -u workitem.idl
        sc -sir -u day.idl
        sc -sir -u caldir.idl
#
# Build the DSOM Implementation Repository.
# Register two servers: Chris and Kevin
#
somdimpl: workitem.idl day.idl caldir.idl
        -regimpl -A -i Chris
        -regimpl -a -i Chris -c WorkItem -c Day -c CalendarDir
        -regimpl -L -i Chris
#
        -regimpl -A -i Kevin
        -regimpl -a -i Kevin -c WorkItem -c Day -c CalendarDir
        -regimpl -L -i Kevin
        @echo x > somdimpl

Building the Application

To build the application, simply invoke NMAKE using the makefile shown above. Before you invoke NMAKE, make sure the following environment variables are set up properly.

  • SOMIR
  • SOMDDIR
  • HOSTNAME

For example, you can use the following settings.

 set SOMIR=%SOMBASE%\etc\som.ir;som.ir
 set SOMDDIR=%SOMBASE%\etc\dsom
 set HOSTNAME=localhost

The file dsomenu.cmd contains the commands you need to set up your environment for this example.

Running the Application

We are ready to run our distributed calendar application. In the following, we assume we are running our application on a single 08/2 with the SOMobjects Workstation Enabler, or the SOMobjects Developer Toolkit installed. Note that it is possible to distribute the calendar objects to a AIX/6000 system by building a shared library for the AIX/6000, and using Workgroup DSOM to access the calendar objects.

Make sure the environment variables listed above are still set to the correct values in the session you are going to start the DSOM daemon. In addition, make sure the environment variable USERID is also set. For example:

set USERID=chris

Start the DSOM daemon by typing:

> start If somdd

This starts the DSOM daemon in a separate window. Figure 5.16 shows the screen when the DSOM daemon is ready. In the same window you started the DSOM daemon, type:

> planner

This starts the PLANNER program using the default ("Chris") server. Figure 5.17 shows the screen after PLANNER is started. Notice that the icon labelled DSOM server somdsur.exe appears on the desktop when PLANNER is started. This is "Chris" server process.

SOM-DSOM-Fig 5 16.png

Figure 5.16 DSOM daemon

SOM-DSOM-Fig 5 17.png

Figure 5.17 Chris Client and Server Object on First Invocation

The file "Chris" should be created in your current directory. Take a look at it. It should contain an entry similar to the following:

SOMl2l2d149bb6-01791457-7f-00-0100007fOOOOICalendarDir14lb0984300

This entry is the string representation for the object reference we created for the object CalendarDir. The object reference is externalized to allow sharing. Add some activities to Chris's calendar. For example, a department meeting is booked for Monday, from 10:00 to 11:00. A tennis game is booked for Tuesday, from 18:00 to 19:00.

In a separate -.v:indow, run dsomenv.cmd to set up the environment variables. Then type "planner" to start a second PLANNER program using the "Chris" server. Notice how all of the activities that were added in the first PLANNER program appear in the second PLANNER program. This is because the two programs share the same calendar objects through the externalization of the proxy. If you add or delete activities from either calendar, they will be reflected on the other calendar when you refresh the list. Figure 5.18 shows the screen capture for the two "Chris Weekly Calendar" windows.

SOM-DSOM-Fig 5 18.png

Figure 5.18 Sharing Chris calendar

In a separate window, run dsomenu.cmd to set up the environment variables. Then type:

 > planner Kevin

to start a PLANNER program using the Kevin calendar server. Figure 5.19 shows the screen after "PLANNER Kevin" is started. Notice that a second icon labelled DSOM server somdsur.exe appears on the desktop when "PLANNER Kevin" is started. This is "Kevin" server process.

The activities that were added to the "Chris" calendar were not shown on the "Kevin" calendar because they do not share objects. The file "Kevin" should be created in the current directory to store the proxy to Kevin's CalendarDir object.

Now terminate the client programs by selecting Quit from the menu to exit. Notice that the two DSOM servers are still around. This is because the somdDestroyObject method only destroys the object that is managed by the server. It does not terminate the server. ff you terminate the DSOM daemon, then the servers will be terminated.

SOM-DSOM-Fig 5 19.png

Figure 5.19 Kevin's calendar

If you want the termination of a server to be controlled by a client, then you will need to implement your own server. You can create a new subclass of SOMDServer and implement a shutdown method in this subclass. The shutdown method should perform the necessary cleanup and then terminate. The client can invoke the shutdown method using the server proxy it obtains from calling the somd.FindServerByName method.

Summary

The Calendar example illustrates some of the key concepts of developing and building a DSOM application. The task of building a distributed application has been greatly simplified with the availability of the Distributed SOM Framework. How many lines of code does a traditional program need to write to provide a similar form of interprocess or intermachine communications?

Notice that our calendars are not persistent. When the server process terminates, the states of the calendar objects are not preserved. In Chapter 6, we will add persistence to our calendar so that our calendar data can be preserved beyond the existence of the server process or the PLANNER program.