DwarfMiddlewareTutorialFull

Chair for Computer Aided Medical Procedures & Augmented Reality
Lehrstuhl für Informatikanwendungen in der Medizin & Augmented Reality

Lesson 1: Watch the middleware do nothing

In this lesson, you will start up the DWARF middleware and an assosciated debugging tool.

Recap: The role of the middleware in DWARF

At system built with DWARF consists of:

  • several networked computers with attached hardware,
  • several Services as processes running on each of these computers,
  • configuration data for these services, and
  • distributed middleware on the computers to let the services communicate.

The middleware, in turn, has per-host and per-process parts, as well as several debugging tools. There is no central component or server.

  • Each computer in the system has
    • a DWARF Service Manager,
    • a CORBA notification service daemon and
    • an SLP daemon.
  • Each Service process has
    • a CORBA ORB, linked as a library, and
    • a helper library to initialize CORBA, called Chair.CorbaInit.

What to build

Make sure you have a built and installed DWARF distribution. In particular, for this tutorial, you need:

  • The OmniORB stubs (in src/stub/omni)
  • The common libraries (in =src/common/
  • The middleware helpers (in src/middleware/helpers)
  • The service manager (in src/middleware/servicemgr)
  • DIVE (in src/services/DIVE, requires QT)

Runing make all install in the top build directory will install these automatically.

Starting the service manager

In a terminal window, type run-servicemgr. This script starts the CORBA notification service daemon and the service manager. It also checks that the SLP daemon is running; that should be set up to start automatically as root if you have installed SLP properly.

If all goes well, you will see a bunch of messages such as:

atbruegge11:~/test/bin$ run-servicemgr
Press Control-C to stop Service Manager and Notification Service.
Checking for SLP Daemon...running, good.
Checking network configuration...ok.
Checking IP address...ok.
Starting Notification Service.......ok.
Starting Service Manager...
Property 'EventChannelFactory' set to 'file:///tmp/notification.ior'.
Property 'XmlDescriptions' set to '/home/macwilli/test/share'.
Property 'ServiceBinaries' set to '/home/macwilli/test/bin'.
15:28:06.695 30044  1 main.cpp             168 Service Manager global not running: The DWARF Service Manager is starting...
15:28:06.734 30044  1 main.cpp             241 Service Manager global running: Using SLP Service Locator.
15:28:07.313 30044  2 servicemgr.cpp       171 ServiceManager_i  0/ 0 services: newServiceDescription(ServiceManager)
15:28:07.354 30044  2 servicemgr.cpp       141 ServiceManager_i  1/ 0 services: registerService(ServiceManager)
15:28:07.355 30044  2 activeservicedesc.cp 452 ActiveServiceDescription_i ServiceManager,Activating: Waiting for registration
15:28:07.531 30044  1 main.cpp             282 Service Manager global running: Using Notification Service.
15:28:07.566 30044  1 main.cpp             319 Service Manager global running: The DWARF Service Manager has started.
15:28:10.324 30051  2 servicemgr.cpp       171 ServiceManager_i  1/ 1 services: newServiceDescription(ARTTracker)
15:28:10.445 30051  2 servicemgr.cpp       171 ServiceManager_i  2/ 1 services: newServiceDescription(ObjectCalibration)
15:28:10.654 30051  2 servicemgr.cpp       171 ServiceManager_i  3/ 1 services: newServiceDescription(ManualTracker)
15:28:10.712 30051  2 servicemgr.cpp       171 ServiceManager_i  4/ 1 services: newServiceDescription(DIVE)
15:28:10.775 30051  2 servicemgr.cpp       171 ServiceManager_i  5/ 1 services: newServiceDescription(Selector)
...
15:28:22.929 30060  2 servicemgr.cpp       191 ServiceManager_i 42/ 1 services: addClonedServiceDescription(Selector1)
15:28:22.950 30060  2 servicemgr.cpp       191 ServiceManager_i 43/ 1 services: addClonedServiceDescription(Selector2)
15:28:23.018 30060  2 servicemgr.cpp       191 ServiceManager_i 44/ 1 services: addClonedServiceDescription(Selector3)

Starting DIVE

In another terminal window, run DIVE . This starts the DWARF Interactive Visualization Environment, a debugging tool. You will see a window such as this:

DWARF Interactive Visualization Environment

What is going on? The program DIVE is a service, and so is the service manager itself. DIVE has a need for service managers, and the service manager has a matching ability. They are connected together and communicate via CORBA. DIVE uses this CORBA connection to query the service managers about the system state and display it as a graph.

Get information on services, needs and abilities in DIVE

Click on one of the boxes shown in DIVE. A window will appear showing additional information. Try clicking in different places on the boxes; the information will change.

  • Click on the title of a service (in boldface) to show information about that service.
  • Click on the line showing a need or ability (in the bottom part of the box) to show information on that need or ability.

Exercise Try to retrieve the following information:

  • the user account name that the service manager is running under
  • the process ID that DIVE is running under
  • the communication protocols that DIVE and the service manager use to communicate

Exercise If others are using DWARF in your network, try to find you who is running what services. Select "update" from the DIVE menu or even "update continuously" to refresh the view in DIVE.

Shut it all down again

Close DIVE, go back to the terminal you started the service manager from, and press Control-C. After s few seconds, you should see the lines

15:56:26.031 30044  1 main.cpp             344 Service Manager global not running: The DWARF Service Manager has shut down.
Service Manager has terminated, cleaning up...done.

If you do not, try the command killall -9 servicemgr in another terminal. Of course, you should never have to use this, but as we all know, no software is ever completely bug-free. smile


Lesson 2: Set up two communicating services

In this lesson, you will set up two simple services that communicate via CORBA method calls.

Start the service manager

Open two terminals in the bin directory of your installation. In one terminal, start the service manager with run-servicemgr. In a second, start DIVE as in Lesson 1, and select "update continously".

Build and install the time client and server

Make sure you have all the installed components of Lesson 1. In addition, you will now build a simple time-of-day service and a client for it.

Run make all in (top build directory)/src/tutorials/middleware/lesson2. If no errors occur, run make install. This will install the following four files in your install directory:

  • bin/TimeService - the executable for the time service
  • bin/TimeClient - the executable for the time client
  • share/TimeService.xml - the service description of the time service
  • share/TimeClient.xml - the service description of the time client.

Note that, as you install the XML files, the running service manager parses them. You will see two messages in the service manager terminal:

13:28:15.642 32495  2 servicemgr.cpp       171 ServiceManager_i 33/ 1 services: newServiceDescription(TimeService)
13:28:15.658 32495  2 servicemgr.cpp       171 ServiceManager_i 34/ 1 services: newServiceDescription(TimeClient)

Needs and abilities in XML

Let us take a look at the XML service descriptions, side by side:
TimeService TimeClient
<service name="TimeService">
   <ability name="giveTime" type="TimeOfDay">
      <connector protocol="ObjrefExporter"/>
   </ability>
</service>
<service name="TimeClient">
   <need name="time" type="TimeOfDay">
      <connector protocol="ObjrefImporter"/>
   </need>
</service>

What will happen to these services once they are started?

  • The TimeService has an ability of type TimeOfDay, and the TimeClient has a need of the same type. Thus, the need and ability match each other.
  • The TimeService's ability has an ObjrefExporter connector, and the TimeClient's Need has an ObjrefImporter connector. Since these connectors match as well, the two services can actually communicate with one another.
  • Thus, once both services are started, the service manager will export an object reference from the TimeService and import it to the TimeClient.
  • What happens with the object reference after that is up to the services.

Start and register a service

Open two new terminals in the bin directory of your installation. In the third terminal, start the time service with TimeService. You will see:

atbruegge11:~/test/bin$ ./TimeService
13:31:54.559   825 10 TimeService.cpp      079  : Initializing CORBA
13:31:54.563   825 10 corbainit.cpp        063  : ORB_init successful done
13:31:54.580   825 10 corbainit.cpp        069  : RootPOA successful resolved and narrowed
13:31:54.581   825 10 corbainit.cpp        073  : POA manager activated
13:31:54.582   825 10 corbainit.cpp        079  : command line resolution done
13:31:54.583   825 10 corbainit.cpp        082  : ServiceManagerHostString created
13:31:54.587   825 10 corbainit.cpp        095  : service manager resolved and narrowed
13:31:54.588   825 10 TimeService.cpp      082  : Creating Service
13:31:54.589   825 10 TimeService.cpp      044 TimeService_impl : constructor
13:31:54.590   825 10 TimeService.cpp      085  : registering Service
13:31:54.605   832 10 TimeService.cpp      050 TimeService_impl : getStatus
13:31:54.612   825  1 TimeService.cpp      088  : Starting Time Service, exit with Control-C
13:31:57.635   832 10 TimeService.cpp      050 TimeService_impl : getStatus
13:32:00.665   832 10 TimeService.cpp      050 TimeService_impl : getStatus
13:32:03.700   832 10 TimeService.cpp      050 TimeService_impl : getStatus

At the same time, in the service manager's terminal, you will see:

13:31:54.592   831  2 servicemgr.cpp       141 ServiceManager_i 35/ 1 services: registerService(TimeService)

What is going on? Upon startup, the TimeService contacts the service manager on the local host and registers itself. The service manager then associates this running instance of the TimeService with a service description generated from the TimeService.xml file. It periodically calls getStatus on the service to make sure the service is still alive.

In DIVE, you will see a lonely TimeService:

Time Service with no partner

Start another service

In the fourth terminal, run TimeClient. You will see:

atbruegge11:~/test/bin$ ./TimeClient
13:33:28.926   835 10 TimeClient.cpp       104  : Initializing CORBA
13:33:28.931   835 10 corbainit.cpp        063  : ORB_init successful done
13:33:28.937   835 10 corbainit.cpp        069  : RootPOA successful resolved and narrowed
13:33:28.938   835 10 corbainit.cpp        073  : POA manager activated
13:33:28.939   835 10 corbainit.cpp        079  : command line resolution done
13:33:28.940   835 10 corbainit.cpp        082  : ServiceManagerHostString created
13:33:28.949   835 10 corbainit.cpp        095  : service manager resolved and narrowed
13:33:28.949   835 10 TimeClient.cpp       107  : Creating Service
13:33:28.951   835 10 TimeClient.cpp       049 TimeClient_impl : constructor
13:33:28.952   835 10 TimeClient.cpp       110  : registering Service
13:33:28.973   835  1 TimeClient.cpp       113  : Starting Time Client, exit with Control-C
13:33:28.984   835 10 TimeClient.cpp       083 TimeClient_impl : no time service found yet
13:33:28.986   842 10 TimeClient.cpp       061 TimeClient_impl : getStatus
13:33:29.047   842 10 TimeClient.cpp       069 TimeClient_impl : importObjref
13:33:31.066   842 10 TimeClient.cpp       061 TimeClient_impl : getStatus
current time is: 13:33:31
13:33:34.095   842 10 TimeClient.cpp       061 TimeClient_impl : getStatus
current time is: 13:33:35
13:33:37.125   842 10 TimeClient.cpp       061 TimeClient_impl : getStatus
current time is: 13:33:38

In the service manager terminal, you will see:

13:33:28.954   841  2 servicemgr.cpp       141 ServiceManager_i 35/ 2 services: registerService(TimeClient)

Finally, in the TimeService terminal, you will see:

13:33:31.996   845 10 TimeService.cpp      060 TimeService_impl : getDayTime
13:33:33.075   832 10 TimeService.cpp      050 TimeService_impl : getStatus
13:33:35.005   845 10 TimeService.cpp      060 TimeService_impl : getDayTime
13:33:36.105   832 10 TimeService.cpp      050 TimeService_impl : getStatus
13:33:38.015   845 10 TimeService.cpp      060 TimeService_impl : getDayTime

What is going on? The TimeClient registers itself with the service manager, as the TimeServer has already done. After a fer seconds, the service manager creates a connection between the two services and calls importObjref on the TimeClient, giving it a reference to the TimeService. The TimeClient then periodically calls getDayTime on the TimeService and prints the current time of day it has retrieved.

In DIVE, you will see the TimeClient connected to the TimeService:

Time Service and Time Client

Stop and restart the services

Press Control-C in the TimeClient terminal. The program will terminate. In the service manager terminal, you will see:

14:06:52.751  2693  2 activeservicedesc.cp 782 ActiveServiceDescription_i TimeClient,Started: lost connection to service
14:06:52.785  2693  2 servicemgr.cpp       266 ServiceManager_i 35/ 3 services: deletedServiceDescription(TimeClient)
14:06:57.726 32495  2 servicemgr.cpp       171 ServiceManager_i 34/ 3 services: newServiceDescription(TimeClient)

The service manager detects that the getStatus method call has failed, since the TimeClient has terminated, and deletes the service's description. It then re-reads the service description from the XML file.

If you start and stop the TimeClient repeatedly, it will reconnect to the TimeService.

Hunt some bugs

Now, with a TimeClient running, stop the TimeService. Now, the TimeClient will crash the next time it tries to call getDayTime, reporting:

14:17:59.234  3271 10 TimeClient.cpp       117  : Caught CORBA::Exception.
The service manager will report that it has lost the connection to both services.

In the course of starting and stopping services, you may be able to produce an error message in the service manager:

**********************************************
*  Detected run-time error in thread 1338   *
14:17:59.739  1338  0 servicemgr.cpp       373 ServiceManager_i 34/ 2 services: invalid service name
14:17:59.737  1338 10 servicemgr.cpp       368 ServiceManager_i 34/ 2 services: safeGetServiceDescription(TimeClient)
14:17:59.737  1338 10 servicemgr.cpp       210 ServiceManager_i 34/ 2 services: getServiceDescription(TimeClient)
**********************************************

This comes from DIVE, which is trying to retrieve information about the TimeClient service just after the service has died, but before TimeClient.xml has been parsed again. In the DIVE terminal, you will see an error message as well:

14:17:59.742  3287  8 dwarfsystemmodel.cpp 160  : Exception when updating service TimeClient@atbruegge11

Exercise 2.1 Wildly start and stop services, and try to get the service manager to crash. Then report how you did it. smile

Exercise 2.2 Run the TimeService and write a script that periodically starts and stops the TimeClient. Run top in another terminal, sort the processes by memory use (press 'M'), and watch to see if the memory use of DIVE and the service manager increases. If so, you have found a memory hole. Feel free to fix it. smile

Exercise 2.3 Take a look at what the service manager is doing when it appears to be doing nothing. Set the environment variable DWARF_DEBUGLEVEL to a higher value than the default of 2 for the service manager, e.g. run:

atbruegge11:~/test/bin$ DWARF_DEBUGLEVEL=5 ./run-servicemgr

Look at the source

Open the three relevant source files and browse through them:

  • src/tutorials/middleware/lesson2/TimeService.cpp
  • src/tutorials/middleware/lesson2/TimeClient.cpp
  • src/idl/DWARF/Tutorial.idl

First, let us look at the IDL file, which contains:

   interface Clock {
      TimeOfDay getDayTime();
   };
   interface TimeService : Service, Clock {
   };

The interface TimeService is assembled from two parts. It inherits from the Service interface which is mandatory for every DWARF service and from the Clock interface which provides the application logic. This structure demonstrates the separation of internal (DWARF) logic and application logic.

The resulting aggregation interface is implemented by the TimeService_impl class of the TimeService. To do this, the TimeService has only to implement the method getStatus() from Service interface and the method getDayTime() from the Clock interface:

   class TimeService_impl :
      public POA_DWARF::TimeService,
      public Debuggable
   {
   public:
      TimeService_impl();
      virtual ~TimeService_impl();
      //Service interface
      char* getStatus();
      // Clock interface
      DWARF::TimeOfDay getDayTime();
   };

Because we have chosen communication via object references, we need not to implement any further methods for communication.

The TimeClient that should act a client to our TimeService also has to be a DWARF service, i.e. it has also to implement getStatus(). Beside this, it does not need to implement additional application logic methods. But because of the communication style, it has to implement importObjref(Object objref) inherited from SvcProtObjrefImporter, and maintain a variable to store the CORBA object reference to the TimeService:

   class TimeClient_impl :
      public POA_DWARF::Service_ObjrefImporter,
      public Debuggable
   {
      DWARF::Clock_var myTimeService;
   public:
      TimeClient_impl();
      virtual ~TimeClient_impl();
      //Service interface
      char* getStatus();
      //SvcProtObjrefImporter interface
      void importObjref(CORBA::Object *  objref);
      //the work
      void run();
   };

Exercise 2.4 Add better error handling to the TimeClient, so that it will simply ignore a failed getDayTime call.

Exercise 2.5 Trace the definition of the interface Service_ObjrefImporter back through Service.idl.

Exercise 2.6 These aggregation interfaces are clumsy. Try to implement the TimeService_impl class using multiple inheritance, such as thus:

class TimeService_impl :
   public POA_DWARF::Service,  //invalid multiple inheritance in CORBA!
   public POA_DWARF::Clock,    //invalid multiple inheritance in CORBA!
   public Debuggable
Even if you fix the compiler error (hint: try a cast to POA_DWARF::Service*), you will get a run-time error later on. Why?


Lesson 3: Starting services on demand

(use TimeService and TimeClient with specific XML files)

In this lesson, you will learn how a service can be started automatically when it is needed by another service.

Reconfigure the time client and server

Use the installed binaries TimeService and TimeClient of lesson 2, and start up the service manager.

Run make all in (top build directory)/src/tutorials/middleware/lesson3. This will install two new service descriptions:

TimeService TimeClient
<service name="TimeService"
   startOnDemand="true" stopOnNoUse="true"
   startCommand="TimeService">
   <ability name="giveTime" type="TimeOfDay">
      <connector protocol="ObjrefExporter"/>
   </ability>
</service>
<service name="TimeClient"
   startOnDemand="false" stopOnNoUse="false">
   <need name="time" type="TimeOfDay">
      <connector protocol="ObjrefImporter"/>
   </need>
</service>

Note the new XML tags:

  • startOnDemand: This indicates that the service manager should start the service only when an ability is requested by another service. The default is false; thus, the TimeClient will behave as in lesson 2.
  • stopOnNoUse: These indicates that the service manager should shut the service down when no abilities are requested anymore. Again, the default is false.
  • startCommand: This is the name of a binary or jar file in the installation directory that the service manager should execute in order to load the service.

Try it out

In one terminal, run TimeClient. A new xterm window called TimeService will open automatically and run a Time Service.

The service manager shows what's going on: the TimeClient registers itself; the service manager launches an executable for the TimeService, and once the executable is started, it registers itself with the service manager.

13:52:19.765 26060  2 servicemgr.cpp       141 ServiceManager_i 33/ 1 services: registerService(TimeClient)
13:52:19.804 26051  2 defaultsvcload.cpp   099 DefaultSvcLoad : loadService: running /home/macwilli/test/bin/TimeService
13:52:21.931 26094  2 servicemgr.cpp       141 ServiceManager_i 33/ 2 services: registerService(TimeService)

Press Control-C to terminate the TimeClient. After a while of idling, the TimeService will be killed by the service manager:

13:52:41.150 26052  2 activeservicedesc.cp 782 ActiveServiceDescription_i TimeClient,Started: lost connection to service
13:52:41.162 26052  2 servicemgr.cpp       266 ServiceManager_i 33/ 2 services: deletedServiceDescription(TimeClient)
13:52:41.953 25874  2 servicemgr.cpp       171 ServiceManager_i 32/ 2 services: newServiceDescription(TimeClient)
13:52:56.329 26051  2 defaultsvcload.cpp   152 DefaultSvcLoad : loadService: killing process 26063, service TimeService
13:52:56.339 26051  2 servicemgr.cpp       266 ServiceManager_i 33/ 1 services: deletedServiceDescription(TimeService)
13:52:57.864 25874  2 servicemgr.cpp       171 ServiceManager_i 32/ 1 services: newServiceDescription(TimeService)

Exercise 3.1 Try running the TimeService manually, without a TimeClient running. What happens? How does the service manager know which process ID to kill?

Exercise 3.2 Edit the installed TimeClient.xml to read startOnDemand=true. Why doesn't this work?

Exercise 3.3 Edit the installed TimeClient.xml and add stopOnNoUse=true. What is going wrong here?

Exercise 3.4 Start the TimeClient, and close the window of the TimeService once it is up and running. What happens? Use your improved version of the TimeClient from Exercise 2.4. What happens now?

The SvcStartup interface

A service may implement the SvcStartup interface, as defined in Service.idl:

   interface SvcStartup {
      void startService(in ActiveServiceDescription serviceDesc);
      void stopService(in ActiveServiceDescription serviceDesc);
   };

The service manager calls the methods startService and stopService when a service is started or stopped. This allows a service to perform initialization and cleanup tasks, such as starting or stopping worker threads.

Exercise 3.5 Write a NewTimeService that implements the SvcStartup interface:

  • Define a new aggregation interface NewTimeService in Tutorial.idl that inherits from SvcStartup, Service and Clock.
  • Copy lesson2/TimeService.cpp to lesson3/NewTimeService.cpp, and edit the Makefile.am appropriately to create and install a NewTimeService binary. Modify TimeService.xml to launch this binary.
  • Implement the SvcStartup interface in NewTimeService. Note: you will have to derive from POA_DWARF::NewTimeService.
  • Display a greeting message on service startup and shutdown.


Lesson 4: Attributes for abilities, predicates for needs

In this lesson, you will learn how to perform a finer-grained selection of abilities to fulfill a given need.

Reconfigure the time client and server

Use the installed binaries TimeService and TimeClient of lesson 2, and start up the service manager.

Run make all in (top build directory)/src/tutorials/middleware/lesson4. This will install two new service descriptions:

TimeService TimeClient
<service name="TimeService">
   <ability name="giveTime" type="TimeOfDay">
      <attribute name="accuracy" value="low"/>
      <connector protocol="ObjrefExporter"/>
   </ability>
</service>
<service name="TimeClient">
   <need name="time" type="TimeOfDay"
      predicate="(accuracy=high)">
      <connector protocol="ObjrefImporter"/>
   </need>
</service>

An ability may have attributes, specified by name-value pairs. Here, the TimeOfDay ability has the attribute accuracy with a value of low.

Likewise, a need may have a predicate, specified as a boolean expression in LDAP notation. This basically means prefixing operators and a lot of parentheses, e.g. (|(a=1)(!(b=2))). Note that the ampersand, the and operator, must be escaped as & in XML.

Try it out

In one terminal, run TimeClient. In another, run TimeService. Nothing will happen. In DIVE, you will see two services that are not connected:

Non-matching Time Service and Time Client

When you inspect the TimeOfDay need and ability in DIVE, you will see the attribute values and predicate. Note that many attributes are built-in and set by the service manager. Also note that a service itself may have attributes, which are inherited by the ability. Even a need may have attributes, but these are not used for selection of the communication partner.

Now modify TimeService.xml so that the accuracy attribute is high. The service manager will re-read the XML file, but still nothing will happen. In fact, the service manager has disconnected the running TimeService from its service description, since the XML file has changed. Restart the TimeService and the two services will be connected:

Connected Time Service and Time Client

Exercise 4.1 Add another attribute to the TimeService, and try out a matching and a non-matching boolean expression for the TimeClient.

Exercise 4.2 Make the TimeService have three different abilities of type TimeOfDay, differing in their name, each with a different value of accuracy. Create a boolean expression for the TimeClient so that it will connect only to the third of these needs, creating this image in DIVE:

Time Service with three abilities

Exercise 4.3 Predicates may contain wildcards, e.g. fo*ba* matches foobar and foambath. Try this out.

Exercise 4.4 Work with another user on a second machine. Each run a service manager, DIVE, a Time Service and a Time Client. Turn on the "show host name" option in DIVE. By default, each Time Client will connect to the Time Server on its own machine. Reverse this using attributes, creating this image in DIVE:

Time Service on two hosts

The SvcSelection interface

A service may implement the SvcSelection interface, as defined in Service.idl:

   interface SvcSelection {
      void foundPartners(in ActiveServiceDescription serviceDesc,
         in string needabilityname, in PartnerSeq partners);
   };

The service manager calls this for a service that has a need, order to let the service choose its partner on its own. Normally, you have no need to do this; boolean expressions and wildcards will suffice.

Exercise 4.5 Write a NewTimeService that implements the SvcSelection interface and selects partners that have a number attribute which is prime.


Lesson 5: Multiple services using templates

In this lesson, you will learn how to create multiple instances of the same service.

Reconfigure the time client and server

Use the installed binaries TimeService and TimeClient of DwarfMiddlewareTutorialLesson2, and start up the service manager.

Run make all in (top build directory)/src/tutorials/middleware/lesson5. This will install two new service descriptions:

TimeService TimeClient
<service name="TimeService" isTemplate="false">
   <ability name="giveTime" type="TimeOfDay">
      <connector protocol="ObjrefExporter"/>
   </ability>
</service>
<service name="TimeClient" isTemplate="true">
   <need name="time" type="TimeOfDay">
      <connector protocol="ObjrefImporter"/>
   </need>
</service>

This lesson introduces the isTemplate attribute. By default, it is false, and only one instance of the service may exist on the host at any time. This makes sense for services that encapsulate physical resources, such as trackers or displays. When it is true, there may be any number of instances of this service running.

In this example, only the client is a template; the TimeService will behave as before.

Try it out

In one terminal, run TimeService. In three other terminals, run TimeClient. All three clients will be able to run and connect to the TimeService, showing this DIVE image:

Time Service and Three Clients

The service manager shows the multiple instances of the time clients starting up, called TimeClient1 to TimeClient3. Note that an additional service description, TimeClient4, is kept in reserve, so that it can prepare to set up connections even before the service itself is started.

19:19:44.999  3348  2 servicemgr.cpp       171 ServiceManager_i  1/ 1 services: newServiceDescription(TimeClient)
19:19:45.017  3348  2 servicemgr.cpp       171 ServiceManager_i  2/ 1 services: newServiceDescription(TimeService)
19:19:45.033  3351  2 servicemgr.cpp       191 ServiceManager_i  3/ 1 services: addClonedServiceDescription(TimeClient1)
19:19:45.042  3348  2 servicemgr.cpp       171 ServiceManager_i  4/ 1 services: newServiceDescription(DIVE)
19:19:45.096  3348  2 servicemgr.cpp       171 ServiceManager_i  5/ 1 services: newServiceDescription(ServiceReporter)
19:20:02.946  3389  2 servicemgr.cpp       141 ServiceManager_i  6/ 1 services: registerService(TimeService)
19:20:06.936  3404  2 servicemgr.cpp       141 ServiceManager_i  6/ 2 services: registerService(TimeClient)
19:20:06.948  3351  2 servicemgr.cpp       191 ServiceManager_i  6/ 2 services: addClonedServiceDescription(TimeClient2)
19:20:07.067  3411  2 servicemgr.cpp       141 ServiceManager_i  7/ 3 services: registerService(TimeClient)
19:20:07.083  3351  2 servicemgr.cpp       191 ServiceManager_i  7/ 3 services: addClonedServiceDescription(TimeClient3)
19:20:07.087  3413  2 servicemgr.cpp       141 ServiceManager_i  8/ 4 services: registerService(TimeClient)
19:20:07.111  3351  2 servicemgr.cpp       191 ServiceManager_i  8/ 4 services: addClonedServiceDescription(TimeClient4)

In another terminal, run TimeService again, while one instance is already running. Since isTemplate=false for TimeService, an error message occurs:

**********************************************
*  Detected run-time error in thread 4966   *
19:48:17.721  4966  0 activeservicedesc.cp 450 ActiveServiceDescription_i TimeService,Started: service alredy registered
19:48:17.713  4966  5 activeservicedesc.cp 435 ActiveServiceDescription_i TimeService,Started: registerService
19:48:17.712  4966  2 servicemgr.cpp       141 ServiceManager_i  9/ 5 services: registerService(TimeService)
**********************************************

Now, set isTemplate=true for the TimeService. This will allow you to start more than one TimeService at a time. However, the TimeClients will still only connect to one TimeService each - in fact, this will usually be the first one.

Exercise 5.1 How many TimeClients can you start until the machine you are using becomes unusable?


Lesson 6: Session Handling

In this lesson, you will learn how one service can handle multiple sessions to other services.

Reconfigure the time client and server

Use the installed binaries TimeService and TimeClient of DwarfMiddlewareTutorialLesson2, and start up the service manager.

Run make all in (top build directory)/src/tutorials/middleware/lesson6. This will install two new binaries, TimeService and TimeClient, overwriting those of DwarfMiddlewareTutorialLesson2. It will also install two new service descriptions:

TimeService TimeClient
<service name="TimeService" isTemplate="true">
   <ability name="giveTime" type="TimeOfDay">
      <connector protocol="ObjrefExporter"/>
   </ability>
</service>
<service name="TimeClient" isTemplate="true">
   <need name="time" type="TimeOfDay"
    minInstances="1" maxInstances="3">
      <connector protocol="ObjrefImporter"/>
   </need>
</service>

There are two differences to the previous lesson:

  • This time, both the TimeService and the TimeClient are template services.
  • The need time of the TimeClient has two new parameters, minInstances and maxInstances. These specify how many communication partners should be connected to this need:
    • The middleware will not connect the TimeClient to anything else until at least minInstances partners have been found for the need.
    • The middleware will connect up to maxInstances partners at any time.

Try it out

In three terminals, run three TimeServices. In three other terminals, run three TimeClients. All three clients will be able to run and connect to all three TimeServices, showing this DIVE image:

Three Time Services and Three Clients

The TimeClient will get the time from each partner:

Getting time from 3 partners...
current time from service:TimeOfDay.ObjrefExporter://atbruegge11/TimeService5/giveTime/ObjrefExporter is: 16:44:44
current time from service:TimeOfDay.ObjrefExporter://atbruegge11/TimeService6/giveTime/ObjrefExporter is: 16:44:44
current time from service:TimeOfDay.ObjrefExporter://atbruegge11/TimeService7/giveTime/ObjrefExporter is: 16:44:44

How it works

If a service implements the SvcSession interface, the service manager calls the newSession and endSession methods when a need or ability is about to be connected to other services.

For example, TimeClient_impl::newSession() is called for each TimeService:

16:44:35.270 00003 10 TimeClient.cpp       148 TimeClient_impl :
   newSession(time,ObjrefImporter,service:TimeOfDay.ObjrefExporter://atbruegge11/TimeService5/giveTime/ObjrefExporter)
16:44:35.313 00003 10 TimeClient.cpp       150 TimeClient_impl :
   partner's attributes are: (abilityName=giveTime),(abilityState=Registered),(hostname=atbruegge11),
   (pid=12253),(registered=true),(serviceID=TimeService5-atbruegge11-Tue-Nov-11-16-44-00-2003),
   (serviceName=TimeService5),(serviceState=Started),(started=true),(status=test status),(username=macwilli)

In the newSession method, the TimeService creates a new object of class TimeContact to handle the connection to the TimeService. It returns a reference to that object to the service manager. The service manager then calls importObjref on that newly created object.

Exercise 6.1 How many TimeServices does one TimeClient connect to? What happens if you change maxInstances?

Exercise 6.2 How many TimeClients can you connect to one TimeService? What happens if you change maxInstances?

Exercise 6.3 Set the startOnDemand and startCommand parameters for the TimeService, as in DwarfMiddlewareTutorialLesson3. Then, start a TimeClient. How many TimeServices are started? What happens if you start a second TimeClient?

Exercise 6.4 Take a close look at the implementation of newSession and endSession. How do they achieve thread safety? How does endSession ensure that the TimePartner object is deleted?

Exercise 6.5 Extend the TimeService to give a different time to each TimeClient that connects to it: the first client receives the correct time, the second receives the current time one hour ago, and so on.

Exercise 6.6 Give the TimeService three different abilities which differ by an attribute timezone, with values of Munich, NewYork and Tokyo. Make the TimeService give clients the correct time for each time zone. Also, make the TimeClient print e.g. "the current time in Munich is 16:59". (Hint: the TimeClient should use the getAttribute() method at some point, and the TimeServer should call serviceDesc->getAbility(needabilityname) in newSession().)

Exercise 6.7 Now do it the other way around: have the TimeService only have one ability, but give the TimeClient an attribute timezoneRelativeToGMT, with a value of +1, -6, etc., specified in the XML file. Have the TimeService give each TimeClient its correct time.


Lesson 7: Sending and Receiving Events

In this lesson, you will how services can communicate with events.

Build time receiver and sender

Run make install in (top build directory)/src/tutorials/middleware/lesson7. This will install two new binaries, TimeSender and TimeReceiver. It will also install two new service descriptions:

TimeSender TimeReceiver
<service name="TimeSender" isTemplate="true">
   <ability name="senddate" type="TimeOfDay">
      <connector protocol="PushSupplier"/>
   </ability>
</service>
<service name="TimeReceiver" isTemplate="true">
   <need name="receivedate" type="TimeOfDay"
    minInstances="1" maxInstances="3">
      <connector protocol="PushConsumer"/>
   </need>
</service>

Note the two new communication protocols, PushSupplier and PushConsumer. A PushSupplier sends events, a PushConsumer receives them.

Try it out

In one terminal, run the TimeSender. In another, run the TimeReceiver. The two services will start communicating, showing this image in DIVE:

A string sender and receiver

The TimeSender will periodically send an event:

16:49:38.378 00000 10 TimeSender.cpp       119 TimeSender_impl : preparing event
16:49:38.378 00000 10 TimeSender.cpp       127 TimeSender_impl : getting time
16:49:38.378 00000 10 TimeSender.cpp       138 TimeSender_impl : sending event
16:49:39.387 00000 10 TimeSender.cpp       119 TimeSender_impl : preparing event
16:49:39.388 00000 10 TimeSender.cpp       127 TimeSender_impl : getting time
16:49:39.388 00000 10 TimeSender.cpp       138 TimeSender_impl : sending event

And the TimeReceiver will receive it:

got event with time of day: 16:49:38
got event with time of day: 16:49:39

Debugging the events

In DIVE, click on the sendstrings ability of the TimeSender. Then click on "View Events". A new window will appear, allowing you to see the events sent by the TimeSender. Note that for this, DIVE creates its own little debugging service that itself subscribes to the events sent by the TimeSender. You will see this it you click on "Update" in DIVE.

A string sender and receiver

Exercise 7.1 How many TimeReceivers does one TimeSender connect to?

Exercise 7.2 How many TimeSenders does one TimeReceiver connect to? What happens if you change maxInstances?

How it works


Lesson 8: Changing Attributes

In this lesson, you will learn how a service can be configured with attributes.

Build time receiver and sender

First, run make install in (top build directory)/src/tutorials/middleware/lesson7.

Now, run make install in (top build directory)/src/tutorials/middleware/lesson8. This will install a new binary, for the TimeSender. We will reuse the TimeReceiver of lesson 7. It will also install a new service description:

TimeSender
<service name="TimeSender" isTemplate="true">
   <attribute name="tickInterval" value="3">
   <ability name="senddate" type="TimeOfDay">
      <connector protocol="PushSupplier"/>
   </ability>
</service>

Note the attribute tickInterval. The TimeSender evaluates this attribute to decide how often to send an event.

Try it out

In one terminal, run the TimeSender. In another, run the TimeReceiver. The two services will start communicating, as in DwarfMiddlewareTutorialLesson7.

Note how the TimeSender reads its configuration:

15:20:02.540 00003 10 TimeSender.cpp       135 TimeSender_impl : tickInterval attribute found: 3
15:20:02.540 00003 10 TimeSender.cpp       137 TimeSender_impl : tickInterval set to 3

And the TimeReceiver will receive events every three seconds:

got event with time of day: 15:20:42
15:20:45.262 00003 10 TimeReceiver.cpp     064 TimeClient_impl : getStatus
got event with time of day: 15:20:45
got event with time of day: 15:20:48
15:20:49.467 00003 10 TimeReceiver.cpp     064 TimeClient_impl : getStatus
got event with time of day: 15:20:51

Changing the attribute

In DIVE, click on the sendDate ability of the TimeSender. Then click on "Edit Attributes". A new window will appear, allowing you to adit the attributes of the TimeSender.

Editing attribtues in DIVE

Change the attribute tickInterval to 5. See what happens. Change it to 1. See what happens.

Changing the attribute quickly

In DIVE, select the sendDate ability and click on "Python Shell". Enter the following command (don't forget to hit return twice):

for i in range(1,100) : ad.setAttribute("tickInterval", "%d" % i)

This is what happens...

15:54:17.689 00003 10 TimeSender.cpp       135 TimeSender_impl : tickInterval attribute found: 1
15:54:17.689 00003 10 TimeSender.cpp       137 TimeSender_impl : tickInterval set to 1
15:54:17.789 00003 10 TimeSender.cpp       135 TimeSender_impl : tickInterval attribute found: 63
15:54:17.790 00003 10 TimeSender.cpp       137 TimeSender_impl : tickInterval set to 63
15:54:17.890 00003 10 TimeSender.cpp       135 TimeSender_impl : tickInterval attribute found: 99
15:54:17.891 00003 10 TimeSender.cpp       137 TimeSender_impl : tickInterval set to 99

How it works

Whenever a service implements the AttributesChanged interface, it will be notified when the attributes of its service description change.

Thus, the TimeSender service reads it attributes in two places:

  • the startService method
  • the attribtuesHaveChanged method

Excercise 8.1 Run the TimeSender with the command line parameters -DtickInterval=5. What happens? Why?


Lesson X: Planned Future Lessons

These lessons still need to be written...

Communication using shared memory

Multitithreading and multiple services per executable

(use modified TimeClient with multiple services in the same executable)


Communication using shared memory

(use TestStringSender and TestStringReceiver with specific XML files)


Registering needs and abilities

(use Sheep)


Delegated selection

(use TestStringSender, TestStringReceiver and TestStringSenderJAVA, select using Selector and ListMenuDisplay)


Chains of services

(use TestStringSender and TestStringReceiver with specific XML files; write a TestStringFilter. Or, easier: reference the UIC tutorial.)


Context attributes

(Combine lessons 11 and 12)

-- AsaMacWilliams - 23 Feb 2004



Edit | Attach | Refresh | Diffs | More | Revision r1.7 - 17 Mar 2005 - 21:07 - NikolasDoerfler

Lehrstuhl für Computer Aided Medical Procedures & Augmented Reality    rss.gif