PreviousException Handling Frameworks Descriptions of OO Run-time SwitchesNext

Chapter 21: Component Frameworks

This chapter describes the component framework, which provides an alternative mechanism for objects to communicate.

21.1 Overview

The component framework provides a mechanism which enables you to treat objects like pluggable components. Once you have defined an object as a component, you can:

The input socket of a component does not have to define behavior for every signal it might receive when connected to the output of another component.

When a component sends a signal, it doesn't send it to a particular object. It sends it to all the components which are connected to it. Each component that receives a signal can either ignore or respond to the signal. The figure below shows an interface component using signals to communicate with database and application components.

This is different to the situation with a message, where you must specify a single destination object when you send a message. If you don't specify a valid object, or specify one that doesn't understand the message, the application breaks. With the component mechanism, you can define objects (or groups of cooperating objects) which act as common building blocks. You then build your application by plugging together groups of components.

21.2 Using Signals and Sockets

The steps below outline the process for connecting components together and signalling between them.

To use signals and sockets:

  1. Make an object into a component and define the signals it will raise. You have now defined an output component.

  2. Make another object into a component , and create an input socket. Define the input signals the socket recognizes, and the method to be invoked on receipt of the signal. You have now defined an input component.

  3. Connect the two components together.

  4. Each time the output component sends a signal defined by the input socket, the corresponding method is invoked on the input component.

These steps are defined in more detail in the following sections.

21.2.1 Defining Output Signals

A component must define a name for all the signals it will generate. A signal can be any null-terminated string (a literal ending with x"00"), up to 32 characters long. Each output signal defined for a particular component must have a unique name. Before you can define output signals for an object, you must make it into a component.

To make an object into a component:

Names do not have to be unique throughout an application. You can have many different components which define signals with the same names. Each component which wants to receive signals defines a different input socket for each different type of component to which it wants to connect. So even if different types of components define signals with the same names, there are no name clashes.

We recommend that you define all the literals for your signal names as level 78 data items in a copyfile, and always refer to the signals by the names in the copyfile. When you compile a program, you will get a compile time error if you have mistyped the dataname for the signal. If you use literals directly in your code, mistyping a signal-name will not raise an error until run-time.

The statement to define a signal is:

define signal signalId

where the parameter is:

signalId Null-terminated (ends in x"00") string

DEFINE SIGNAL is vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the DEFINE SIGNAL verbs. Using DEFINE SIGNAL is an alternative syntax to:

invoke self "defineOutSignal" using SignalId

See the chapter Requirements-based Vocabulary for more information about using vocabularies.

This is an example piece of code defining a set of signals:

*Define signals
 define signal new-signal
 define signal open-signal
 define signal save-signal
 define signal exit-signal

The signals in this example are defined as level-78s in a copyfile:

 78 exit-signal        value z"001".
 78 new-signal         value z"002".
 78 open-signal        value z"003".
 78 save-signal        value z"004".

21.2.2 Defining Input Sockets

To receive signals from another component, you must define an input socket for the component. Identify each input socket with a unique name. The name can be any null-terminated (ends in x"00") string, up to 32 characters long. You can define as many input sockets for a component as you want, as long as each one has a different name. Different components can have the same input socket name.

The statement to declare an input socket is:

declare socket socketName

where the parameter is:

socketName Null-terminated literal (ends in x"00") naming the socket.

DECLARE SOCKET is vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the DECLARE SOCKET verb. Using DECLARE SOCKET is an alternative syntax to:

invoke self "declareSocket" using socketName

Having defined an input socket, you need to map all the signals you expect to receive on that input socket to methods in the component. The socket name and signal names are all declared as level-78 data items in a copyfile (see the previous section).

The statement to map a signal to a method is:

map signal signalId from socketName
                      to methodName 

where the parameters are:

signalId Null-terminated literal (ends in x"00") naming the signal.
socketName Null-terminated literal (ends in x"00") naming the socket.
methodName Null-terminated literal (ends in x"00") naming the method.

MAP SIGNAL is vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the DECLARE SOCKET verb. Using MAP SIGNAL is an alternative to:

invoke self "defineInSignal" using signalId
                        socketId methodName 

See the chapter Requirements-based Vocabulary for more information about using vocabularies.

The following example shows an object making itself into a component, declaring a socket, and setting up mapping for a set of signals.

 make component
 declare socket mainApplicationSocket *> declare a socket
 map signal fileOpen-signal from mainApplicationSocket
                             to method z"onSignalFileOpen"
 map signal fileOpenfailed-signal from mainApplicationSocket
                             to method z"onSignalOpenFailed"

21.2.3 Connecting Components

Output signals and input sockets only do anything when they are connected together. You can connect an output component to any number of input sockets, and any input socket can be connected to the outputs of more than one component.

In this example, two components are connected together, through a socket defined on the input component. The socket is named by a level 78 data item in a copyfile.

connect output to input at socketId

where the parameters are:

output OBJECT REFERENCE containing a handle to the output component
input OBJECT REFERENCE containing a handle to the input component
socketId A null-terminated literal (ends in x"00") naming the socket on the input component.

CONNECT is vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the CONNECT verb. Using CONNECT is an alternative syntax to:

invoke outputcomponent "connect" using inputComponent                                       socketId

See the chapter Requirements-based Vocabulary for more information about using vocabularies.

21.2.4 Sending Signals

When a component sends a signal, if it is connected to an input socket which recognizes that signal, it invokes the method defined by the input socket. You might have the component connected to separate input sockets on different components, in which case all input sockets which recognize that signal invoke methods. Methods are invoked in the order that the components were connected together.

You can optionally send one object handle as a parameter with a signal. This enables you to send some data with the signal. Sometimes you want to send a signal which is a request for data; in this case, the receiving component should define a signal for sending the data back.

For example, an interface component might request a list of customer accounts for display, by sending a "retrieve-accounts" signal. The retrieval component receives this signal, and if it finds the data, sends a "display-accounts" signal, with the account information as data. The SymbolTable class in the class library enables you to package up several pieces of information into a single object, each of which is identified by a separate key.

In this example, a component sends a signal. The signal name is defined as a level 78 in a copyfile. The parameter, dataBlock, contains an object handle which is sent as data with the signal.

signal signalId dataBlock

where the parameters are:

signalId Null-terminated literal (ends in x"00) naming the signal
dataBlock OBJECT REFERENCE containing the handle to an object to be sent with the signal as data

This code uses vocabulary defined in base.if. You must put this as a copyfile at the beginning of your program to use the SIGNAL verb. Using SIGNAL is an alternative syntax to:

invoke self "signal" using  signalId dataBlock

See the chapter Requirements-based Vocabulary for more information about using vocabularies.


Copyright © 1999 MERANT International Limited. All rights reserved.
This document and the proprietary marks and names used herein are protected by international law.

PreviousException Handling Frameworks Descriptions of OO Run-time SwitchesNext