session.C

00001 /*
00002  * This file is part of the "Archon" framework.
00003  * (http://files3d.sourceforge.net)
00004  *
00005  * Copyright © 2002 by Kristian Spangsege and Brian Kristiansen.
00006  *
00007  * Permission to use, copy, modify, and distribute this software and
00008  * its documentation under the terms of the GNU General Public License is
00009  * hereby granted. No representations are made about the suitability of
00010  * this software for any purpose. It is provided "as is" without express
00011  * or implied warranty. See the GNU General Public License
00012  * (http://www.gnu.org/copyleft/gpl.html) for more details.
00013  *
00014  * The characters in this file are ISO8859-1 encoded.
00015  *
00016  * The documentation in this file is in "Doxygen" style
00017  * (http://www.doxygen.org).
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         // Attempt to contact the server
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         // Create a mapping from Node type IDs to locally known Node
00154         // type indices.
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         // Create a session on the server
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; // Release the server reference
00198 
00199         // Startup the disposer thread
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           // Fetch the root naming context
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 }

Generated on Sun Jul 30 22:55:47 2006 for Archon by  doxygen 1.4.4