00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
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
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
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
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
00188
00189
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
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
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
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
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 }