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