00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include <archon/util/cxx_demangle.H>
00021
00022 #include <archon/x3d/proxy/session.H>
00023
00024 #include <archon/x3d/proxy/node.H>
00025 #include <archon/x3d/proxy/application.H>
00026 #include <archon/x3d/proxy/event.H>
00027
00028 #define CORBA_ENTRY_D(x) try { session->x; } \
00029 catch(CORBA::UserException &) { throw; } \
00030 catch(ForwardDestroyedException &) {} \
00031 catch(...) { string m = Proxy::exceptionCatchShortExplanation(); \
00032 throw x3d::sai::ApplicationErrorException(m.c_str()); }
00033
00034 #define CORBA_ENTRY_V(x) try { session->x; } \
00035 catch(CORBA::UserException &) { throw; } \
00036 catch(ForwardDestroyedException &) \
00037 { throw x3d::sai::InvalidApplicationException(); } \
00038 catch(...) { string m = Proxy::exceptionCatchShortExplanation(); \
00039 throw x3d::sai::ApplicationErrorException(m.c_str()); }
00040
00041 #define CORBA_ENTRY_R(x) try { return session->x; } \
00042 catch(CORBA::UserException &) { throw; } \
00043 catch(ForwardDestroyedException &) \
00044 { throw x3d::sai::InvalidApplicationException(); } \
00045 catch(...) { string m = Proxy::exceptionCatchShortExplanation(); \
00046 throw x3d::sai::ApplicationErrorException(m.c_str()); }
00047
00048 namespace Archon
00049 {
00050 namespace X3D
00051 {
00052 namespace Proxy
00053 {
00058 struct Session::Servant:
00059 POA_x3d::sai::Application, PortableServer::RefCountServantBase
00060 {
00061 const BackRef<Session> session;
00062
00063 Servant(Ref<Session> s): session(s) {}
00064
00065 void handleEvent(const x3d::sai::EventSeq &s)
00066 {
00067 CORBA_ENTRY_V(handleEvent(s));
00068 }
00069
00070 x3d::sai::StreamReader_ptr requestFile(const char *path)
00071 {
00072 CORBA_ENTRY_R(requestFile(path));
00073 }
00074
00075 void log(const char *message)
00076 {
00077 CORBA_ENTRY_V(log(message));
00078 }
00079 };
00080
00081
00082 template<typename T>
00083 struct AutoArrayPtr
00084 {
00085 AutoArrayPtr(unsigned long n): p(new T[n]) {}
00086 ~AutoArrayPtr() {{ delete[] p; }}
00087 T *p;
00088 };
00089
00090
00091 Ref<Session> Session::create(string serverName,
00092 int corbaEndpointPort,
00093 Logger *l)
00094 throw(ConnectionException)
00095 {
00096 Ref<Session> s = new Session(serverName, corbaEndpointPort, l);
00097 s->activate();
00098 return s;
00099 }
00100
00101 Session::Session(string serverName, int corbaEndpointPort, Logger *l)
00102 throw(ConnectionException):
00103 logger(l), orb(corbaEndpointPort), active(false)
00104 {
00105
00106 server = getServerReference(serverName);
00107
00108 nodeTypeIdBits = server->getNodeTypeBitsInId();
00109 nodeTypeIdMask = (1 << nodeTypeIdBits) - 1;
00110 nodeTypeIdRoof = 0;
00111
00112 for(unsigned long i=0; i<numberOfNodeTypes; ++i)
00113 {
00114 const NodeDef &d = nodeDefs[i];
00115 NodeLink &l = nodeLinks[i];
00116 try
00117 {
00118 unsigned long ti = server->getNodeType(d.name);
00119 if(ti >= nodeTypeIdRoof) nodeTypeIdRoof = ti+1;
00120 l.id = ti + 1;
00121 }
00122 catch(x3d::sai::InvalidNameException &)
00123 {
00124 logger->log("Server does not support node type '" +
00125 string(d.name) + "'");
00126 continue;
00127 }
00128
00129 if(!d.numberOfFields) continue;
00130
00131 l.fields = new FieldLink[d.numberOfFields];
00132
00133 for(unsigned long j=0; j<d.numberOfFields; ++j)
00134 {
00135 try
00136 {
00137 l.fields[j].id = server->
00138 getImmediateField(l.id-1, d.fields[j].name) + 1;
00139 }
00140 catch(x3d::sai::InvalidNameException &)
00141 {
00142 logger->log("Server does not support field '" +
00143 string(d.name) + "." +
00144 string(d.fields[j].name) + "'");
00145 continue;
00146 }
00147
00148 l.fields[j].id = 0;
00149 }
00150 }
00151
00152
00153
00154
00155 if(nodeTypeIdRoof > 1024)
00156 ARCHON_THROW1(ProtocolException, "Suspiciously many node types: " +
00157 Text::toString(nodeTypeIdRoof));
00158 nodeTypeMap.reset(Array<unsigned long>(nodeTypeIdRoof).out());
00159 for(unsigned long i=0; i<nodeTypeIdRoof; ++i) nodeTypeMap[i] = 0;
00160 for(unsigned long i=0; i<numberOfNodeTypes; ++i)
00161 {
00162 unsigned long id = nodeLinks[i].id;
00163 if(!id) continue;
00164 nodeTypeMap[id-1] = i+1;
00165 }
00166 }
00167
00168 void Session::refForwardDestroy()
00169 {
00170 if(active)
00171 {
00172 orb.getPoa()->deactivate_object(servantId);
00173 disposer->terminate();
00174 disposer.reset();
00175 }
00176 }
00177
00189 void Session::activate()
00190 {
00191
00192 ServantRef<Servant> servant = new Servant(this);
00193 servantId = orb.getPoa()->activate_object(servant.get());
00194 x3d::sai::Application_var ref = servant->_this();
00195 session = server->createSession(ref);
00196 active = true;
00197 server = 0;
00198
00199
00200 disposer = new Disposer(session);
00201 Thread::start(disposer);
00202 }
00203
00204
00205
00206 void Session::beginUpdate()
00207 {
00208 session->beginUpdate();
00209 }
00210
00211 void Session::endUpdate()
00212 {
00213 session->endUpdate();
00214 }
00215
00216
00217
00218 Ref<Application> Session::createApplication(const Uri &u)
00219 {
00220 CORBA::Boolean localFileUri = true;
00221 Ref<ExecutionContext> c =
00222 id2context(session->createApplicationScene(u.toString().c_str(),
00223 localFileUri), true);
00224 return dynamic_cast<Application *>(c.get());
00225 }
00226
00227 Ref<Application> Session::loadApplication(const Uri &u)
00228 {
00229 CORBA::Boolean localFileUri = true;
00230 Ref<ExecutionContext> c =
00231 id2context(session->loadApplicationScene(u.toString().c_str(),
00232 localFileUri), true);
00233 return dynamic_cast<Application *>(c.get());
00234 }
00235
00236 void Session::destroyContext(ExecutionContext *c)
00237 {
00238 unsigned long ci = c->id;
00239 {
00240 Mutex::Lock l(contextMapMutex);
00241 ExecutionContext *&m = contextMap[ci];
00242 if(m == c) m = 0;
00243 }
00244
00245 disposer->addContext(c->id, c->transCount);
00246 }
00247
00251 Ref<ExecutionContext> Session::id2context(unsigned long id, bool isApplication)
00252 {
00253 if(!id) return 0;
00254 if(id > 1024L)
00255 ARCHON_THROW1(ProtocolException,
00256 "Got suspiciously large context ID: " +
00257 Text::toString(id));
00258 Mutex::Lock l(contextMapMutex);
00259 if(contextMap.size() <= id) contextMap.resize(id+1, 0);
00260 ExecutionContext *&m = contextMap[id];
00261 Ref<ExecutionContext> c(m, RefSafeIncTag());
00262 if(!c) c = isApplication ? new Application(this, id) :
00263 new ExecutionContext(this, id);
00264 ++c->transCount;
00265 m = c.get();
00266 return c;
00267 }
00268
00269 unsigned long Session::context2id(Ref<ExecutionContext> c)
00270 {
00271 return c ? c->id : 0;
00272 }
00273
00274
00275
00276
00277 Ref<NodeBase> Session::createNode(ExecutionContext *c, unsigned long i)
00278 {
00279 unsigned long ti = nodeLinks[i].id;
00280 if(!ti)
00281 {
00282 ARCHON_THROW1(SupportException,
00283 "Node type '" + string(nodeDefs[i].name) +
00284 "' is not supported by the server");
00285 return 0;
00286 }
00287 --ti;
00288 return id2node(session->createNodeFast(c->id, ti));
00289 }
00290
00291 void Session::destroyNode(NodeBase *n)
00292 {
00293 unsigned long ni = n->id >> nodeTypeIdBits;
00294 {
00295 Mutex::Lock l(nodeMapMutex);
00296 NodeBase *&m = nodeMap[ni];
00297 if(m == n) m = 0;
00298 }
00299
00300 disposer->addNode(n->id, n->transCount);
00301 }
00302
00318 Ref<NodeBase> Session::id2node(unsigned long id)
00319 throw(IllegalNodeIdException)
00320 {
00321 if(!id) return 0;
00322 unsigned long i = id >> nodeTypeIdBits;
00323 if(i > 1048576UL)
00324 ARCHON_THROW1(ProtocolException,
00325 "Got suspiciously large node index: " +
00326 Text::toString(i));
00327 Mutex::Lock l(nodeMapMutex);
00328 if(nodeMap.size() <= i) nodeMap.resize(i+1, 0);
00329 NodeBase *&m = nodeMap[i];
00330 Ref<NodeBase> n(m, RefSafeIncTag());
00331 if(!n)
00332 {
00333 unsigned long ti = id & nodeTypeIdMask;
00334 if(ti >= nodeTypeIdRoof)
00335 ARCHON_THROW1(ProtocolException,
00336 "Got invalid node type ID from server: " +
00337 Text::toString(ti));
00338 unsigned long i = nodeTypeMap[ti];
00339 if(!i) ARCHON_THROW1(ProtocolException,
00340 "Got invalid node type ID from server: " +
00341 Text::toString(ti));
00342 NodeInstantiator p = nodeDefs[i-1].instantiator;
00343 if(!p) ARCHON_THROW1(ProtocolException,
00344 "Got abstract node type ID from server: " +
00345 Text::toString(ti));
00346 n = (*p)(this, id);
00347 }
00348 ++n->transCount;
00349 m = n.get();
00350 return n;
00351 }
00352
00353 unsigned long Session::node2id(Ref<NodeBase> n)
00354 {
00355 return n ? n->id : 0;
00356 }
00357
00361 void Session::registerFieldInterest(unsigned long ni, unsigned long fi,
00362 EventHandlerBase *h, bool destroy)
00363 {
00364 unsigned long cookie = 0;
00365 {
00366 Mutex::Lock l(eventHandlersMutex);
00367 while(cookie < eventHandlers.size() && eventHandlers[cookie])
00368 ++cookie;
00369 if(cookie == eventHandlers.size()) eventHandlers.resize(cookie+1);
00370 eventHandlers[cookie].reset(h);
00371 }
00372 session->registerFieldInterest(ni, fi, cookie);
00373 }
00374
00375
00376 struct Session::StreamReaderServant:
00377 POA_x3d::sai::StreamReader, PortableServer::RefCountServantBase
00378 {
00379 StreamReaderServant(Ref<Session> s, Ref<Stream::Reader> r):
00380 session(s), reader(r) {}
00381
00382 virtual ~StreamReaderServant()
00383 {
00384 }
00385
00386 x3d::sai::chararray *read(CORBA::Long n)
00387 {
00388 AutoArrayPtr<char> buffer(n);
00389 try
00390 {
00391 int m = reader->readAll(buffer.p, n);
00392 x3d::sai::chararray_var a(new x3d::sai::chararray(m));
00393 a->length(m);
00394 for(int i=0; i<m; ++i) a[i] = buffer.p[i];
00395 return a._retn();
00396 }
00397 catch(Archon::Utilities::Stream::ReadException &e)
00398 {
00399 string m = e.getMessage();
00400 throw x3d::sai::StreamReadException(m.c_str());
00401 }
00402 catch(ThreadTerminatedException &)
00403 {
00404 throw x3d::sai::StreamReadException("Reader was terminated");
00405 }
00406 catch(...)
00407 {
00408 string m = Proxy::exceptionCatchShortExplanation();
00409 throw x3d::sai::StreamReadException(m.c_str());
00410 }
00411 }
00412
00413 void dispose()
00414 {
00415 session->orb.getPoa()->deactivate_object(servantId);
00416 }
00417
00418 Ref<Session> session;
00419 Ref<Stream::Reader> reader;
00420 PortableServer::ObjectId_var servantId;
00421 };
00422
00423
00424 void Session::handleEvent(const x3d::sai::EventSeq &s)
00425 {
00426 for(unsigned i=0; i<s.length(); ++i)
00427 {
00428 const x3d::sai::Event &e = s[i];
00429 if(e.cookie < eventHandlers.size())
00430 {
00431 Mutex::Lock l(eventHandlersMutex);
00432 Ref<EventHandlerBase> h = eventHandlers[e.cookie];
00433 if(h)
00434 {
00435 h->handle(this, e.val);
00436 continue;
00437 }
00438 }
00439 ARCHON_THROW1(InternalException, "Illegal event handler index");
00440 }
00441 }
00442
00443 x3d::sai::StreamReader_ptr Session::requestFile(const char *p)
00444 {
00445 logger->log("Request for file \"" + string(p) + "\"");
00446 try
00447 {
00448 Ref<Stream::Reader> r = Stream::makeFileReader(p);
00449 ServantRef<StreamReaderServant> servant =
00450 new StreamReaderServant(this, r);
00451 servant->servantId = orb.getPoa()->activate_object(servant.get());
00452 x3d::sai::StreamReader_var ref = servant->_this();
00453 return ref._retn();
00454 }
00455 catch(Stream::FileOpenException &e)
00456 {
00457 string m = e.getMessage();
00458 throw x3d::sai::FileRequestException(m.c_str());
00459 }
00460 }
00461
00462 void Session::log(const char *m)
00463 {
00464 logger->log(m);
00465 }
00466
00467
00468
00469 x3d::sai::Server_ptr Session::getServerReference(string name)
00470 throw(ConnectionException)
00471 {
00472 CosNaming::NamingContext_var context;
00473
00474 try
00475 {
00476
00477 CORBA::Object_var obj =
00478 orb.getCorbaOrb()->resolve_initial_references("NameService");
00479 context = CosNaming::NamingContext::_narrow(obj);
00480 if(CORBA::is_nil(context))
00481 ARCHON_THROW1(ConnectionException,
00482 "CORBA naming service is not available.");
00483 }
00484 catch(CORBA::Exception &)
00485 {
00486 ARCHON_THROW1(ConnectionException,
00487 "CORBA naming service is not available.");
00488 }
00489
00490 CORBA::Object_var obj;
00491 try
00492 {
00493 CosNaming::Name path;
00494 path.length(3);
00495 path[0].id = "x3d";
00496 path[1].id = "sai";
00497 path[2].id = name.c_str();
00498 obj = context->resolve(path);
00499 }
00500 catch(CosNaming::NamingContext::NotFound &)
00501 {
00502 ARCHON_THROW1(ConnectionException,
00503 "Server '" + name + "' not found.");
00504 }
00505
00506 x3d::sai::Server_var server;
00507 try
00508 {
00509 if(obj->_non_existent())
00510 ARCHON_THROW1(ConnectionException,
00511 "Server '" + name + "' not found.");
00512 server = x3d::sai::Server::_narrow(obj);
00513 }
00514 catch(CORBA::Exception &)
00515 {
00516 ARCHON_THROW1(ConnectionException,
00517 "Server '" + name + "' not found.");
00518 }
00519 if(CORBA::is_nil(server))
00520 ARCHON_THROW1(ConnectionException,
00521 "Server '" + name + "' not found.");
00522
00523 return server._retn();
00524 }
00525 }
00526 }
00527 }