Salome HOME
d1760f2ab6566f740a918a5bbb26e96615267fda
[modules/smesh.git] / src / Tools / padder / meshjob / impl / MeshJobManager_i.cxx
1 // Copyright (C) 2011-2012  EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // Authors : Guillaume Boulant (EDF) - 01/03/2011
20
21 #ifdef WIN32
22 #include <winsock2.h>
23 #include <windows.h> 
24 #else
25 #include <sys/time.h>
26 #endif
27
28 #include "MeshJobManager_i.hxx"
29
30 #include <SALOMEconfig.h>
31 #include CORBA_SERVER_HEADER(SALOME_Exception)
32
33
34 #include "Basics_Utils.hxx"         // For standard logging
35 #undef LOG
36 #include "SALOME_KernelServices.hxx"   // For CORBA logging
37 #undef LOG
38
39 #define LOG STDLOG
40
41 //
42 // ====================================================================
43 // General purpose helper functions (to put elsewhere at least)
44 // ====================================================================
45 //
46
47 /*!
48  * This function must be used to associate a datetime tag to a job
49  */
50
51 #ifndef WIN32
52 static long timetag() {
53   timeval tv;
54   gettimeofday(&tv,0);
55   long tag = tv.tv_usec + tv.tv_sec*1000000;
56   return tag;
57 }
58 #endif
59
60 /*!
61  * This function returns true if the string text starts with the string
62  * token.
63  */
64 static bool myStartsWith(const std::string& text,const std::string& token){     
65   if(text.length() < token.length())
66     return false;
67   return (text.compare(0, token.length(), token) == 0);
68 }
69
70 //
71 // ====================================================================
72 // Constructor/Destructor
73 // ====================================================================
74 //
75 MeshJobManager_i::MeshJobManager_i(CORBA::ORB_ptr orb,
76                                    PortableServer::POA_ptr poa,
77                                    PortableServer::ObjectId * contId,
78                                    const char *instanceName,
79                                    const char *interfaceName)
80   : Engines_Component_i(orb, poa, contId, instanceName, interfaceName)
81 {
82   LOG("Activating MESHJOB::MeshJobManager object");
83   _thisObj = this ;
84   _id = _poa->activate_object(_thisObj);
85
86   _salomeLauncher   = KERNEL::getSalomeLauncher();
87   if(CORBA::is_nil(_salomeLauncher)){
88     LOG("The SALOME launcher can't be reached ==> STOP");
89     throw KERNEL::createSalomeException("SALOME launcher can't be reached");
90   }
91
92   _resourcesManager = KERNEL::getResourcesManager();
93   if(CORBA::is_nil(_resourcesManager)){
94     LOG("The SALOME resource manager can't be reached ==> STOP");
95     throw KERNEL::createSalomeException("The SALOME resource manager can't be reached");
96   }
97
98   _lastErrorMessage = "";
99 }
100
101 MeshJobManager_i::~MeshJobManager_i() {
102   LOG("MeshJobManager_i::~MeshJobManager_i()");
103 }
104
105 //
106 // ====================================================================
107 // Helper functions to deals with the local and remote file systems
108 // ====================================================================
109 //
110 #include <fstream>     // to get the file streams
111 #ifdef WNT             
112 #include <stdlib.h>    // to get _splitpath
113 #include <direct.h>    // to get _mkdir
114 #else
115 #include <unistd.h>    // to get basename
116 #include <sys/stat.h>  // to get mkdir
117 #include <sys/types.h> // to get mkdir options
118 #endif
119
120 #include <stdlib.h>    // to get system and getenv
121
122 static std::string OUTPUTFILE("output.med");
123 static std::string DATAFILE("data.txt");
124 static std::string SCRIPTFILE("padder.sh");
125 static std::string SEPARATOR(" ");
126
127 static std::string USER(getenv("USER"));
128 static std::string LOCAL_INPUTDIR("/tmp/spadder.local.inputdir."+USER);
129 static std::string LOCAL_RESULTDIR("/tmp/spadder.local.resultdir."+USER);
130 static std::string REMOTE_WORKDIR("/tmp/spadder.remote.workdir."+USER);
131
132 /*!
133  * This function creates the padder text input file containing the
134  * input data (list of filenames and groupnames) and returns the path
135  * of the created file. This function is the one that knows the format
136  * of the padder input file. If the input file format changes, then
137  * this function (and only this one) should be updated.
138  */
139 const char * MeshJobManager_i::_writeDataFile(std::vector<MESHJOB::MeshJobParameter> listConcreteMesh,
140                                               std::vector<MESHJOB::MeshJobParameter> listSteelBarMesh) {
141 #ifdef WIN32
142   _mkdir(LOCAL_INPUTDIR.c_str());
143 #else
144   mkdir(LOCAL_INPUTDIR.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
145 #endif
146
147   // Make it static so that it's allocated once (constant name)
148   static std::string * dataFilename = new std::string(LOCAL_INPUTDIR+"/"+DATAFILE);
149   std::ofstream dataFile(dataFilename->c_str());
150   
151   // We first specify the concrete mesh data (filename and groupname)
152   std::string line;
153 #ifdef WIN32
154   char fname[ _MAX_FNAME ];
155   _splitpath( listConcreteMesh[0].file_name, NULL, NULL, fname, NULL );
156   char* bname = &fname[0];
157 #else
158   char* bname = basename(listConcreteMesh[0].file_name);
159 #endif
160   line = std::string(bname) + " " + std::string(listConcreteMesh[0].group_name);
161   dataFile << line.c_str() << std::endl;
162   // Note that we use here the basename because the files are supposed
163   // to be copied in the REMOTE_WORKDIR for execution.
164   
165   // The, we can specify the steelbar mesh data, starting by the
166   // number of meshes
167   int nbSteelBarMesh=listSteelBarMesh.size();
168   line = std::string("nbSteelbarMesh") + SEPARATOR + ToString(nbSteelBarMesh);
169   dataFile << line.c_str() << std::endl;
170   for (int i=0; i<nbSteelBarMesh; i++) {
171 #ifdef WIN32
172         char fname[ _MAX_FNAME ];
173         _splitpath( listSteelBarMesh[i].file_name, NULL, NULL, fname, NULL );
174         char* bname = &fname[0];
175 #else
176         char* bname = basename(listSteelBarMesh[i].file_name);
177 #endif
178     line = std::string(bname) + " " + std::string(listSteelBarMesh[i].group_name);
179     dataFile << line.c_str() << std::endl;
180   }
181   
182   // Finally, we conclude with the name of the output file
183   line = OUTPUTFILE;
184   dataFile << line.c_str() << std::endl;
185   dataFile.close();
186   return dataFilename->c_str();  
187 }
188
189 /*!
190  * This function creates a shell script that runs padder whith the
191  * specified data file, and returns the path of the created script
192  * file. The config id is used to retrieve the path to the binary file
193  * and other required files.
194  */
195 const char* MeshJobManager_i::_writeScriptFile(const char * dataFileName, const char * configId) {
196 #ifdef WIN32
197   _mkdir(LOCAL_INPUTDIR.c_str());
198 #else
199   mkdir(LOCAL_INPUTDIR.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
200 #endif
201
202   // Make it static so that it's allocated once (constant name)
203   static std::string * scriptFilename = new std::string(LOCAL_INPUTDIR+"/"+SCRIPTFILE);
204
205   char * binpath = _configMap[configId].binpath;
206   char * envpath = _configMap[configId].envpath;
207
208 #ifdef WIN32
209         char fname[ _MAX_FNAME ];
210         _splitpath( dataFileName, NULL, NULL, fname, NULL );
211         const char* bname = &fname[0];
212 #else
213         const char* bname = basename(dataFileName);
214 #endif
215
216
217   std::ofstream script(scriptFilename->c_str());
218   script << "#!/bin/sh"                                   << std::endl;
219   script << "here=$(dirname $0)"                          << std::endl;
220   script << ". " << envpath                               << std::endl;
221   script << binpath << " $here/" << bname                 << std::endl;
222   // Note that we use the basename of the datafile because all data
223   // files are supposed to have been copied in the REMOTE_WORKDIR.
224   script.close();
225   return scriptFilename->c_str();
226 }
227
228 //
229 // ====================================================================
230 // Functions to initialize and supervise the mesh computation job
231 // ====================================================================
232 //
233 bool MeshJobManager_i::configure(const char *configId,
234                                  const MESHJOB::ConfigParameter & configParameter)
235 {
236   beginService("MeshJobManager_i::configure");
237   
238   _configMap[configId] = configParameter;
239
240   LOG("Adding configuration for " << configId);
241   LOG("- binpath = " << _configMap[configId].binpath);
242   LOG("- envpath = " << _configMap[configId].envpath);
243
244   endService("MeshJobManager_i::configure");
245   return true;
246 }
247
248 long MeshJobManager_i::JOBID_UNDEFINED = -1;
249
250 /*! Initialize a smesh computation job and return the job identifier */
251 CORBA::Long MeshJobManager_i::initialize(const MESHJOB::MeshJobParameterList & meshJobParameterList,
252                                          const char * configId)
253 {
254   beginService("MeshJobManager_i::initialize");
255   std::cerr << "##################################### initialize" << std::endl;
256   std::cerr << "#####################################" << std::endl;
257
258   //
259   // We first analyse the CORBA sequence to store data in C++ vectors
260   //
261   std::vector<MESHJOB::MeshJobParameter> listConcreteMesh;
262   std::vector<MESHJOB::MeshJobParameter> listSteelBarMesh;
263   for(CORBA::ULong i=0; i<meshJobParameterList.length(); i++) {
264     MESHJOB::MeshJobParameter currentMesh = meshJobParameterList[i];
265     switch ( currentMesh.file_type ) {
266     case MESHJOB::MED_CONCRETE:
267       listConcreteMesh.push_back(currentMesh);
268       break;
269     case MESHJOB::MED_STEELBAR:
270       listSteelBarMesh.push_back(currentMesh);
271       break;
272     default:
273       _lastErrorMessage =
274         std::string("The type of the file ")+
275         std::string(currentMesh.file_name)+
276         std::string(" is not recognized");
277       LOG(_lastErrorMessage);
278       return JOBID_UNDEFINED;
279     }
280   }
281   
282   // It is not possible to specify more than one concrete
283   // file. Converselly, it is possible to specify no concrete file.
284   if ( listConcreteMesh.size() > 1 ) {
285     // Not consistent with the specification
286     _lastErrorMessage = std::string("You specify more than one concrete mesh (not authorized)");
287     LOG(_lastErrorMessage);
288     return JOBID_UNDEFINED;
289   }
290   
291   LOG("Nb. concrete mesh = " << listConcreteMesh.size());
292   LOG("Nb. steelbar mesh = " << listSteelBarMesh.size());
293
294   // We initiate here a datetime to tag the files and folder
295   // associated to this job.
296 #ifdef WIN32
297   DWORD jobDatetimeTag = timeGetTime();
298 #else
299   long jobDatetimeTag = timetag();
300 #endif
301   // And a MESHJOB::MeshJobPaths structure to hold the directories
302   // where to find data
303   MESHJOB::MeshJobPaths * jobPaths = new MESHJOB::MeshJobPaths();
304   jobPaths->local_inputdir  = LOCAL_INPUTDIR.c_str();
305   jobPaths->local_resultdir = (LOCAL_RESULTDIR + "." + ToString(jobDatetimeTag)).c_str();
306   jobPaths->remote_workdir  = (REMOTE_WORKDIR + "." + ToString(jobDatetimeTag)).c_str();  
307
308   //
309   // Then, we have to create the padder input data file. This input
310   // data is a text file containing the list of file names and group
311   // names.
312   //
313   const char * dataFilename = this->_writeDataFile(listConcreteMesh, listSteelBarMesh);
314   LOG("dataFilename = " << dataFilename);
315   const char * scriptFilename = this->_writeScriptFile(dataFilename, configId);
316   LOG("scriptFilename = " << scriptFilename);
317
318   //
319   // Then, the following instructions consists in preparing the job
320   // parameters to request the SALOME launcher for creating a new
321   // job.
322   //
323   Engines::JobParameters_var jobParameters = new Engines::JobParameters;
324   jobParameters->job_type = CORBA::string_dup("command");
325   // CAUTION: the job_file must be a single filename specifying a
326   // self-consistent script to be executed without any argument on the
327   // remote host.
328   jobParameters->job_file = CORBA::string_dup(scriptFilename);
329
330   //
331   // Specification of the working spaces:
332   //
333   // - local_directory: can be used to specify where to find the input
334   //   files on the local resource. It's optionnal if you specify the
335   //   absolute path name of input files.
336   //
337   // - result_directory: must be used to specify where to download the
338   //   output files on the local resources
339   //
340   // - work_directory: must be used to specify the remote directory
341   //   where to put all the stuff to run the job. Note that the job
342   //   will be executed from within this directory, i.e. a change
343   //   directory toward this working directory is done by the batch
344   //   system before running the specified job script.
345   //
346   jobParameters->local_directory  = CORBA::string_dup("");
347   jobParameters->result_directory = CORBA::string_dup(jobPaths->local_resultdir);
348   jobParameters->work_directory   = CORBA::string_dup(jobPaths->remote_workdir);
349
350   // We specify the input files that are required to execute the
351   // job_file. If basenames are specified, then the files are supposed
352   // to be located in local_directory.
353   int nbFiles = listSteelBarMesh.size()+2;
354   // The number of input file is: 
355   //   (nb. of steelbar meshfile)
356   // + (1 concrete meshfile)
357   // + (1 padder input file)
358   // = nb steelbar meshfile + 2
359   jobParameters->in_files.length(nbFiles);
360   jobParameters->in_files[0] = CORBA::string_dup(listConcreteMesh[0].file_name);
361   for (int i=0; i<listSteelBarMesh.size(); i++) {
362     jobParameters->in_files[1+i] = CORBA::string_dup(listSteelBarMesh[i].file_name);
363   }
364   jobParameters->in_files[1+listSteelBarMesh.size()] = CORBA::string_dup(dataFilename);
365   // Note that all these input files will be copied in the
366   // REMOTE_WORKDIR on the remote host
367   
368   // Then, we have to specify the existance of an output
369   // filenames. The path is supposed to be a path on the remote
370   // resource, i.e. where the job is executed.
371   jobParameters->out_files.length(1);
372   std::string outputfile_name = std::string(jobPaths->remote_workdir)+"/"+OUTPUTFILE;
373   jobParameters->out_files[0] = CORBA::string_dup(outputfile_name.c_str());
374
375   // CAUTION: the maximum duration has to be set with a format like "hh:mm"
376   jobParameters->maximum_duration = CORBA::string_dup("01:00");
377   jobParameters->queue = CORBA::string_dup("");
378
379   // Setting resource and additionnal properties (if needed)
380   // The resource parameters can be initiated from scratch, for
381   // example by specifying the values in hard coding:
382   // >>>
383   //jobParameters->resource_required.name = CORBA::string_dup("localhost");
384   //jobParameters->resource_required.hostname = CORBA::string_dup("localhost");
385   //jobParameters->resource_required.mem_mb = 1024 * 10;
386   //jobParameters->resource_required.nb_proc = 1;
387   // <<<
388   // But it's better to initiate these parameters from a resource
389   // definition known by the resource manager. This ensures that the
390   // resource will be available:
391   //const char * resourceName = "localhost";
392   //const char * resourceName = "boulant@claui2p1";
393   //const char * resourceName = "nepal@nepal";
394   const char * resourceName = _configMap[configId].resname;
395   
396   Engines::ResourceDefinition * resourceDefinition;
397   try {
398     resourceDefinition = _resourcesManager->GetResourceDefinition(resourceName);
399   }
400   catch (const CORBA::SystemException& ex) {
401     _lastErrorMessage = std::string("We can not access to the ressource ") + std::string(resourceName);
402     _lastErrorMessage+= std::string("(check the file CatalogResource.xml)");
403     LOG(_lastErrorMessage);
404     return JOBID_UNDEFINED;
405   }
406   // CAUTION: This resource should have been defined in the
407   // CatalogResource.xml associated to the SALOME application.
408   //
409   // Then, the values can be used to initiate the resource parameters
410   // of the job:
411   jobParameters->resource_required.name     = CORBA::string_dup(resourceDefinition->name.in());
412   // CAUTION: the additionnal two following parameters MUST be
413   // specified explicitly, because they are not provided by the
414   // resource definition:
415   jobParameters->resource_required.mem_mb   = resourceDefinition->mem_mb;
416   jobParameters->resource_required.nb_proc  = resourceDefinition->nb_proc_per_node;
417   // CAUTION: the parameter mem_mb specifies the maximum memory value
418   // that could be allocated for executing the job. This takes into
419   // account not only the data that could be loaded by the batch
420   // process but also the linked dynamic library.
421   //
422   // A possible problem, for exemple in the case where you use the ssh
423   // emulation of a batch system, is to get an error message as below
424   // when libBatch try to run the ssh command:
425   //
426   // ## /usr/bin/ssh: error while loading shared libraries: libcrypto.so.0.9.8: failed
427   // ## to map segment from shared object: Cannot allocate memory
428   //
429   // In this exemple, the mem_mb was set to 1MB, value that is not
430   // sufficient to load the dynamic libraries linked to the ssh
431   // executable (libcrypto.so in the error message).
432   //
433   // So, even in the case of a simple test shell script, you should
434   // set this value at least to a standard threshold as 500MB
435   int jobId = JOBID_UNDEFINED;
436   try {
437     jobId = _salomeLauncher->createJob(jobParameters);
438     // We register the datetime tag of this job
439     _jobDateTimeMap[jobId]=jobDatetimeTag;
440     _jobPathsMap[jobId] = jobPaths;
441   }
442   catch (const SALOME::SALOME_Exception & ex) {
443     LOG("SALOME Exception at initialization step !" <<ex.details.text.in());
444     _lastErrorMessage = ex.details.text.in();
445     return JOBID_UNDEFINED;
446   }
447   catch (const CORBA::SystemException& ex) {
448     LOG("Receive SALOME System Exception: "<<ex);
449     LOG("Check SALOME servers...");
450     _lastErrorMessage = "Check the SALOME servers (or try to restart SALOME)";
451     return JOBID_UNDEFINED;
452   }
453   
454   endService("MeshJobManager_i::initialize");
455   return jobId;
456 }
457
458 /*! Submit the job execution and return true if submission is OK */
459 bool MeshJobManager_i::start(CORBA::Long jobId) {
460   beginService("MeshJobManager_i::start");
461
462   try {
463     _salomeLauncher->launchJob(jobId);
464   }
465   catch (const SALOME::SALOME_Exception & ex) {
466     LOG("SALOME Exception in launchjob !" <<ex.details.text.in());
467     _lastErrorMessage = ex.details.text.in();
468     return false;
469   }
470   catch (const CORBA::SystemException& ex) {
471     LOG("Receive SALOME System Exception: "<<ex);
472     LOG("Check SALOME servers...");
473     _lastErrorMessage = "Check the SALOME servers (or try to restart SALOME)";
474     return false;
475   }
476
477   endService("MeshJobManager_i::initialize");
478   return true;
479 }
480
481 /*! Request the launch manager for the state of the specified job */
482 char* MeshJobManager_i::getState(CORBA::Long jobId) {
483   beginService("MeshJobManager_i::getState");
484
485   std::string state;
486   try
487   {
488     state = _salomeLauncher->getJobState(jobId);
489   }
490   catch (const SALOME::SALOME_Exception & ex)
491   {
492     LOG("SALOME Exception in getJobState !");
493     _lastErrorMessage = ex.details.text.in();
494     state = ex.details.text;
495   }
496   catch (const CORBA::SystemException& ex)
497   {
498     LOG("Receive SALOME System Exception: " << ex);
499     state="SALOME System Exception - see logs";
500   }
501   LOG("jobId="<<ToString(jobId)<<" state="<<state);
502   endService("MeshJobManager_i::getState");
503   return CORBA::string_dup(state.c_str());
504 }
505
506 MESHJOB::MeshJobPaths * MeshJobManager_i::getPaths(CORBA::Long jobId) {
507
508   MESHJOB::MeshJobPaths * jobPaths = _jobPathsMap[jobId];
509   if ( jobPaths == NULL ) {
510     LOG("You request the working paths for an undefined job (jobId="<<ToString(jobId)<<")");
511     return NULL; // Maybe raise an exception?
512   }
513   return jobPaths;
514 }
515
516
517 MESHJOB::MeshJobResults * MeshJobManager_i::finalize(CORBA::Long jobId) {
518   beginService("MeshJobManager_i::getResults");
519   MESHJOB::MeshJobResults * result = new MESHJOB::MeshJobResults();
520
521   MESHJOB::MeshJobPaths * jobPaths = this->getPaths(jobId);
522   std::string local_resultdir(jobPaths->local_resultdir);
523   result->results_dirname = local_resultdir.c_str();  
524   try
525   {
526     _salomeLauncher->getJobResults(jobId, local_resultdir.c_str());
527  
528     // __BUG__: to prevent from a bug of the MED driver (SALOME
529     // 5.1.5), we change the basename of the output file to force the
530     // complete reloading of data by the med driver.
531     long jobDatetimeTag = _jobDateTimeMap[jobId];
532     std::string outputFileName = "output"+ToString(jobDatetimeTag)+".med";
533     rename((local_resultdir+"/"+OUTPUTFILE).c_str(), (local_resultdir+"/"+outputFileName).c_str());
534
535     result->outputmesh_filename = outputFileName.c_str();
536     result->status = "OK";
537  }
538   catch (const SALOME::SALOME_Exception & ex)
539   {
540     LOG("SALOME Exception in getResults !");
541     result->status = "SALOME Exception in getResults !";
542     _lastErrorMessage = ex.details.text.in();
543   }
544   catch (const CORBA::SystemException& ex)
545   {
546     LOG("Receive CORBA System Exception: " << ex);
547     result->status = "Receive CORBA System Exception: see log";
548   }
549   endService("MeshJobManager_i::getResults");
550   return result;
551 }
552
553
554 /*! Clean all data associated to this job and remove the job from the launch manager */
555 bool MeshJobManager_i::clean(CORBA::Long jobId) {
556   beginService("MeshJobManager_i::clean");
557   
558   // __GBO__ WORK IN PROGRESS: we just clean the temporary local
559   // directories. The remote working directories are tag with the
560   // execution datetime and the we prevent the task from conflict
561   // with files of another task.
562   MESHJOB::MeshJobPaths * jobPaths = this->getPaths(jobId);
563   if ( jobPaths == NULL ) return false;
564
565   // WARN: !!!!!
566   // For safety reason (and prevent from bug that could erase the
567   // filesystem), we cancel the operation in the case where the
568   // directories to delete are not in the /tmp folder.
569   std::string shell_command("rm -rf ");
570   std::string inputdir(jobPaths->local_inputdir);
571   std::string resultdir(jobPaths->local_resultdir);
572   if ( !myStartsWith(inputdir,"/tmp/") )  {
573     LOG("WRN: The directory "<<inputdir<<" is not in /tmp. NO DELETE is done");
574   } else {
575     shell_command+=inputdir+" ";
576   }
577   if ( !myStartsWith(resultdir,"/tmp/"))  {
578     LOG("WRN: The directory "<<resultdir<<" is not in /tmp. NO DELETE is done");
579   } else {
580     shell_command+=resultdir;
581   }
582
583   LOG("DBG: clean shell command = "<<shell_command);
584
585   bool cleanOk = false;
586   int error = system(shell_command.c_str());
587   if (error == 0) cleanOk = true;
588
589   endService("MeshJobManager_i::clean");
590   return cleanOk;
591 }
592
593
594 std::vector<std::string> * MeshJobManager_i::_getResourceNames() {
595
596   //
597   // These part is just to control the available resources
598   //
599   Engines::ResourceParameters params;
600   KERNEL::getLifeCycleCORBA()->preSet(params);
601
602   Engines::ResourceList * resourceList = _resourcesManager->GetFittingResources(params);
603   Engines::ResourceDefinition * resourceDefinition = NULL;
604   LOG("### resource list:");
605   std::vector<std::string>* resourceNames = new std::vector<std::string>();
606   if (resourceList) {
607     for (int i = 0; i < resourceList->length(); i++) {
608       const char* aResourceName = (*resourceList)[i];
609       resourceNames->push_back(std::string(aResourceName));
610       LOG("resource["<<i<<"] = "<<aResourceName);
611       resourceDefinition = _resourcesManager->GetResourceDefinition(aResourceName);
612       LOG("protocol["<<i<<"] = "<<resourceDefinition->protocol);
613     }
614   }
615
616   // Note: a ResourceDefinition is used to create a batch configuration
617   // in the Launcher. This operation is done at Launcher startup from
618   // the configuration file CatalogResources.xml provided by the
619   // SALOME application.
620   // In the code instructions, you just have to choose a resource
621   // configuration by its name and then define the ResourceParameters
622   // that specify additionnal properties for a specific job submission
623   // (use the attribute resource_required of the JobParameters).
624
625   return resourceNames;
626 }
627
628 char* MeshJobManager_i::getLastErrorMessage() {
629   beginService("MeshJobManager_i::getState");
630   endService("MeshJobManager_i::getState");
631   return CORBA::string_dup(_lastErrorMessage.c_str());
632 }
633
634 //
635 // ==========================================================================
636 // Factory services
637 // ==========================================================================
638 //
639 extern "C"
640 {
641   PortableServer::ObjectId * MeshJobManagerEngine_factory( CORBA::ORB_ptr orb,
642                                                            PortableServer::POA_ptr poa,
643                                                            PortableServer::ObjectId * contId,
644                                                            const char *instanceName,
645                                                            const char *interfaceName)
646   {
647     LOG("PortableServer::ObjectId * MeshJobManagerEngine_factory()");
648     MeshJobManager_i * myEngine = new MeshJobManager_i(orb, poa, contId, instanceName, interfaceName);
649     return myEngine->getId() ;
650   }
651 }