If you send events to the publisher object you obtain by calling Topic::getPublisher
, the event is forwarded to all subscribers for that topic:
IceStorm::TopicPrx topic = ...; Ice::ObjectPrx pub = topic->getPublisher()->ice_oneway(); MonitorPrx monitor = MonitorPrx::uncheckedCast(pub); Measurement m = ...; monitor->report(m); // Sent to all subscribers
You can also publish an event to a single specific subscriber, by using the return value of subscribeAndGetPublisher
. For example:
MonitorPtr monitor = new MonitorI; Ice::ObjectPrx proxy = adapter->addWithUUID(monitor)->ice_oneway(); IceStorm::topicPrx topic = ...; Icestorm::QoS qos; Ice::ObjectPrx pub = topic->subscribeAndGetPublisher(qos, proxy); MonitorPrx monitor = MonitorPrx::uncheckedCast(pub); Measurement m = ...; monitor->report(m); // Sent only to this subscriber
Note that, here, we save the return value of subscribeAndGetPublisher
. The return value is a proxy that connects specifically to the MonitorI
instance denoted by proxy
. However, when the code calls report
on that proxy, instead of directly invoking on the MonitorI
instance, the request is forwarded via IceStorm.
As it stands, this code is not very interesting. After all, the call to monitor->report
is just a round-about way for the subscriber to publish a message to itself. However, the subscriber can pass this subscriber-specific publisher proxy to another process. When that process publishes an event via the proxy, the event is sent only to the specific subscriber, instead of to all subscribers for the topic. In turn, this is useful if you are using the observer pattern, with all observers attached to an IceStorm topic.
As an example, we might have a list whose state is to be monitored by a number of observers. Updates to the list are published to an IceStorm topic, say, ListUpdates
. The observers of the list subscribe with an interface such as:
interface ListObserver { void init(/* The entire state of the list */); void itemChange(/* The added or deleted item */); };
The idea is that, when an observer first starts observing the list, the init
operation is called on the observer and passed the entire list. This initializes the observer with the current state of the list. Thereafter, whenever the list changes, it calls itemChange
on the observer to inform it of the addition or deletion of an item. (The details of how this happens are secondary; the important point is that the observer is informed of the current state of the list initially and, thereafter, receives incremental updates about modifications to the list, rather than the entire list whenever it changes.)
The list itself might look something like this:
interface List { void add(Item i); void remove(Item i); void addObserver(ListObserver* lo); void removeObserver(ListObserver* lo); };
The list provides operations to add and remove an item, as well as operations to add and remove an observer. Every time add
or remove
are called on the list, the list publishes an itemChange
event to the ListUpdates
topic; this informs all the subscribed observers of the change to the list. However, when an observer is first added, the observer's init
operation must be called. Moreover, we want to call that method only once for each observer, so we cannot just publish the initial state of the list on a topic that all observers subscribe to.
The subscriber-specific proxy that is returned by subscribeAndGetPublisher
solves this nicely: the implementation of addObserver
calls subscribeAndGetPublisher
, and then invokes init
on the observer. This both subscribes the observer to the topic, and IceStorm forwards the call to init
to the observer. This is preferable to the list invoking init
on the observer directly: if the observer is misbehaved (for example, if its init
implementation blocks for some time), the list is unaffected because IceStorm shields the list from such behavior.