The fundamental abstraction for events is the event channel, which will be a component instance. An instance of an event channel serves as a broadcast hub. In a pure event channel, all events posted to an event channel will be sent to all listeners on that channel.

Figure 1. P1 and P2 are publishers. L1 and L2 are listeners. ai represent different events of the same type. Similarly with bi and ci. All events are sent to all listeners.
A filtering event channel allows listeners to select the events they wish to receive. Each listener receives a subset of posted events. Internally, a switch will be used to route each event only to listeners who have a matching filter specification.

Figure 1. P1 and P2 are publishers. L1, L2 and L3 are listeners. ai represent different events of the same type. Similarly with bi and ci. Events are only sent to listeners who have registered to receive events of that type.
Events may be broadly grouped into standard events and application events.
In addition, this service requres the use of a component proxy object ComponentID which provides access to the ports by name.
As a component, an event channel has three types of ports: a publishing uses port, a registration provides port, and one or more provides ports for listeners. The registration port is used by other components to register themselves as publishers or listeners. During the registration process for a publisher, the event channel will connect its publishing provides port to a uses port of the publisher. During the listener registration process, the event channel will connect one of its uses ports to a provides port of the listener.
To receive events, a component must first implement an a provides Event Port and add it as a port. A publisher would have a uses port of this type through which he pushes events.
A publisher can send events directly to listeners without an intervening event channel. The ports can simply be connected together. This is an instance of a 1-to-n uses-to-provides connection, which is permited by the CCA specification. Connecting multiple providers is also possible. The CCA specification allows general m-to-n connections between m uses ports and n provides ports.
We believe that filtering on event types will be a very common requirement for event channels. Since such functionality is a superset of the functionality of pure event channels, this is the type we define below. Note that such a channel will have one uses port for every active event type. This uses the ability of a CCA component to dynamically add ports at runtime.
valuetype CCAEvent {
string getSource();
string getType();
long getKey();
long long getSequenceNum();
string getDetails();
};
interface EventPort : Port {
void notify(CCAEvent event);
};
interface FilteringEventChannel : CCAPort {
void registerListener(ComponentID listener,
string listener_provides_port, string type)
raises (AlreadyRegisteredException);
void registerPublisher(ComponentID publisher,
string publisher_uses_port)
raises (AlreadyRegisteredException);
void unregister(ComponentID component, string port)
raises (NotRegisteredException);
};
class CCAEvent {
virtual char *getSource() = 0;
virtual char *getType() = 0;
virtual long getKey() = 0;
virtual cca::Int64 getSequenceNum() = 0;
virtual char *getDetails() = 0;
};
class EventPort : public Port {
virtual void notify(CCAEvent *event) = 0;
};
class FilteringEventChannel : public CCAPort {
virtual void registerListener(const ComponentID *listener,
const char *listener_provides_port,
const char *type)
throw (AlreadyRegisteredException) = 0;
virtual void registerPublisher(const ComponentID *publisher,
const char *publisher_uses_port)
throw (AlreadyRegisteredException) = 0;
virtual void unregister(const ComponentID *component, const char *port)
throw (NotRegisteredException) = 0;
};
interface CCAEvent {
String getSource();
String getType();
int getKey();
long getSequenceNum();
String getDetails();
};
interface EventPort extends Port {
void notify(CCAEvent event);
};
interface FilteringEventChannel extends CCAPort {
void registerListener(ComponentID listener,
String listener_provides_port, String type)
throws (AlreadyRegisteredException);
void registerPublisher(ComponentID publisher,
String publisher_uses_port)
throws (AlreadyRegisteredException);
void unregister(ComponentID component, String port)
throws (NotRegisteredException);
};
Returns a string identifying the source of the event. This might be an URL to be used with a Instance Registry to obtain a ComponentID object, or it may be a service identifier.
Returns a string representing the event type.
This identifier can be used to distinguish between multiple event sources within one component.
Events can be lost or delivered out of order. This number can be used to order events when required. The tuple ( getSource(), getKey(), getSequenceNum() ) uniquely identifies an event.
This returns a string that contains additional type-specific information. This can be decoded based on the type field.
When the interface is being implemented by a uses port, this method will publish the event. When being implemented as a provides port, this method is called to deliver an event to a listener.
void registerListener(ComponentID listener, string listener_provides_port, string type)
raises (AlreadyRegisteredException);
This method registers the caller as a listener. The Event Channel will connect its uses port to 'listener_provides_port'. Events of type 'type' will be delivered to the listener through this port.
void registerPublisher(ComponentID publisher, string publisher_uses_port)
raises (AlreadyRegisteredException);
This method registers the caller as a publisher. The Event Channel will connect its provides port to 'publisher_uses_port'. Events that are delivered through this port will be sent to all listeners registered for the type of the event.
void unregister(ComponentID component, string port)
raises (NotRegisteredException);
The specified port belonging to the specified component is unregistered.
This event channel's registration port is also pre-exported as "StandardEventChannel", so other components can register themselves as listeners.
Example 1. The code below will connect a component A as a publisher, and a component B as a listener. Component A has a uses port named 'publish' through which it will publish events. Component B has a provides port named 'listen' through which it will listen for events.
FilteringEventChannel ec = ...; // Obtain reference to an Event Channel somehow.
ComponentID A, B;
...
// Do what's necessary here to set A and B.
...
ec->registerPublisher(A, "publish");
ec->registerListener(B, "listen", "CCAEvents"); // All event types
Example 2. This example illustrates how a component would register itself as a listener to events of type "X" (a extension of class CCAEvent) on a standard event channel.
class MyComponent extends CCA.Component {
CCA.Services serviceObject;
ComponentID myCID;
class XPort implements EventPort{
public void notify(CCAEvent e){
// event handling code here.
}
}
public void setServices(CCA.Services so){
serviceObject = so;
// note-an extension of the services object... danger here!
myCID = serviceObject.getComponentID();
XPort xp = new XPort();
serviceObject.addProvidesPort(xp, "X Events");
...
}
...
public void register_for_X_events(){
FilteringEventChannel ch = ... // Obtain reference somehow.
ch->registerListener(MyCID, "X Events", "X");
}
}
Example 3. This code illustrates how a GUI component might normally work. It instantiates a component, then registers itself as a listener to that component's "StandardEventChannel".
class GuiComponent extends CCA.Component {
CCA.Services serviceObject;
ComponentID myCID;
class StandardEventPort implements EventPort{
public void notify(CCAEvent e){
// event handling code here.
}
}
public void setServices(CCA.Services so){
serviceObject = so;
StandardEventPort sep = new StandardEventPort();
// "Standard Event Listener" is a provides port that the GUI
// uses to listen to standard events.
servicesObject.addProvidesPort(sep, "Standard Event Listener");
}
public void createComponents(){
// my_registration_up
// Uses port used by GUI to register as listener of the
// component's StandardEventChannel.
...
// Get my component ID.
myCID = serviceObject.getComponentID();
// Create a component somehow.
compID = create(...);
// Connect to its exported standard event channel.
connection_service->connect(myID, "my_registration_up",
compID, "StandardEventChannel");
my_registration_up = services.getPort("my_registration_up");
// Register as listener.
my_registration_up->registerListener(myID, "standard_events_listen",
"CCAEvents");
services.releasePort("my_registration_up");
...
}
}
If no events are desired, the StandardEventChannel may simply be ignored.
Example 4. This pseudo codes illustrates the design of a simple FilteringEventChannel.
class FilteringChannel implements CCAComponent, FilteringEventChannel {
CCA.Services serviceObject;
ComponentID myCID;
class ChannelInput implements EventPort {
public void notify(CCAEvent e){
// event handling code here.
// for each event look at its type.
// if its type is one of the types of the channels current
// output uses port
// then route it out that port.
// otherwise ignore it.
}
}
public void setServices(CCA.Services so){
serviceObject = so;
myCID = serviceObject.getComponentID();
ChannelInput in = new ChannelInput();
servicesObject.addProvidesPort(in, new DefaultPortInfo("Channel Input",
"EventPort"));
servicesObject.addProvidesPort(this,
new DefaultPortInfo("StandardEventChannel",
"FilteringEventChannel"));
}
void registerListener(ComponentID listener, string listener_provides_port,
string type){
// there is a uses port on this component for each type registered.
// 1. search the list of uses ports to see if type is already there.
// if not found then add a new uses port of that type.
// 2. use the connection service to connect the local uses port to the
// listeners provides port.
}
void registerPublisher(ComponentID publisher, string publisher_uses_port){
CCAConnectionService cs = serviceObject.getPort("Connection Service");
cs.connect(publisher, publisher_uses_port, myCID, "ChannelInput");
}
}
Example 5. The code below illustrates how a component may be moved from one GUI to another. The current GUI is performing the actual operations, and the new GUI is passive.
class GuiComponent extends CCA.Component {
CCA.Services serviceObject;
ComponentID myCID;
class StandardEventPort implements EventPort{
public void notify(CCAEvent e){
// event handling code here.
}
}
public void setServices(CCA.Services so){
serviceObject = so;
StandardEventPort sep = new StandardEventPort();
// "Standard Event Listener" is a provides port that the GUI
// uses to listen to standard events.
servicesObject.addProvidesPort(sep, "Standard Event Listener");
}
public void moveComponent(ComponentID compID, ComponentID gui2){
// Assume that we have obtained gui2 somehow as the new GUI's
// ComponentID. Also assume that we know the port with which
// the new GUI wants to listen to events.
myID = serviceObject->getComponentID();
// Connect to the component's exported standard event channel.
connection_service->connect(myID, "my_registration_up",
compID, "StandardEventChannel");
my_registration_up = services.getPort("my_registration_up");
// Unregister as listener.
my_registration_up->unregister(myID, "Standard Event Listener");
// Register the new GUI as a listener.
my_registration_up->registerListener(gui2, "Standard Event Listener",
"CCAEvents");
services.releasePort("my_registration_up");
}
}