#include <Blackboard.h>
NOTE: BLACKBOARD IS NO LONGER USED BY THE INET FRAMEWORK -- SUCCESSOR IS NotificationBoard.
Blackboard makes it possible for several modules (representing e.g. protocol layers) to share information, in a publish-subscribe fashion. Participating modules or classes have to implement the BlackboardAccess interface (=have this abstract class as base class).
Anyone can publish data items on the blackboard. In order to allow for some type safety via dynamic_cast, items have to be subclassed from cPolymorphic.
Every item is published with a unique string label. The Blackboard makes no assumption about the format of the label, but it's generally a good idea to make it structured, e.g. with a dotted format ("nic1.linkstatus"). The label can be used by subscribers to identify an item ("I want to subscribe to nic1.linkstatus"). There are other ways to subscribe too, not only by label, e.g. one can browse through all Blackboard items, examine each (its label, C++ class, actual contents etc.) and decide individually whether to subscribe to it or not.
Labels are only used when publishing or subscribing to items. After that, items can be referred to using BBItemRefs. (BBItemRefs are sort of "handles" to blackboard items; think of file handles (fd's) or FILE* variables in Unix and C programming, or window handles (HWND) in the Windows API.) BBItemRefs allow very efficient (constant-time) access to Blackboard items.
Clients may browse blackboard contents and subscribe to items. After subscription, clients will receive notifications via BlackboardAccess callbacks whenever the subscribed item changes. Clients may also subscribe to get notified whenever items are published to or withdrawn from the blackboard.
Notifications are done with callback-like mechanism. Participating modules or classes have to subclass from the abstract base class BlackboardAccess and implement its methods (in Java, one would say clients should implement the BlackboardAccess interface.) This usually means multiple inheritance, but without the problems of multiple inheritance (Multiple inheritance is sometimes considered "evil", but never if used to do "mixins" as here.)
Some examples for usage. The first one, Foo demonstrates subscribe-by-name, the second one, Bar shows subscribe-when-published, and the third one, Zak does subscribe-by-browse.
// subscribe-by-name class Foo : public cSimpleModule, public BlackboardAccess { protected: BBItemRef ref; ... };
void Foo::initialize(int stage) { // to avoid a subscribe-BEFORE-publish a two stage // initialization should be used and all publish calls should // go into the first stage (stage 0) whereas you should subscribe // in the second stage (stage 1) if(stage==0) { ... } else if(stage==1) { ref = getBlackboard()->subscribe(this,"routingTable"); ... } }
void Foo::blackboardItemChanged(BBItemRef item) { if (item==ref) { IRoutingTable *rt = check_and_cast<IRoutingTable *>(ref->getData()); ... } else ... }
// subscribe-when-published class Bar : public cSimpleModule, public BlackboardAccess { ... };
void Bar::initialize(int stage) { if(stage==0) { getBlackboard()->registerClient(this); // make sure we get what's already on the blackboard getBlackboard()->getBlackboardContent(this); } }
void Bar::blackboardItemPublished(BBItemRef item) { // if label begins with "nic." and it's a NetworkInterfaceData, subscribe if (!strncmp(item->getLabel(),"nic.",4) && dynamic_cast<NetworkInterfaceData *>(item->getData())) { // new network interface appeared, subscribe to it and do whatever // other actions are necessary getBlackboard()->subscribe(this, item); ... } }
// subscribe-by-browse class Zak : public cSimpleModule, public BlackboardAccess { ... };
void Zak::letsCheckWhatIsOnTheBlackboard() { // subscribe to all NetworkInterfaceData Blackboard *bb = getBlackboard(); for (Blackboard::iterator i=bb->begin(); i!=bb->end(); ++i) if (dynamic_cast<NetworkInterfaceData *>((*i)->getData())) bb->subscribe(this, *i); }
Public Types | |
typedef std::vector < BlackboardAccess * > | SubscriberVector |
typedef BBItem * | BBItemRef |
Public Member Functions | |
Blackboard () | |
virtual | ~Blackboard () |
Methods for publishers | |
virtual BBItemRef | publish (const char *label, cPolymorphic *item) |
virtual void | withdraw (BBItemRef bbItem) |
virtual void | changed (BBItemRef bbItem, cPolymorphic *item=NULL) |
Methods for subscribers | |
virtual BBItemRef | subscribe (BlackboardAccess *bbClient, const char *label) |
virtual BBItemRef | find (const char *label) |
virtual BBItemRef | subscribe (BlackboardAccess *bbClient, BBItemRef bbItem) |
virtual void | unsubscribe (BlackboardAccess *bbClient, BBItemRef bbItem) |
virtual void | registerClient (BlackboardAccess *bbClient) |
virtual void | removeClient (BlackboardAccess *bbClient) |
virtual void | getBlackboardContent (BlackboardAccess *bbClient) |
iterator | begin () |
iterator | end () |
Protected Types | |
typedef std::map< std::string, BBItem * > | ContentsMap |
Protected Member Functions | |
virtual void | initialize () |
virtual void | handleMessage (cMessage *msg) |
Protected Attributes | |
bool | coreDebug |
Set debugging for the basic module. | |
ContentsMap | contents |
SubscriberVector | registeredClients |
Friends | |
class | Iterator |
Classes | |
class | BBItem |
class | iterator |
typedef std::vector<BlackboardAccess *> Blackboard::SubscriberVector |
typedef std::map<std::string, BBItem*> Blackboard::ContentsMap [protected] |
typedef BBItem* Blackboard::BBItemRef |
"Handle" to blackboard items. To get the data or the label, write bbref->getData() and bbref->getLabel(), respectively. BBItemRefs become stale when the item gets withdrawn (unpublished) from the blackboard.
Blackboard::~Blackboard | ( | ) | [virtual] |
void Blackboard::initialize | ( | ) | [protected, virtual] |
void Blackboard::handleMessage | ( | cMessage * | msg | ) | [protected, virtual] |
BBItemRef Blackboard::publish | ( | const char * | label, | |
cPolymorphic * | item | |||
) | [virtual] |
Publish new item on the BB, with the given label.
00064 { 00065 Enter_Method("publish(\"%s\", %s *ptr)", label, item->getClassName()); 00066 00067 // check uniqueness of label 00068 ContentsMap::iterator k = contents.find(std::string(label)); 00069 if (k!=contents.end()) 00070 error("publish(): blackboard already contains an item with label `%s'", label); 00071 00072 // add to BB contents 00073 BBItem *bbitem = new BBItem(); 00074 bbitem->_item = item; 00075 bbitem->_label = label; 00076 contents[bbitem->_label] = bbitem; 00077 00078 coreEV <<"publish "<<label<<" on bb\n"; 00079 coreEV <<"bbItem->label: "<<bbitem->getLabel()<<endl; 00080 // notify 00081 SubscriberVector& vec = registeredClients; 00082 for (SubscriberVector::iterator i=vec.begin(); i!=vec.end(); ++i){ 00083 (*i)->blackboardItemPublished(bbitem); 00084 } 00085 return bbitem; 00086 }
void Blackboard::withdraw | ( | BBItemRef | bbItem | ) | [virtual] |
Withdraw (unpublish) item from the BB (typically called by publisher).
00089 { 00090 Enter_Method("withdraw(\"%s\")", bbItem->getLabel()); 00091 00092 // find on BB 00093 ContentsMap::iterator k = contents.find(bbItem->_label); 00094 if (k==contents.end()) 00095 error("withdraw(): item labelled `%s' is not on clipboard (BBItemRef stale?)", bbItem->_label.c_str()); 00096 00097 // notify subscribers 00098 SubscriberVector& vec = bbItem->subscribers; 00099 for (SubscriberVector::iterator i=vec.begin(); i!=vec.end(); ++i) 00100 (*i)->blackboardItemWithdrawn(bbItem); 00101 00102 // remove 00103 contents.erase(k); 00104 bbItem->_item = NULL; // may make bogus code crash sooner 00105 delete bbItem; 00106 }
void Blackboard::changed | ( | BBItemRef | bbItem, | |
cPolymorphic * | item = NULL | |||
) | [virtual] |
Tell BB that an item has changed (typically called by publisher). When item pointer is omitted, it is assumed that the item object was updated "in place" (as opposed to being replaced by another object).
00109 { 00110 coreEV <<"enter changed; item: "<<bbItem->getLabel()<<" changed -> notify subscribers\n"; 00111 00112 Enter_Method("changed(\"%s\", %s *ptr)", bbItem->getLabel(), item->getClassName()); 00113 00114 // update data pointer 00115 if (item) 00116 bbItem->_item = item; 00117 // notify subscribers 00118 SubscriberVector& vec = bbItem->subscribers; 00119 for (SubscriberVector::iterator i=vec.begin(); i!=vec.end(); ++i){ 00120 (*i)->blackboardItemChanged(bbItem); 00121 } 00122 }
BBItemRef Blackboard::subscribe | ( | BlackboardAccess * | bbClient, | |
const char * | label | |||
) | [virtual] |
Subscribe to a BB item identified by a label
00125 { 00126 Enter_Method("subscribe(this,\"%s\")", label); 00127 00128 // look up item by label 00129 BBItemRef item = find(label); 00130 if (!item) 00131 error("subscribe(): item labelled `%s' not on blackboard", label); 00132 00133 // subscribe 00134 coreEV <<"subscribe for "<<label<<" on bb\n"; 00135 subscribe(bbClient, item); 00136 return item; 00137 }
BBItemRef Blackboard::find | ( | const char * | label | ) | [virtual] |
Find item with given label on the BB
Referenced by subscribe().
00140 { 00141 ContentsMap::iterator k = contents.find(std::string(label)); 00142 return k==contents.end() ? NULL : (*k).second; 00143 }
BBItemRef Blackboard::subscribe | ( | BlackboardAccess * | bbClient, | |
BBItemRef | bbItem | |||
) | [virtual] |
Subscribe to a BB item identified by item reference
00146 { 00147 Enter_Method("subscribe(this,\"%s\")", bbItem->getLabel()); 00148 00149 // check if already subscribed 00150 SubscriberVector& vec = bbItem->subscribers; 00151 if (std::find(vec.begin(), vec.end(), bbClient)!=vec.end()) 00152 return bbItem; // already subscribed 00153 00154 // add subscriber 00155 vec.push_back(bbClient); 00156 00157 coreEV <<"sucessfully subscribed for item: "<<bbItem->getLabel()<<endl; 00158 return bbItem; 00159 }
void Blackboard::unsubscribe | ( | BlackboardAccess * | bbClient, | |
BBItemRef | bbItem | |||
) | [virtual] |
Unsubcribe module from change notifications
00162 { 00163 Enter_Method("unsubscribe(this,\"%s\")", bbItem->getLabel()); 00164 00165 // check if already subscribed 00166 SubscriberVector& vec = bbItem->subscribers; 00167 SubscriberVector::iterator k = std::find(vec.begin(), vec.end(), bbClient); 00168 if (k==vec.end()) 00169 return; // ok, not subscribed 00170 00171 // remove subscriber 00172 vec.erase(k); 00173 }
void Blackboard::registerClient | ( | BlackboardAccess * | bbClient | ) | [virtual] |
Generally subscribe to notifications about items being published to/withdrawn from BB.
00176 { 00177 Enter_Method("registerClient(this)"); 00178 00179 // check if already subscribed 00180 SubscriberVector& vec = registeredClients; 00181 if (std::find(vec.begin(), vec.end(), bbClient)!=vec.end()) 00182 return; // ok, already subscribed 00183 00184 // add subscriber 00185 vec.push_back(bbClient); 00186 }
void Blackboard::removeClient | ( | BlackboardAccess * | bbClient | ) | [virtual] |
Cancel subscription initiated by registerClient().
00189 { 00190 Enter_Method("removeClient(this)"); 00191 00192 // check if subscribed 00193 SubscriberVector& vec = registeredClients; 00194 SubscriberVector::iterator k = std::find(vec.begin(), vec.end(), bbClient); 00195 if (k==vec.end()) 00196 return; // ok, not subscribed 00197 00198 // remove subscriber 00199 vec.erase(k); 00200 }
void Blackboard::getBlackboardContent | ( | BlackboardAccess * | bbClient | ) | [virtual] |
Utility function: the client gets immediate notification with all items currently on clipboard (as if they were added just now). This may simplify initialization code in a subscribe-when-published style client.
00203 { 00204 Enter_Method("getBlackboardContent(this)"); 00205 00206 for (ContentsMap::iterator i=contents.begin(); i!=contents.end(); ++i) 00207 bbClient->blackboardItemPublished((*i).second); 00208 }
iterator Blackboard::begin | ( | ) | [inline] |
iterator Blackboard::end | ( | ) | [inline] |
friend class Iterator [friend] |
bool Blackboard::coreDebug [protected] |
ContentsMap Blackboard::contents [protected] |
Referenced by find(), getBlackboardContent(), initialize(), publish(), withdraw(), and ~Blackboard().
SubscriberVector Blackboard::registeredClients [protected] |
Referenced by publish(), registerClient(), and removeClient().