Back to index

nordugrid-arc-nox  1.1.0~rc6
TargetRetrieverARC1.cpp
Go to the documentation of this file.
00001 // -*- indent-tabs-mode: nil -*-
00002 
00003 #ifdef HAVE_CONFIG_H
00004 #include <config.h>
00005 #endif
00006 
00007 #include <arc/ArcConfig.h>
00008 #include <arc/Logger.h>
00009 #include <arc/StringConv.h>
00010 #include <arc/Thread.h>
00011 #include <arc/URL.h>
00012 #include <arc/XMLNode.h>
00013 #include <arc/client/ExecutionTarget.h>
00014 #include <arc/client/TargetGenerator.h>
00015 #include <arc/message/MCC.h>
00016 #include <arc/data/DataHandle.h>
00017 
00018 #include "AREXClient.h"
00019 #include "TargetRetrieverARC1.h"
00020 
00021 namespace Arc {
00022 
00023   struct ThreadArg {
00024     TargetGenerator *mom;
00025     const UserConfig *usercfg;
00026     URL url;
00027     int targetType;
00028     int detailLevel;
00029   };
00030 
00031   ThreadArg* TargetRetrieverARC1::CreateThreadArg(TargetGenerator& mom,
00032                                                   int targetType,
00033                                                   int detailLevel) {
00034     ThreadArg *arg = new ThreadArg;
00035     arg->mom = &mom;
00036     arg->usercfg = &usercfg;
00037     arg->url = url;
00038     arg->targetType = targetType;
00039     arg->detailLevel = detailLevel;
00040     return arg;
00041   }
00042 
00043   Logger TargetRetrieverARC1::logger(TargetRetriever::logger, "ARC1");
00044 
00045   TargetRetrieverARC1::TargetRetrieverARC1(const UserConfig& usercfg,
00046                                            const URL& url, ServiceType st)
00047     : TargetRetriever(usercfg, url, st, "ARC1") {}
00048 
00049   TargetRetrieverARC1::~TargetRetrieverARC1() {}
00050 
00051   Plugin* TargetRetrieverARC1::Instance(PluginArgument *arg) {
00052     TargetRetrieverPluginArgument *trarg =
00053       dynamic_cast<TargetRetrieverPluginArgument*>(arg);
00054     if (!trarg)
00055       return NULL;
00056     return new TargetRetrieverARC1(*trarg, *trarg, *trarg);
00057   }
00058 
00059   void TargetRetrieverARC1::GetTargets(TargetGenerator& mom, int targetType,
00060                                        int detailLevel) {
00061 
00062     logger.msg(VERBOSE, "TargetRetriverARC1 initialized with %s service url: %s",
00063                tostring(serviceType), url.str());
00064 
00065     switch (serviceType) {
00066     case COMPUTING:
00067       if (mom.AddService(url)) {
00068         ThreadArg *arg = CreateThreadArg(mom, targetType, detailLevel);
00069         if (!CreateThreadFunction(&InterrogateTarget, arg)) {
00070           delete arg;
00071           mom.RetrieverDone();
00072         }
00073       }
00074       break;
00075     case INDEX:
00076       if (mom.AddIndexServer(url)) {
00077         ThreadArg *arg = CreateThreadArg(mom, targetType, detailLevel);
00078         if (!CreateThreadFunction(&QueryIndex, arg)) {
00079           delete arg;
00080           mom.RetrieverDone();
00081         }
00082       }
00083       break;
00084     }
00085   }
00086 
00087   void TargetRetrieverARC1::QueryIndex(void *arg) {
00088     ThreadArg *thrarg = (ThreadArg*)arg;
00089     TargetGenerator& mom = *thrarg->mom;
00090     const UserConfig& usercfg = *thrarg->usercfg;
00091     URL& url = thrarg->url;
00092     MCCConfig cfg;
00093     usercfg.ApplyToConfig(cfg);
00094     AREXClient ac(url, cfg, usercfg.Timeout());
00095     std::list< std::pair<URL, ServiceType> > services;
00096     if (!ac.listServicesFromISIS(services)) {
00097       delete thrarg;
00098       mom.RetrieverDone();
00099       return;
00100     }
00101     logger.msg(VERBOSE, "Found %u execution services from the index service at %s", services.size(), url.str());
00102 
00103     for (std::list< std::pair<URL, ServiceType> >::iterator it = services.begin(); it != services.end(); it++) {
00104       TargetRetrieverARC1 r(usercfg, it->first, it->second);
00105       r.GetTargets(mom, thrarg->targetType, thrarg->detailLevel);
00106     }
00107 
00108     delete thrarg;
00109     mom.RetrieverDone();
00110   }
00111 
00112   void TargetRetrieverARC1::InterrogateTarget(void *arg) {
00113     ThreadArg *thrarg = (ThreadArg*)arg;
00114     TargetGenerator& mom = *thrarg->mom;
00115     const UserConfig& usercfg = *thrarg->usercfg;
00116     int targetType = thrarg->targetType;
00117     URL& url = thrarg->url;
00118 
00119     if (targetType == 0) {
00120       MCCConfig cfg;
00121       usercfg.ApplyToConfig(cfg);
00122       AREXClient ac(url, cfg, usercfg.Timeout());
00123       XMLNode servicesQueryResponse;
00124       if (!ac.sstat(servicesQueryResponse)) {
00125         delete thrarg;
00126         mom.RetrieverDone();
00127         return;
00128       }
00129 
00130       for (XMLNode GLUEService = servicesQueryResponse["ComputingService"];
00131            GLUEService; ++GLUEService) {
00132         ExecutionTarget target;
00133 
00134         target.GridFlavour = "ARC1";
00135         target.Cluster = thrarg->url;
00136         target.url = url;
00137         target.InterfaceName = "BES";
00138         target.Implementor = "NorduGrid";
00139 
00140         target.DomainName = url.Host();
00141 
00142         logger.msg(VERBOSE, "Generating A-REX target: %s", target.Cluster.str());
00143 
00144         XMLNode ComputingEndpoint = GLUEService["ComputingEndpoint"];
00145         if (ComputingEndpoint["HealthState"])
00146           target.HealthState = (std::string)ComputingEndpoint["HealthState"];
00147         else
00148           logger.msg(VERBOSE, "The Service advertises no Health State.");
00149         if (ComputingEndpoint["HealthStateInfo"])
00150           target.HealthState = (std::string)ComputingEndpoint["HealthStateInfo"];
00151         if (GLUEService["Name"])
00152           target.ServiceName = (std::string)GLUEService["Name"];
00153         if (ComputingEndpoint["Capability"])
00154           for (XMLNode n = ComputingEndpoint["Capability"]; n; ++n)
00155             target.Capability.push_back((std::string)n);
00156         else if (GLUEService["Capability"])
00157           for (XMLNode n = GLUEService["Capability"]; n; ++n)
00158             target.Capability.push_back((std::string)n);
00159         if (GLUEService["Type"])
00160           target.ServiceType = (std::string)GLUEService["Type"];
00161         else
00162           logger.msg(VERBOSE, "The Service doesn't advertise its Type.");
00163         if (ComputingEndpoint["QualityLevel"])
00164           target.QualityLevel = (std::string)ComputingEndpoint["QualityLevel"];
00165         else if (GLUEService["QualityLevel"])
00166           target.QualityLevel = (std::string)GLUEService["QualityLevel"];
00167         else
00168           logger.msg(VERBOSE, "The Service doesn't advertise its Quality Level.");
00169 
00170         if (ComputingEndpoint["Technology"])
00171           target.Technology = (std::string)ComputingEndpoint["Technology"];
00172         if (ComputingEndpoint["InterfaceName"])
00173           target.InterfaceName = (std::string)ComputingEndpoint["InterfaceName"];
00174         else if (ComputingEndpoint["Interface"])
00175           target.InterfaceName = (std::string)ComputingEndpoint["Interface"];
00176         else
00177           logger.msg(VERBOSE, "The Service doesn't advertise its Interface.");
00178         if (ComputingEndpoint["InterfaceVersion"])
00179           target.InterfaceName = (std::string)ComputingEndpoint["InterfaceVersion"];
00180         if (ComputingEndpoint["InterfaceExtension"])
00181           for (XMLNode n = ComputingEndpoint["InterfaceExtension"]; n; ++n)
00182             target.InterfaceExtension.push_back((std::string)n);
00183         if (ComputingEndpoint["SupportedProfile"])
00184           for (XMLNode n = ComputingEndpoint["SupportedProfile"]; n; ++n)
00185             target.SupportedProfile.push_back((std::string)n);
00186         if (ComputingEndpoint["Implementor"])
00187           target.Implementor = (std::string)ComputingEndpoint["Implementor"];
00188         if (ComputingEndpoint["ImplementationName"])
00189           if (ComputingEndpoint["ImplementationVersion"])
00190             target.Implementation =
00191               Software((std::string)ComputingEndpoint["ImplementationName"],
00192                        (std::string)ComputingEndpoint["ImplementationVersion"]);
00193           else
00194             target.Implementation = Software((std::string)ComputingEndpoint["ImplementationName"]);
00195         if (ComputingEndpoint["ServingState"])
00196           target.ServingState = (std::string)ComputingEndpoint["ServingState"];
00197         else
00198           logger.msg(VERBOSE, "The Service doesn't advertise its Serving State.");
00199         if (ComputingEndpoint["IssuerCA"])
00200           target.IssuerCA = (std::string)ComputingEndpoint["IssuerCA"];
00201         if (ComputingEndpoint["TrustedCA"]) {
00202           XMLNode n = ComputingEndpoint["TrustedCA"];
00203           while (n) {
00204             target.TrustedCA.push_back((std::string)n);
00205             ++n; //The increment operator works in an unusual manner (returns void)
00206           }
00207         }
00208         if (ComputingEndpoint["DowntimeStart"])
00209           target.DowntimeStarts = (std::string)ComputingEndpoint["DowntimeStart"];
00210         if (ComputingEndpoint["DowntimeEnd"])
00211           target.DowntimeEnds = (std::string)ComputingEndpoint["DowntimeEnd"];
00212         if (ComputingEndpoint["Staging"])
00213           target.Staging = (std::string)ComputingEndpoint["Staging"];
00214         if (ComputingEndpoint["JobDescription"])
00215           for (XMLNode n = ComputingEndpoint["JobDescription"]; n; ++n)
00216             target.JobDescriptions.push_back((std::string)n);
00217 
00218         //Attributes below should possibly consider elements in different places (Service/Endpoint/Share etc).
00219         if (ComputingEndpoint["TotalJobs"])
00220           target.TotalJobs = stringtoi((std::string)ComputingEndpoint["TotalJobs"]);
00221         else if (GLUEService["TotalJobs"])
00222           target.TotalJobs = stringtoi((std::string)GLUEService["TotalJobs"]);
00223         if (ComputingEndpoint["RunningJobs"])
00224           target.RunningJobs = stringtoi((std::string)ComputingEndpoint["RunningJobs"]);
00225         else if (GLUEService["RunningJobs"])
00226           target.RunningJobs = stringtoi((std::string)GLUEService["RunningJobs"]);
00227         if (ComputingEndpoint["WaitingJobs"])
00228           target.WaitingJobs = stringtoi((std::string)ComputingEndpoint["WaitingJobs"]);
00229         else if (GLUEService["WaitingJobs"])
00230           target.WaitingJobs = stringtoi((std::string)GLUEService["WaitingJobs"]);
00231         if (ComputingEndpoint["StagingJobs"])
00232           target.StagingJobs = stringtoi((std::string)ComputingEndpoint["StagingJobs"]);
00233         else if (GLUEService["StagingJobs"])
00234           target.StagingJobs = stringtoi((std::string)GLUEService["StagingJobs"]);
00235         if (ComputingEndpoint["SuspendedJobs"])
00236           target.SuspendedJobs = stringtoi((std::string)ComputingEndpoint["SuspendedJobs"]);
00237         else if (GLUEService["SuspendedJobs"])
00238           target.SuspendedJobs = stringtoi((std::string)GLUEService["SuspendedJobs"]);
00239         if (ComputingEndpoint["PreLRMSWaitingJobs"])
00240           target.PreLRMSWaitingJobs = stringtoi((std::string)ComputingEndpoint["PreLRMSWaitingJobs"]);
00241         else if (GLUEService["PreLRMSWaitingJobs"])
00242           target.PreLRMSWaitingJobs = stringtoi((std::string)GLUEService["PreLRMSWaitingJobs"]);
00243         if (ComputingEndpoint["LocalRunningJobs"])
00244           target.LocalRunningJobs = stringtoi((std::string)ComputingEndpoint["LocalRunningJobs"]);
00245         else if (GLUEService["LocalRunningJobs"])
00246           target.LocalRunningJobs = stringtoi((std::string)GLUEService["LocalRunningJobs"]);
00247         if (ComputingEndpoint["LocalWaitingJobs"])
00248           target.LocalWaitingJobs = stringtoi((std::string)ComputingEndpoint["LocalWaitingJobs"]);
00249         else if (GLUEService["LocalWaitingJobs"])
00250           target.LocalWaitingJobs = stringtoi((std::string)GLUEService["LocalWaitingJobs"]);
00251         if (ComputingEndpoint["LocalSuspendedJobs"])
00252           target.LocalSuspendedJobs = stringtoi((std::string)ComputingEndpoint["LocalSuspendedJobs"]);
00253         else if (GLUEService["LocalSuspendedJobs"])
00254           target.LocalWaitingJobs = stringtoi((std::string)GLUEService["LocalSuspendedJobs"]);
00255 
00256         /*
00257          * The following target attributes might be problematic since there might be many shares and
00258          * the relevant one is ill defined. Only the first share is currently stored.
00259          */
00260         XMLNode ComputingShare = GLUEService["ComputingShares"]["ComputingShare"];
00261         if (ComputingShare["FreeSlots"])
00262           target.FreeSlots = stringtoi((std::string)ComputingShare["FreeSlots"]);
00263         if (ComputingShare["FreeSlotsWithDuration"]) {
00264           std::string value = (std::string)ComputingShare["FreeSlotsWithDuration"];
00265           std::string::size_type pos = 0;
00266           do {
00267             std::string::size_type spacepos = value.find(' ', pos);
00268             std::string entry;
00269             if (spacepos == std::string::npos)
00270               entry = value.substr(pos);
00271             else
00272               entry = value.substr(pos, spacepos - pos);
00273             int num_cpus;
00274             Period time;
00275             std::string::size_type colonpos = entry.find(':');
00276             if (colonpos == std::string::npos) {
00277               num_cpus = stringtoi(entry);
00278               time = LONG_MAX;
00279             }
00280             else {
00281               num_cpus = stringtoi(entry.substr(0, colonpos));
00282               time = stringtoi(entry.substr(colonpos + 1)) * 60;
00283             }
00284             target.FreeSlotsWithDuration[time] = num_cpus;
00285             pos = spacepos;
00286             if (pos != std::string::npos)
00287               pos++;
00288           } while (pos != std::string::npos);
00289         }
00290         if (ComputingShare["UsedSlots"])
00291           target.UsedSlots = stringtoi((std::string)ComputingShare["UsedSlots"]);
00292         if (ComputingShare["RequestedSlots"])
00293           target.RequestedSlots = stringtoi((std::string)ComputingShare["RequestedSlots"]);
00294         if (ComputingShare["Name"])
00295           target.ComputingShareName = (std::string)ComputingShare["Name"];
00296         if (ComputingShare["MaxWallTime"])
00297           target.MaxWallTime = (std::string)ComputingShare["MaxWallTime"];
00298         if (ComputingShare["MaxTotalWallTime"])
00299           target.MaxTotalWallTime = (std::string)ComputingShare["MaxTotalWallTime"];
00300         if (ComputingShare["MinWallTime"])
00301           target.MinWallTime = (std::string)ComputingShare["MinWallTime"];
00302         if (ComputingShare["DefaultWallTime"])
00303           target.DefaultWallTime = (std::string)ComputingShare["DefaultWallTime"];
00304         if (ComputingShare["MaxCPUTime"])
00305           target.MaxCPUTime = (std::string)ComputingShare["MaxCPUTime"];
00306         if (ComputingShare["MaxTotalCPUTime"])
00307           target.MaxTotalCPUTime = (std::string)ComputingShare["MaxTotalCPUTime"];
00308         if (ComputingShare["MinCPUTime"])
00309           target.MinCPUTime = (std::string)ComputingShare["MinCPUTime"];
00310         if (ComputingShare["DefaultCPUTime"])
00311           target.DefaultCPUTime = (std::string)ComputingShare["DefaultCPUTime"];
00312         if (ComputingShare["MaxTotalJobs"])
00313           target.MaxTotalJobs = stringtoi((std::string)ComputingShare["MaxTotalJobs"]);
00314         if (ComputingShare["MaxRunningJobs"])
00315           target.MaxRunningJobs = stringtoi((std::string)ComputingShare["MaxRunningJobs"]);
00316         if (ComputingShare["MaxWaitingJobs"])
00317           target.MaxWaitingJobs = stringtoi((std::string)ComputingShare["MaxWaitingJobs"]);
00318         if (ComputingShare["MaxPreLRMSWaitingJobs"])
00319           target.MaxPreLRMSWaitingJobs = stringtoi((std::string)ComputingShare["MaxPreLRMSWaitingJobs"]);
00320         if (ComputingShare["MaxUserRunningJobs"])
00321           target.MaxUserRunningJobs = stringtoi((std::string)ComputingShare["MaxUserRunningJobs"]);
00322         if (ComputingShare["MaxSlotsPerJob"])
00323           target.MaxSlotsPerJob = stringtoi((std::string)ComputingShare["MaxSlotsPerJob"]);
00324         if (ComputingShare["MaxStageInStreams"])
00325           target.MaxStageInStreams = stringtoi((std::string)ComputingShare["MaxStageInStreams"]);
00326         if (ComputingShare["MaxStageOutStreams"])
00327           target.MaxStageOutStreams = stringtoi((std::string)ComputingShare["MaxStageOutStreams"]);
00328         if (ComputingShare["SchedulingPolicy"])
00329           target.SchedulingPolicy = (std::string)ComputingShare["SchedulingPolicy"];
00330         if (ComputingShare["MaxMainMemory"])
00331           target.MaxMainMemory = stringtoi((std::string)ComputingShare["MaxMainMemory"]);
00332         if (ComputingShare["MaxVirtualMemory"])
00333           target.MaxVirtualMemory = stringtoi((std::string)ComputingShare["MaxVirtualMemory"]);
00334         if (ComputingShare["MaxDiskSpace"])
00335           target.MaxDiskSpace = stringtoi((std::string)ComputingShare["MaxDiskSpace"]);
00336         if (ComputingShare["DefaultStorageService"])
00337           target.DefaultStorageService = (std::string)ComputingShare["DefaultStorageService"];
00338         if (ComputingShare["Preemption"])
00339           target.Preemption = ((std::string)ComputingShare["Preemption"] == "true") ? true : false;
00340         if (ComputingShare["EstimatedAverageWaitingTime"])
00341           target.EstimatedAverageWaitingTime = (std::string)ComputingShare["EstimatedAverageWaitingTime"];
00342         if (ComputingShare["EstimatedWorstWaitingTime"])
00343           target.EstimatedWorstWaitingTime = stringtoi((std::string)ComputingShare["EstimatedWorstWaitingTime"]);
00344         if (ComputingShare["ReservationPolicy"])
00345           target.ReservationPolicy = stringtoi((std::string)ComputingShare["ReservationPolicy"]);
00346 
00347         XMLNode ComputingManager = GLUEService["ComputingManager"];
00348         if (ComputingManager["ProductName"])
00349           target.ManagerProductName = (std::string)ComputingManager["ProductName"];
00350         else if (ComputingManager["Type"]) // is this non-standard fallback needed?
00351           target.ManagerProductName = (std::string)ComputingManager["Type"];
00352         if (ComputingManager["ProductVersion"])
00353           target.ManagerProductName = (std::string)ComputingManager["ProductVersion"];
00354         if (ComputingManager["Reservation"])
00355           target.Reservation = ((std::string)ComputingManager["Reservation"] == "true") ? true : false;
00356         if (ComputingManager["BulkSubmission"])
00357           target.BulkSubmission = ((std::string)ComputingManager["BulkSubmission"] == "true") ? true : false;
00358         if (ComputingManager["TotalPhysicalCPUs"])
00359           target.TotalPhysicalCPUs = stringtoi((std::string)ComputingManager["TotalPhysicalCPUs"]);
00360         if (ComputingManager["TotalLogicalCPUs"])
00361           target.TotalLogicalCPUs = stringtoi((std::string)ComputingManager["TotalLogicalCPUs"]);
00362         if (ComputingManager["TotalSlots"])
00363           target.TotalSlots = stringtoi((std::string)ComputingManager["TotalSlots"]);
00364         if (ComputingManager["Homogeneous"])
00365           target.Homogeneous = ((std::string)ComputingManager["Homogeneous"] == "true") ? true : false;
00366         if (ComputingManager["NetworkInfo"])
00367           for (XMLNode n = ComputingManager["NetworkInfo"]; n; ++n)
00368             target.NetworkInfo.push_back((std::string)n);
00369         if (ComputingManager["WorkingAreaShared"])
00370           target.WorkingAreaShared = ((std::string)ComputingManager["WorkingAreaShared"] == "true") ? true : false;
00371         if (ComputingManager["WorkingAreaFree"])
00372           target.WorkingAreaFree = stringtoi((std::string)ComputingManager["WorkingAreaFree"]);
00373         if (ComputingManager["WorkingAreaTotal"])
00374           target.WorkingAreaTotal = stringtoi((std::string)ComputingManager["WorkingAreaTotal"]);
00375         if (ComputingManager["WorkingAreaLifeTime"])
00376           target.WorkingAreaLifeTime = (std::string)ComputingManager["WorkingAreaLifeTime"];
00377         if (ComputingManager["CacheFree"])
00378           target.CacheFree = stringtoi((std::string)ComputingManager["CacheFree"]);
00379         if (ComputingManager["CacheTotal"])
00380           target.CacheTotal = stringtoi((std::string)ComputingManager["CacheTotal"]);
00381         for (XMLNode n = ComputingManager["Benchmark"]; n; ++n) {
00382           double value;
00383           if (n["Type"] && n["Value"] &&
00384               stringto((std::string)n["Value"], value))
00385             target.Benchmarks[(std::string)n["Type"]] = value;
00386           else {
00387             logger.msg(VERBOSE, "Couldn't parse benchmark XML:\n%s", (std::string)n);
00388             continue;
00389           }
00390         }
00391         for (XMLNode n = ComputingManager["ApplicationEnvironments"]["ApplicationEnvironment"]; n; ++n) {
00392           ApplicationEnvironment ae((std::string)n["AppName"], (std::string)n["AppVersion"]);
00393           ae.State = (std::string)n["State"];
00394           if (n["FreeSlots"])
00395             ae.FreeSlots = stringtoi((std::string)n["FreeSlots"]);
00396           else
00397             ae.FreeSlots = target.FreeSlots;
00398           if (n["FreeJobs"])
00399             ae.FreeJobs = stringtoi((std::string)n["FreeJobs"]);
00400           else
00401             ae.FreeJobs = -1;
00402           if (n["FreeUserSeats"])
00403             ae.FreeUserSeats = stringtoi((std::string)n["FreeUserSeats"]);
00404           else
00405             ae.FreeUserSeats = -1;
00406           target.ApplicationEnvironments.push_back(ae);
00407         }
00408 
00409         mom.AddTarget(target);
00410       }
00411     }
00412     else if (targetType == 1) {
00413       /*
00414        * If errors are encountered here there cannot be returned in usual manner
00415        * since this function should run in a separate thread. There might be a
00416        * need for reporting errors, maybe a bit flag of some kind.
00417        */
00418       DataHandle dir_url(url, usercfg);
00419       if (!dir_url) {
00420         logger.msg(INFO, "Failed retrieving job IDs: Unsupported url (%s) given", url.str());
00421         mom.RetrieverDone();
00422         return;
00423       }
00424 
00425       dir_url->SetSecure(false);
00426       std::list<FileInfo> files;
00427       if (!dir_url->ListFiles(files, false, false, false)) {
00428         if (files.size() == 0) {
00429           logger.msg(INFO, "Failed retrieving job IDs");
00430           mom.RetrieverDone();
00431           return;
00432         }
00433         logger.msg(VERBOSE, "Error encoutered during job ID retrieval. All job IDs might not have been retrieved");
00434       }
00435 
00436       for (std::list<FileInfo>::iterator file = files.begin();
00437            file != files.end(); file++) {
00438         NS ns;
00439         XMLNode info(ns, "Job");
00440         info.NewChild("JobID") = (std::string)url.str() + "/" + file->GetName();
00441         info.NewChild("Flavour") = "ARC1";
00442         info.NewChild("Cluster") = url.str();
00443         mom.AddJob(info);
00444       }
00445     }
00446 
00447     delete thrarg;
00448     mom.RetrieverDone();
00449   }
00450 
00451 } // namespace Arc