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:
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.
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
:
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
:
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.
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.
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:
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:
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:
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:
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:
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:
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:
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.
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
.
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