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