Salome HOME
CCAR: change computer name in container log file name
[modules/kernel.git] / src / Container / SALOME_ContainerManager.cxx
1 // Copyright (C) 2005  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
2 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
3 // 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either 
7 // version 2.1 of the License.
8 // 
9 // This library is distributed in the hope that it will be useful 
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
12 // Lesser General Public License for more details.
13 //
14 // You should have received a copy of the GNU Lesser General Public  
15 // License along with this library; if not, write to the Free Software 
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
17 //
18 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
19 //
20 #include "SALOME_ContainerManager.hxx"
21 #include "SALOME_NamingService.hxx"
22 #include "OpUtil.hxx"
23 #include <sys/types.h>
24 #ifndef WNT
25 #include <unistd.h>
26 #endif
27 #include <vector>
28 #include "Utils_CorbaException.hxx"
29 #include "Batch_Date.hxx"
30
31 #ifdef WITH_PACO_PARALLEL
32 #include "PaCO++.h"
33 #endif
34
35 #define TIME_OUT_TO_LAUNCH_CONT 21
36
37 using namespace std;
38
39 vector<Engines::Container_ptr> SALOME_ContainerManager::_batchLaunchedContainers;
40
41 vector<Engines::Container_ptr>::iterator SALOME_ContainerManager::_batchLaunchedContainersIter;
42
43 const char *SALOME_ContainerManager::_ContainerManagerNameInNS = 
44   "/ContainerManager";
45
46 //=============================================================================
47 /*! 
48  *  Constructor
49  *  \param orb
50  *  Define a CORBA single thread policy for the server, which avoid to deal
51  *  with non thread-safe usage like Change_Directory in SALOME naming service
52  */
53 //=============================================================================
54
55 SALOME_ContainerManager::SALOME_ContainerManager(CORBA::ORB_ptr orb, PortableServer::POA_var poa, SALOME_ResourcesManager *rm, SALOME_NamingService *ns)
56 {
57   MESSAGE("constructor");
58   _NS = ns;
59   _ResManager = rm;
60   _id=0;
61
62   PortableServer::POAManager_var pman = poa->the_POAManager();
63   _orb = CORBA::ORB::_duplicate(orb) ;
64   CORBA::PolicyList policies;
65   policies.length(1);
66   PortableServer::ThreadPolicy_var threadPol = 
67     poa->create_thread_policy(PortableServer::SINGLE_THREAD_MODEL);
68   policies[0] = PortableServer::ThreadPolicy::_duplicate(threadPol);
69
70   _poa = poa->create_POA("SThreadPOA",pman,policies);
71   threadPol->destroy();
72   PortableServer::ObjectId_var id = _poa->activate_object(this);
73   CORBA::Object_var obj = _poa->id_to_reference(id);
74   Engines::ContainerManager_var refContMan =
75     Engines::ContainerManager::_narrow(obj);
76
77   _NS->Register(refContMan,_ContainerManagerNameInNS);
78   MESSAGE("constructor end");
79 }
80
81 //=============================================================================
82 /*! 
83  * destructor
84  */
85 //=============================================================================
86
87 SALOME_ContainerManager::~SALOME_ContainerManager()
88 {
89   MESSAGE("destructor");
90 }
91
92 //=============================================================================
93 /*! CORBA method:
94  *  shutdown all the containers, then the ContainerManager servant
95  */
96 //=============================================================================
97
98 void SALOME_ContainerManager::Shutdown()
99 {
100   MESSAGE("Shutdown");
101   ShutdownContainers();
102   _NS->Destroy_Name(_ContainerManagerNameInNS);
103   PortableServer::ObjectId_var oid = _poa->servant_to_id(this);
104   _poa->deactivate_object(oid);
105   //_remove_ref() has already been done at creation
106   //_remove_ref();
107 }
108
109 //=============================================================================
110 /*! CORBA Method:
111  *  Loop on all the containers listed in naming service, ask shutdown on each
112  */
113 //=============================================================================
114
115 void SALOME_ContainerManager::ShutdownContainers()
116 {
117   MESSAGE("ShutdownContainers");
118   bool isOK;
119   isOK = _NS->Change_Directory("/Containers");
120   if( isOK ){
121     vector<string> vec = _NS->list_directory_recurs();
122     list<string> lstCont;
123     for(vector<string>::iterator iter = vec.begin();iter!=vec.end();iter++){
124       SCRUTE((*iter));
125       CORBA::Object_var obj=_NS->Resolve((*iter).c_str());
126       Engines::Container_var cont=Engines::Container::_narrow(obj);
127       if(!CORBA::is_nil(cont)){
128         lstCont.push_back((*iter));
129       }
130     }
131     MESSAGE("Container list: ");
132     for(list<string>::iterator iter=lstCont.begin();iter!=lstCont.end();iter++){
133       SCRUTE((*iter));
134     }
135     for(list<string>::iterator iter=lstCont.begin();iter!=lstCont.end();iter++){
136       SCRUTE((*iter));
137       CORBA::Object_var obj=_NS->Resolve((*iter).c_str());
138       Engines::Container_var cont=Engines::Container::_narrow(obj);
139       if(!CORBA::is_nil(cont))
140         {
141           MESSAGE("ShutdownContainers: " << (*iter));
142           try
143             {
144               cont->Shutdown();
145             }
146           catch(CORBA::SystemException& e)
147             {
148               INFOS("CORBA::SystemException ignored : " << e);
149             }
150           catch(CORBA::Exception&)
151             {
152               INFOS("CORBA::Exception ignored.");
153             }
154           catch(...)
155             {
156               INFOS("Unknown exception ignored.");
157             }
158         }
159       else 
160         MESSAGE("ShutdownContainers: no container ref for " << (*iter));
161     }
162   }
163 }
164
165 //=============================================================================
166 /*! CORBA Method:
167  *  Find a suitable Container in a list of machines, or start one
168  *  \param params            Machine Parameters required for the container
169  *  \param possibleComputers list of machines usable for find or start
170  */
171 //=============================================================================
172
173 Engines::Container_ptr
174 SALOME_ContainerManager::
175 FindOrStartContainer(const Engines::MachineParameters& params,
176                      const Engines::MachineList& possibleComputers)
177 {
178   Engines::Container_ptr ret = FindContainer(params,possibleComputers);
179   if(!CORBA::is_nil(ret))
180     return ret;
181   MESSAGE("Container doesn't exist try to launch it ...");
182
183   return StartContainer(params,possibleComputers,Engines::P_FIRST);
184
185 }
186
187 //=============================================================================
188 /*! CORBA Method:
189  *  Start a suitable Container in a list of machines
190  *  \param params            Machine Parameters required for the container
191  *  \param possibleComputers list of machines usable for start
192  */
193 //=============================================================================
194
195 Engines::Container_ptr
196 SALOME_ContainerManager::
197 StartContainer(const Engines::MachineParameters& params,
198                const Engines::MachineList& possibleComputers,
199                Engines::ResPolicy policy)
200 {
201 #ifdef WITH_PACO_PARALLEL
202   std::string parallelLib(params.parallelLib);
203   if (parallelLib != "")
204     return FindOrStartParallelContainer(params, possibleComputers);
205 #endif
206   long id;
207   string containerNameInNS;
208   char idc[3*sizeof(long)];
209   Engines::Container_ptr ret = Engines::Container::_nil();
210
211   MESSAGE("SALOME_ContainerManager::StartContainer " <<
212           possibleComputers.length());
213
214   string theMachine;
215   try{
216     switch(policy){
217     case Engines::P_FIRST:
218       theMachine=_ResManager->FindFirst(possibleComputers);
219       break;
220     case Engines::P_CYCL:
221       theMachine=_ResManager->FindNext(possibleComputers);
222       break;
223     case Engines::P_BEST:
224       theMachine=_ResManager->FindBest(possibleComputers);
225       break;
226     }
227   }
228   catch( const SALOME_Exception &ex ){
229     MESSAGE(ex.what());
230     return Engines::Container::_nil();
231   }
232
233   //If the machine name is localhost use the real name
234   if(theMachine == "localhost")
235     theMachine=GetHostname();
236
237   MESSAGE("try to launch it on " << theMachine);
238
239   // Get Id for container: a parallel container registers in Naming Service
240   // on the machine where is process 0. ContainerManager does'nt know the name
241   // of this machine before the launch of the parallel container. So to get
242   // the IOR of the parallel container in Naming Service, ContainerManager
243   // gives a unique Id. The parallel container registers his name under
244   // /ContainerManager/Id directory in NamingService
245
246   id = GetIdForContainer();
247
248   string command;
249   if(theMachine==""){
250     MESSAGE("SALOME_ContainerManager::StartContainer : " <<
251             "no possible computer");
252     return Engines::Container::_nil();
253   }
254   else if(theMachine==GetHostname())
255     command=_ResManager->BuildCommandToLaunchLocalContainer(params,id);
256   else
257     command = _ResManager->BuildCommandToLaunchRemoteContainer(theMachine,params,id);
258
259   _ResManager->RmTmpFile();
260
261   //check if an entry exists in Naming service
262   if(params.isMPI)
263     {
264       containerNameInNS = "/ContainerManager/id";
265       sprintf(idc,"%ld",id);
266       containerNameInNS += idc;
267     }
268   else
269     containerNameInNS = _NS->BuildContainerNameForNS(params,theMachine.c_str());
270
271   SCRUTE(containerNameInNS);
272   CORBA::Object_var obj = _NS->Resolve(containerNameInNS.c_str());
273   if ( !CORBA::is_nil(obj) )
274     {
275       // unregister the registered container if it exists
276       _NS->Destroy_Name(containerNameInNS.c_str());
277       // unregister component instances ???
278       //Engines::Container_var cont=Engines::Container::_narrow(obj);
279     }
280
281   //redirect stdout and stderr in a file
282   string logFilename="/tmp/"+_NS->ContainerName(params)+"_"+ theMachine +"_"+getenv( "USER" )+".log" ;
283   command += " > " + logFilename + " 2>&1 &";
284
285   // launch container with a system call
286   int status=system(command.c_str());
287   if (status == -1){
288     MESSAGE("SALOME_LifeCycleCORBA::StartOrFindContainer rsh failed " <<
289             "(system command status -1)");
290     return Engines::Container::_nil();
291   }
292   else if (status == 217){
293     MESSAGE("SALOME_LifeCycleCORBA::StartOrFindContainer rsh failed " <<
294             "(system command status 217)");
295     return Engines::Container::_nil();
296   }
297   else{
298     int count=TIME_OUT_TO_LAUNCH_CONT;
299     MESSAGE("count = "<<count);
300     while ( CORBA::is_nil(ret) && count ){
301 #ifndef WNT
302       sleep( 1 ) ;
303 #else
304       Sleep(1000);
305 #endif
306       count-- ;
307       if ( count != 10 )
308         MESSAGE( count << ". Waiting for container on " << theMachine);
309
310       CORBA::Object_var obj = _NS->Resolve(containerNameInNS.c_str());
311       ret=Engines::Container::_narrow(obj);
312     }
313     
314     if ( CORBA::is_nil(ret) )
315       {
316         MESSAGE("SALOME_LifeCycleCORBA::StartOrFindContainer rsh failed");
317       }
318     else
319       {
320         logFilename=":"+logFilename;
321         logFilename="@"+GetHostname()+logFilename;
322         logFilename=getenv( "USER" )+logFilename;
323         ret->logfilename(logFilename.c_str());
324       }
325
326     return ret;
327   }
328 }
329
330 //=============================================================================
331 /*! CORBA Method:
332  *  Start a suitable Container in a list of machines
333  *  \param params            Machine Parameters required for the container
334  *  \param possibleComputers list of machines usable for start
335  */
336 //=============================================================================
337
338 Engines::Container_ptr
339 SALOME_ContainerManager::
340 StartContainer(const Engines::MachineParameters& params,
341                Engines::ResPolicy policy,
342                const Engines::CompoList& componentList)
343 {
344   Engines::MachineList_var possibleComputers = _ResManager->GetFittingResources(params,componentList);
345   return StartContainer(params,possibleComputers,policy);
346 }
347
348 #ifdef WITH_PACO_PARALLEL
349 //=============================================================================
350 /*! CORBA Method:
351  *  Find or Start a suitable PaCO++ Parallel Container in a list of machines.
352  *  \param params            Machine Parameters required for the container
353  *  \param possibleComputers list of machines usable for find or start
354  *
355  *  \return CORBA container reference.
356  */
357 //=============================================================================
358 Engines::Container_ptr
359 SALOME_ContainerManager::
360 FindOrStartParallelContainer(const Engines::MachineParameters& params_const,
361                              const Engines::MachineList& possibleComputers)
362 {
363   CORBA::Object_var obj;
364   PaCO::InterfaceManager_var proxy;
365   Engines::Container_ptr ret = Engines::Container::_nil();
366   Engines::MachineParameters params(params_const);
367
368   // Step 1 : Try to find a suitable container
369   // Currently not as good as could be since
370   // we have to verified the number of nodes of the container
371   // if a user tell that.
372   ret = FindContainer(params, possibleComputers);
373
374   if(CORBA::is_nil(ret)) {
375     // Step 2 : Starting a new parallel container
376     INFOS("[FindOrStartParallelContainer] Starting a parallel container");
377
378     // Step 2.1 : Choose a computer
379     string theMachine = _ResManager->FindFirst(possibleComputers);
380     if(theMachine == "") {
381       INFOS("[FindOrStartParallelContainer] !!!!!!!!!!!!!!!!!!!!!!!!!!");
382       INFOS("[FindOrStartParallelContainer] No possible computer found");
383       INFOS("[FindOrStartParallelContainer] !!!!!!!!!!!!!!!!!!!!!!!!!!");
384     }
385     else {
386       INFOS("[FindOrStartParallelContainer] on machine : " << theMachine);
387       string command;
388       if(theMachine == GetHostname()) {
389         // Step 3 : starting parallel container proxy
390         params.hostname = CORBA::string_dup(theMachine.c_str());
391         Engines::MachineParameters params_proxy(params);
392         try {
393           command = _ResManager->BuildCommandToLaunchLocalParallelContainer("SALOME_ParallelContainerProxy", params_proxy, "xterm");
394         }
395         catch(const SALOME_Exception & ex){
396           MESSAGE(ex.what());
397           return Engines::Container::_nil();
398         }
399         // LaunchParallelContainer uses this value to know if it launches the proxy or the nodes
400         params_proxy.nb_component_nodes = 0;
401         obj = LaunchParallelContainer(command, params_proxy, _NS->ContainerName(params));
402         ret = Engines::Container::_narrow(obj);
403         proxy = PaCO::InterfaceManager::_narrow(obj);
404
405         // Step 4 : starting parallel container nodes
406         command = _ResManager->BuildCommandToLaunchLocalParallelContainer("SALOME_ParallelContainerNode", params, "xterm");
407         string name = _NS->ContainerName(params) + "Node";
408         LaunchParallelContainer(command, params, name);
409         // Step 5 : connecting nodes and the proxy to actually create a parallel container
410         try {
411           for (int i = 0; i < params.nb_component_nodes; i++) {
412
413             char buffer [5];
414 #ifndef WNT
415             snprintf(buffer,5,"%d",i);
416 #else
417             _snprintf(buffer,5,"%d",i);
418 #endif
419             string name_cont = name + string(buffer);
420
421             string theNodeMachine(CORBA::string_dup(params.hostname));
422             string containerNameInNS = _NS->BuildContainerNameForNS(name_cont.c_str(),theNodeMachine.c_str());
423             int count = TIME_OUT_TO_LAUNCH_CONT;
424             obj = _NS->Resolve(containerNameInNS.c_str());
425             while (CORBA::is_nil(obj) && count) {
426               INFOS("[FindOrStartParallelContainer] CONNECTION FAILED !!!!!!!!!!!!!!!!!!!!!!!!");
427 #ifndef WNT
428               sleep(1) ;
429 #else
430               Sleep(1000);
431 #endif
432               count-- ;
433               obj = _NS->Resolve(containerNameInNS.c_str());
434             }
435
436             PaCO::InterfaceParallel_var node = PaCO::InterfaceParallel::_narrow(obj);
437             MESSAGE("[FindOrStartParallelContainer] Deploying node : " << name);
438             node->deploy();
439           }
440           proxy->start();
441         }
442         catch(CORBA::SystemException& e)
443         {
444           INFOS("Caught CORBA::SystemException. : " << e);
445         }
446         catch(PortableServer::POA::ServantAlreadyActive&)
447         {
448           INFOS("Caught CORBA::ServantAlreadyActiveException");
449         }
450         catch(CORBA::Exception&)
451         {
452           INFOS("Caught CORBA::Exception.");
453         }
454         catch(std::exception& exc)
455         {
456           INFOS("Caught std::exception - "<<exc.what()); 
457         }
458         catch(...)
459         {
460           INFOS("Caught unknown exception.");
461         }
462         INFOS("[FindOrStartParallelContainer] node " << name << " deployed");
463       }
464       else {
465         INFOS("[FindOrStartParallelContainer] Currently parallel containers are launched only on the local host");
466       }
467     }
468 }
469 return ret;
470 }
471 #else
472 //=============================================================================
473 /*! CORBA Method:
474  *  Find or Start a suitable PaCO++ Parallel Container in a list of machines.
475  *  \param params            Machine Parameters required for the container
476  *  \param possibleComputers list of machines usable for find or start
477  *
478  *  \return CORBA container reference.
479  */
480 //=============================================================================
481 Engines::Container_ptr
482 SALOME_ContainerManager::
483 FindOrStartParallelContainer(const Engines::MachineParameters& params,
484                              const Engines::MachineList& possibleComputers)
485 {
486   Engines::Container_ptr ret = Engines::Container::_nil();
487   INFOS("[FindOrStartParallelContainer] is disabled !");
488   INFOS("[FindOrStartParallelContainer] recompile SALOME Kernel to enable parallel extension");
489   return ret;
490 }
491 #endif
492
493 //=============================================================================
494 /*! CORBA Method:
495  *  Give a suitable Container in a list of machines
496  *  \param params            Machine Parameters required for the container
497  *  \param possibleComputers list of machines usable for start
498  */
499 //=============================================================================
500
501 Engines::Container_ptr
502 SALOME_ContainerManager::
503 GiveContainer(const Engines::MachineParameters& params,
504                Engines::ResPolicy policy,
505                const Engines::CompoList& componentList)
506 {
507   char *valenv=getenv("SALOME_BATCH");
508   if(valenv)
509     if (strcmp(valenv,"1")==0)
510       {
511         if(_batchLaunchedContainers.empty())
512           fillBatchLaunchedContainers();
513         return *(_batchLaunchedContainersIter++);
514       }
515   return StartContainer(params,policy,componentList);
516 }
517
518 //=============================================================================
519 /*! 
520  * 
521  */
522 //=============================================================================
523
524 Engines::Container_ptr
525 SALOME_ContainerManager::
526 FindContainer(const Engines::MachineParameters& params,
527               const char *theMachine)
528 {
529   string containerNameInNS(_NS->BuildContainerNameForNS(params,theMachine));
530   CORBA::Object_var obj = _NS->Resolve(containerNameInNS.c_str());
531   if( !CORBA::is_nil(obj) )
532     return Engines::Container::_narrow(obj);
533   else
534     return Engines::Container::_nil();
535 }
536
537 //=============================================================================
538 /*! 
539  * 
540  */
541 //=============================================================================
542
543 Engines::Container_ptr
544 SALOME_ContainerManager::
545 FindContainer(const Engines::MachineParameters& params,
546               const Engines::MachineList& possibleComputers)
547 {
548   MESSAGE("FindContainer "<<possibleComputers.length());
549   for(unsigned int i=0;i<possibleComputers.length();i++)
550     {
551       MESSAGE("FindContainer possible " << possibleComputers[i]);
552       Engines::Container_ptr cont = FindContainer(params,possibleComputers[i]);
553       if( !CORBA::is_nil(cont) )
554         return cont;
555     }
556   MESSAGE("FindContainer: not found");
557   return Engines::Container::_nil();
558 }
559
560 //=============================================================================
561 /*! This method launches the parallel container.
562  *  It will may be placed on the ressources manager.
563  *
564  * \param command to launch
565  * \param container's parameters
566  * \param name of the container
567  *
568  * \return CORBA container reference
569  */
570 //=============================================================================
571 CORBA::Object_ptr 
572 SALOME_ContainerManager::LaunchParallelContainer(const std::string& command, 
573                                                  const Engines::MachineParameters& params,
574                                                  const std::string& name)
575 {
576   CORBA::Object_ptr obj = CORBA::Object::_nil();
577   string containerNameInNS;
578   MESSAGE("[LaunchParallelContainer] : command to launch...");
579   MESSAGE(command);
580   if (params.nb_component_nodes == 0) {
581     INFOS("[LaunchParallelContainer] launching the proxy of the parallel container");
582     int status = system(command.c_str());
583     if (status == -1) {
584       INFOS("[LaunchParallelContainer] failed : system command status -1");
585     }
586     else if (status == 217) {
587       INFOS("[LaunchParallelContainer] failed : system command status 217");
588     }
589
590     int count = TIME_OUT_TO_LAUNCH_CONT;
591     string theMachine(CORBA::string_dup(params.hostname));
592     containerNameInNS = _NS->BuildContainerNameForNS((char*) name.c_str(),theMachine.c_str());
593
594     INFOS("[LaunchParallelContainer]  Waiting for Parallel Container proxy on " << theMachine);
595     while (CORBA::is_nil(obj) && count) {
596 #ifndef WNT
597       sleep(1) ;
598 #else
599       Sleep(1000);
600 #endif
601       count-- ;
602       obj = _NS->Resolve(containerNameInNS.c_str());
603     }
604   }
605   else {
606     INFOS("[LaunchParallelContainer] launching the nodes of the parallel container");
607     int status = system(command.c_str());
608     if (status == -1) {
609       INFOS("[LaunchParallelContainer] failed : system command status -1");
610     }
611     else if (status == 217) {
612       INFOS("[LaunchParallelContainer] failed : system command status 217");
613     }
614     // We are waiting all the nodes
615     for (int i = 0; i < params.nb_component_nodes; i++) {
616       obj = CORBA::Object::_nil();
617       int count = TIME_OUT_TO_LAUNCH_CONT;
618
619       // Name of the node
620       char buffer [5];
621 #ifndef WNT
622       snprintf(buffer,5,"%d",i);
623 #else
624       _snprintf(buffer,5,"%d",i);
625 #endif
626
627       string name_cont = name + string(buffer);
628
629       // I don't like this...
630       string theMachine(CORBA::string_dup(params.hostname));
631       containerNameInNS = _NS->BuildContainerNameForNS((char*) name_cont.c_str(),theMachine.c_str());
632       cerr << "[LaunchContainer]  Waiting for Parllel Container node " << containerNameInNS << " on " << theMachine << endl;
633       while (CORBA::is_nil(obj) && count) {
634 #ifndef WNT
635         sleep(1) ;
636 #else
637         Sleep(1000);
638 #endif
639         count-- ;
640         obj = _NS->Resolve(containerNameInNS.c_str());
641       }
642     }
643   }
644
645   if ( CORBA::is_nil(obj) ) {
646     INFOS("[LaunchParallelContainer] failed");
647   }
648   return obj;
649 }
650
651 //=============================================================================
652 /*! 
653  * Get Id for container: a parallel container registers in Naming Service
654  * on the machine where is process 0. ContainerManager does'nt know the name
655  * of this machine before the launch of the parallel container. So to get
656  * the IOR of the parallel container in Naming Service, ContainerManager
657  * gives a unique Id. The parallel container registers his name under
658  * /ContainerManager/Id directory in NamingService
659  */
660 //=============================================================================
661
662
663 long SALOME_ContainerManager::GetIdForContainer(void)
664 {
665   _id++;
666   return _id;
667 }
668
669 void SALOME_ContainerManager::fillBatchLaunchedContainers()
670 {
671   _batchLaunchedContainers.clear();
672   _NS->Change_Directory("/Containers");
673   vector<string> vec = _NS->list_directory_recurs();
674   for(vector<string>::iterator iter = vec.begin();iter!=vec.end();iter++){
675     CORBA::Object_var obj=_NS->Resolve((*iter).c_str());
676     Engines::Container_ptr cont=Engines::Container::_narrow(obj);
677     if(!CORBA::is_nil(cont)){
678       _batchLaunchedContainers.push_back(cont);
679     }
680   }
681   _batchLaunchedContainersIter=_batchLaunchedContainers.begin();
682 }