load.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 <math.h>
00021 
00022 #include <string>
00023 #include <vector>
00024 
00025 #include <archon/util/text.H>
00026 #include <archon/util/stream.H>
00027 #include <archon/util/pipe.H>
00028 #include <archon/util/image.H>
00029 
00030 //#include <archon/x3d/server/parse_vrml.H>
00031 #include <archon/x3d/server/parse_xml.H>
00032 
00033 #include <archon/x3d/server/load.H>
00034 #include <archon/x3d/server/server.H>
00035 
00036 using namespace std;
00037 
00038 namespace Archon
00039 {
00040   using namespace Math;
00041   using namespace Utilities;
00042 
00043   namespace X3D
00044   {
00045     namespace
00046     {
00047       long sourceNameCounter(string sourceName)
00048       {
00049         static map<string, long> m;
00050         return m[sourceName]++;
00051       }
00052 
00053       struct PrefixLogger: Utilities::Logger
00054       {
00055         string prefix;
00056         Logger *logger;
00057         PrefixLogger(string prefix, Logger *logger):
00058           prefix(prefix), logger(logger) {}
00059 
00060         void log(string message) throw()
00061         {
00062           logger->log(prefix + message);
00063         }
00064 
00065         virtual ~PrefixLogger()
00066         {
00067         }
00068       };
00069     }
00070 
00071     struct ImageLoadTracker: Image::ProgressTracker
00072     {
00073       void progress(double fraction) throw()
00074       {
00075         //progressTracker->progress(chars_loaded);
00076       }
00077     };
00078 
00079     Loader::Job::Job(Ref<Loader> loader, Uri baseUri,
00080                      const vector<string> &uriList, UriType uriType,
00081                      Ref<AbstractFileServer> fileServer):
00082       loader(loader), webClient(loader->webClient), baseUri(baseUri),
00083       uriList(uriList), uriType(uriType), completed(false),
00084       fileServer(fileServer)
00085     {
00086     }
00087 
00088     Loader::Job::~Job()
00089     {
00090     }
00091 
00092     void Loader::Job::wait()
00093     {
00094       Mutex::Lock l(loader->mutex);
00095       while(!completed) loader->jobCompletion.wait();
00096     }
00097 
00098     void Loader::Job::kill()
00099     {
00100       Mutex::Lock l(loader->mutex);
00101       loader->jobQueue.cancel(this);
00102     }
00103 
00104     Ref<const Loader::Contents> Loader::Job::getContents()
00105     {
00106       Mutex::Lock l(loader->mutex);
00107       return contents;
00108     }
00109 
00110     Ref<const Loader::Contents>
00111     Loader::Job::loadX3D(Ref<Stream::Reader> reader, string contentType,
00112                          Uri sourceUri, int sourceLine, Logger *logger)
00113     {
00114       Ref<Server> server;
00115       try { server = loader->server.getRef(); }
00116       catch(ForwardDestroyedException &) { return 0; }
00117       bool progressive = server->getProgressiveLoad();
00118       Ref<Scene> rootScene = new Scene(server, sourceUri);
00119       Ref<Group> rootGroup = new Group(Ref<ExecutionContext>(rootScene));
00120       XML::parse(rootScene, rootGroup, reader, progressive);
00121       rootScene->setRootGroup(rootGroup);
00122       return new X3DContents(rootScene);
00123     }
00124 
00125     Ref<const Loader::Contents>
00126     Loader::Job::loadImage(Ref<Stream::Reader> reader, string contentType,
00127                            Uri sourceUri, Logger *logger)
00128     {
00129       Ref<ImageContents> c = new ImageContents;
00130       // progressTracker->setInitialContents(c);
00131       c->image.load(reader, sourceUri.getFile(), "", 0, logger);
00132       return c;
00133     }
00134 
00135     Ref<const Loader::Contents>
00136     Loader::Job::loadMovie(Ref<Stream::Reader> reader, string contentType,
00137                            Uri sourceUri, Logger *logger)
00138     {
00139       return new MovieContents;
00140     }
00141 
00142     Ref<const Loader::Contents>
00143     Loader::Job::loadScript(Ref<Stream::Reader> reader, string contentType,
00144                             Uri sourceUri, int sourceLine, Logger *logger)
00145     {
00146       string s = Stream::readIntoString(reader);
00147       return new ScriptContents("ecmascript", string(s), sourceUri.getFile(),
00148                                 sourceLine);
00149     }
00150 
00151     Ref<const Loader::Contents>
00152     Loader::Job::load(Ref<Stream::Reader> reader, string contentType,
00153                       Uri sourceUri, int sourceLine, Logger *logger)
00154     {
00155       switch(uriType)
00156       {
00157       case uriType_X3D:
00158         return loadX3D(reader, contentType, sourceUri, sourceLine, logger);
00159       case uriType_Image:
00160         return loadImage(reader, contentType, sourceUri, logger);
00161       case uriType_Movie:
00162         return loadMovie(reader, contentType, sourceUri, logger);
00163       case uriType_Script:
00164         return loadScript(reader, contentType, sourceUri, sourceLine, logger);
00165       default:
00166         ARCHON_THROW1(InternalException,
00167                       "X3D::Loader::Job: Unexpected Uri type");
00168       }
00169     }
00170 
00180     void Loader::Job::main()
00181     {
00182       for(unsigned i=0; i<uriList.size(); ++i)
00183       {
00184         string s = uriList[i];
00185 
00186         /*
00187          * Intercept the "ecmascript:" / "javascript:" (deprecated) URI
00188          * schemes.
00189          * Note that these inlined schemes are non-cachable.
00190          */
00191         const string::size_type p = s.find(':');
00192         if(p != string::npos)
00193         {
00194           string scheme(s, 0, p);
00195           if(Text::compareIgnoreCase(scheme, "ecmascript") == 0 ||
00196              Text::compareIgnoreCase(scheme, "javascript") == 0)
00197           {
00198             static unsigned long counter = 0;
00199 
00200             PrefixLogger
00201               logger("[inlined ECMAScript]" +
00202                      string(counter ? "[" + Text::toString(counter) +
00203                             "]" : "") + ": ", loader->logger);
00204             ++counter;
00205 
00206             try
00207             {
00208               Ref<Stream::Reader> reader = Stream::makeStringReader(s.substr(p+1));
00209               Ref<const Contents> c =
00210                 load(reader, "text/ecmascript", Uri(), -1, &logger);
00211               reader.reset();
00212 
00213               Mutex::Lock l(loader->mutex);
00214               contents = c;
00215               break;
00216             }
00217             catch(InternalException &)
00218             {
00219               throw;
00220             }
00221             catch(ThreadTerminatedException &)
00222             {
00223               logger.log("Loading canceled");
00224               break;
00225             }
00226             catch(Image::UnknownFormatException &)
00227             {
00228               logger.log("Unknown image format");
00229             }
00230             catch(...)
00231             {
00232               logger.log("Loading failed");
00233             }
00234             continue;
00235           }
00236         }
00237 
00238 
00239         Uri uri;
00240 
00241         try
00242         {
00243           uri = Uri(s, baseUri);
00244         }
00245         catch(Uri::SyntaxException &e)
00246         {
00247           loader->logger->log("Malformed URI:\n" + Uri::explain(e));
00248           continue;
00249         }
00250 
00251         string absoluteUriString = uri.toString();
00252         bool cachable = uriType != uriType_X3D;
00253 
00254         // Consult the cache
00255         if(cachable)
00256         {
00257           if(uri.isFileScheme() && fileServer)
00258             absoluteUriString = "archon://" +
00259               Text::toString(fileServer->getFileServerId()) +
00260               uri.getPath();
00261 
00262           Mutex::Lock l(loader->mutex);
00263           for(;;)
00264           {
00265             map<string, Ref<const Contents> >::iterator i =
00266               loader->cache.find(absoluteUriString);
00267             if(i == loader->cache.end()) break;
00268             contents = i->second;
00269             if(contents) break;
00270             loader->cacheChange.wait();
00271           }
00272           if(contents) break;
00273 
00274           // No hit so allocate a new cache entry
00275           loader->cache[absoluteUriString];
00276         }
00277 
00278         string sourceName = uri.getFile();
00279         int counter = sourceNameCounter(sourceName);
00280         PrefixLogger
00281           logger(sourceName + (counter ? "[" + Text::toString(counter) +
00282                                "]" : "") + ": ", loader->logger);
00283 
00284         bool quit = false;
00285         try
00286         {
00287           Ref<Stream::Reader> reader;
00288           string contentType;
00289 
00290           if(uri.isFileScheme() && fileServer)
00291           {
00292             string path = uri.getPath();
00293             logger.log("Requesting: " + absoluteUriString);
00294             reader = fileServer->request(path);
00295             logger.log("Found");
00296           }
00297           else
00298           {
00299             logger.log("Requesting: " + absoluteUriString);
00300             Ref<Web::Client::Response> response =
00301               webClient->request(Web::Client::Request::get(uri));
00302             contentType = response->getContentType();
00303             logger.log("Found: " + contentType);
00304             reader = response;
00305           }
00306           Ref<const Contents> c = load(reader, contentType, uri, 1, &logger);
00307           reader.reset();
00308           logger.log("Loaded");
00309 
00310           {
00311             Mutex::Lock l(loader->mutex);
00312             contents = c;
00313             if(cachable) loader->cache[absoluteUriString] = c;
00314           }
00315           if(cachable) loader->cacheChange.notifyAll();
00316           break;
00317         }
00318         catch(InternalException &)
00319         {
00320           throw;
00321         }
00322         catch(ThreadTerminatedException &)
00323         {
00324           logger.log("Loading canceled");
00325           quit = true;
00326         }
00327         catch(Image::UnknownFormatException &)
00328         {
00329           logger.log("Unknown image format");
00330         }
00331         catch(AbstractFileServer::RequestException &e)
00332         {
00333           logger.log("Loading failed: " + e.getMessage());
00334         }
00335         catch(Web::RequestException &e)
00336         {
00337           logger.log("Loading failed: " + e.getMessage());
00338         }
00339         catch(...)
00340         {
00341           logger.log("Loading failed");
00342         }
00343 
00344         // Remove cache entry for faulty URI
00345         if(cachable)
00346         {
00347           {
00348             Mutex::Lock l(loader->mutex);
00349             loader->cache.erase(absoluteUriString);
00350           }
00351           loader->cacheChange.notifyAll();
00352         }
00353         if(quit) break;
00354       }
00355 
00356       // Signal completion
00357       onCompletion();
00358       completed = true;
00359       loader->jobCompletion.notifyAll();
00360     }
00361 
00362 
00363     Loader::Loader(Ref<Server> server, Ref<Web::Client> webClient):
00364       server(server),
00365       logger(server->getLogger()),
00366       webClient(webClient),
00367       jobQueue(5, Time(30l)),
00368       cacheChange(mutex), jobCompletion(mutex)
00369     {
00370     }
00371 
00372     Loader::~Loader()
00373     {
00374       wait();
00375     }
00376 
00377     void Loader::submitJob(Ref<Job> j)
00378     {
00379       jobQueue.add(j.get());
00380     }
00381   }
00382 }

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