00001 /* Seventh VOS tutorial. Creating your own MetaObjects 00002 00003 This tutorial covers: 00004 00005 This file (vostut7hello.cc) is released into the public domain. No 00006 restrictions are placed on its use, distribution or inclusion into 00007 other works. 00008 */ 00009 00010 #include "vostut7hello.hh" 00011 00012 00013 // The hello constructor. Nice and simple :-) 00014 00015 Hello::Hello(MetaObject* superobject) 00016 : MetaObject(superobject) 00017 { 00018 } 00019 00020 00021 // Return the VOS type 00022 00023 const string Hello::getType() 00024 { 00025 return "tutorial:hello"; 00026 } 00027 00028 00029 // This will be called by applications wishing to use the "Hello" extension. 00030 00031 void Hello::registerExtenders() 00032 { 00033 /* Registering an extension consists of supplying a type string 00034 and a function to call to construct a new metaobject for that 00035 type. Here we associate the new_LocalHello function for the 00036 abstract class, the local class, and the VOS type name, meaning 00037 the user can call LocalSite::createMetaObject() with any of 00038 these three strings and get an object with the LocalHello extension. 00039 */ 00040 LocalSite::addLocalObjectExtension(typeid(Hello).name(), &LocalHello::new_LocalHello); 00041 LocalSite::addLocalObjectExtension(typeid(LocalHello).name(), &LocalHello::new_LocalHello); 00042 LocalSite::addLocalObjectExtension("tutorial:hello", &LocalHello::new_LocalHello); 00043 00044 /* Note that for remote objects the VOS type name is extremely 00045 important: it is the VOS type name we get from the network, and 00046 we use it to automatically instantiate the correct extensions 00047 for the newly discovered object. 00048 */ 00049 RemoteSite::addRemoteObjectExtension(typeid(Hello).name(), &RemoteHello::new_RemoteHello); 00050 RemoteSite::addRemoteObjectExtension(typeid(RemoteHello).name(), &RemoteHello::new_RemoteHello); 00051 RemoteSite::addRemoteObjectExtension("tutorial:hello", &RemoteHello::new_RemoteHello); 00052 } 00053 00054 00055 // That's it for the base class! 00056 00057 00058 // Now for RemoteHello. 00059 00060 // Now the constructor for LocalHello. 00061 00062 RemoteHello::RemoteHello(MetaObject* superobject) 00063 : Hello(superobject) 00064 { } 00065 00066 00067 // The client stub implementation of hello. 00068 00069 string RemoteHello::hello(const string& s) 00070 { 00071 // will hold return value 00072 string ret; 00073 00074 vRef<Message> m = new Message(); 00075 00076 /* RemoteVobject::initFields is a convenience function to 00077 initialize some of the fields on the message, including 00078 generating a nonce. A "nonce" is a unique identifier 00079 associated with this message that will be used to match the 00080 reply message (which will bear the same nonce). This function 00081 also returns the local site which is peered to this remote 00082 site, which we need if we want to wait for a reply. 00083 */ 00084 vRef<LocalSite> ls = RemoteVobject::initFields(this, &m, "tutorial:hello", true); 00085 00086 m->insertField(-1, "word", s); 00087 00088 /* Send it off. Note: since 'this' is a remote object, by sending 00089 a message to 'this' we are actualy causing the message to be 00090 delivered to the actual vobject over the network. 00091 */ 00092 sendMessage(&m); 00093 00094 00095 // get our (remote) site 00096 vRef<RemoteSite> site = dynamic_cast<RemoteSite&>(getSite()); 00097 00098 /* The waitFor() method will wait until either a message is 00099 received on our local site from the supplied remote site 00100 bearing the given nonce, or time runs out. It returns the 00101 message that matches the nonce, or throws a TimeoutError if we 00102 ran out of time. In other words, the purpose of this method is 00103 for "waiting for" a reply to a message which we have just sent. 00104 */ 00105 vRef<Message> n = ls->waitFor(m->getNonce(), &site); 00106 00107 try { 00108 00109 // We got a reply back. Extract the answer from the "text" field. 00110 ret = n->getField("word").value; 00111 00112 } catch(Message::NoSuchFieldError) { 00113 00114 // Whoops. There wasn't a "text" field. 00115 // We could perhaps throw an exception here. 00116 00117 ret = "***site did not return a text field***"; 00118 } 00119 00120 // return our results. 00121 return ret; 00122 } 00123 00124 00125 // Constructs a RemoteHello object and returns it. This is a hack 00126 // necessary since you can't take the address of a constructor and 00127 // call it like a normal function or method pointer. 00128 00129 MetaObject* RemoteHello::new_RemoteHello(MetaObject* superobject, const string& type) 00130 { 00131 return new RemoteHello(superobject); 00132 } 00133 00134 00135 // All done with RemoteHello! 00136 00137 00138 // Now the constructor for LocalHello. 00139 00140 LocalHello::LocalHello(MetaObject* superobject) 00141 : Hello(superobject) 00142 { 00143 /* Here we add a method to be called when this Vobject receives a 00144 message with specific method field. The addMessageHandler 00145 method associates the message method ("tutorial:hello") with a 00146 C++ method to call (&LocalHello::handleHello) and a particular 00147 object to call in on (this). It is a template method to keep 00148 C++'s type checking happy. 00149 */ 00150 addMessageHandler<LocalHello>("tutorial:hello", this, &LocalHello::handleHello); 00151 } 00152 00153 00154 // The actual hello method. Yes, all the trouble just to access this 00155 // method over a network :-) 00156 00157 string LocalHello::hello(const string& s) 00158 { 00159 return ("Hello there! You said \"" + s + "\""); 00160 } 00161 00162 00163 00164 // This is the handler method registered with addMessageHandler() and 00165 // called by VOS when a message bearing the method "tutorial:hello" is 00166 // received by this object. 00167 00168 void LocalHello::handleHello(Message* m) 00169 { 00170 // Somebody has said hello to us. We will act upon the message 00171 // and send a reply. 00172 00173 vRef<Message> reply = new Message(); 00174 00175 // LocalVobject has a convenience method which will set 00176 // up the reply for us, such as initializing with the 00177 // correct nonce. See API documentation for details. 00178 00179 LocalVobject::initReply(this, &reply, m, "tutorial:hello-reply"); 00180 00181 00182 try { 00183 // Now call hello() with the field called "word". 00184 00185 string s = hello(m->getField("word").value); 00186 00187 00188 // The result goes into a field called "word" in the reply. 00189 00190 reply->insertField(-1, "word", s); 00191 00192 } catch(Message::NoSuchFieldError) { 00193 00194 // oops! The message we got didn't have the field we were looking for. 00195 reply->insertField(-1, "error", "need a field \"word\""); 00196 00197 } 00198 00199 try { 00200 // Get a handle on the object that sent this message to us 00201 // and send it the reply. 00202 00203 vRef<Vobject> v = findObject(m->getFrom()); 00204 00205 v->sendMessage(&reply); 00206 00207 } catch(NoSuchSiteError) { 00208 } catch(NoSuchObjectError) { 00209 } catch(AccessControlError) { 00210 } catch(RemoteError) { 00211 } catch(URL::BadURLError) { 00212 } 00213 } 00214 00215 00216 // Constructs a LocalHello object and returns it. This is a hack 00217 // necessary since you can't take the address of a constructor and 00218 // call it like a normal function or method pointer. 00219 00220 MetaObject* LocalHello::new_LocalHello(MetaObject* superobject, const string& type) 00221 { 00222 return new LocalHello(superobject); 00223 }