1 // SALOME Session : implementation of Session.idl
3 // Copyright (C) 2003 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // Lesser General Public License for more details.
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
24 // File : SALOME_Session_Server.cxx
25 // Author : Paul RASCLE, EDF
28 #include <Container_init_python.hxx>
29 #include "Utils_ORB_INIT.hxx"
30 #include "Utils_SINGLETON.hxx"
31 #include "SALOME_NamingService.hxx"
32 #include "SALOMETraceCollector.hxx"
34 #include "SALOME_ModuleCatalog_impl.hxx"
36 #include "RegistryService.hxx"
37 #include "ConnectionManager_i.hxx"
48 #include <qapplication.h>
49 #include <qwaitcondition.h>
52 #include "Utils_SALOME_Exception.hxx"
53 #include "Utils_CorbaException.hxx"
54 #include "SALOME_Event.hxx"
56 #include <SALOMEconfig.h>
57 #include CORBA_SERVER_HEADER(SALOME_Session)
58 #include CORBA_SERVER_HEADER(SALOMEDS)
60 #include <utilities.h>
61 #include "Session_Session_i.hxx"
62 #include "Session_ServerLauncher.hxx"
63 #include "Session_ServerCheck.hxx"
65 #include <QtxSplash.h>
66 #include "SUIT_Tools.h"
67 #include "SUIT_Session.h"
68 #include "SUIT_Application.h"
69 #include "SUIT_Desktop.h"
70 #include "SUIT_MessageBox.h"
71 #include "SUIT_ResourceMgr.h"
72 #include "SUIT_ExceptionHandler.h"
74 /*! - read arguments, define list of server to launch with their arguments.
75 * - wait for naming service
76 * - create and run a thread for launch of all servers
80 //! CORBA server for SALOME Session
82 * SALOME_Session Server launches a SALOME session servant.
83 * The servant registers to the Naming Service.
84 * See SALOME_Session.idl for interface specification.
86 * Main services offered by the servant are:
88 * - stop Session ( must be idle )
92 PyObject* salome_shared_modules_module = 0;
94 void MessageOutput( QtMsgType type, const char* msg )
99 MESSAGE( "Debug: " << msg );
102 MESSAGE( "Warning: " << msg );
105 MESSAGE( "Fatal: " << msg );
111 static const char* pixmap_not_found_xpm[] = {
133 QString salomeVersion()
135 QString path( ::getenv( "GUI_ROOT_DIR" ) );
136 if ( !path.isEmpty() )
137 path += QDir::separator();
138 path += QString( "bin/salome/VERSION" );
141 if ( !vf.open( IO_ReadOnly ) )
142 return QString::null;
145 vf.readLine( line, 1024 );
148 if ( line.isEmpty() )
149 return QString::null;
151 while ( !line.isEmpty() && line.at( line.length() - 1 ) == QChar( '\n' ) )
152 line.remove( line.length() - 1, 1 );
155 int idx = line.findRev( ":" );
157 ver = line.mid( idx + 1 ).stripWhiteSpace();
162 class SALOME_ResourceMgr : public SUIT_ResourceMgr
165 SALOME_ResourceMgr( const QString& app, const QString& resVarTemplate ) : SUIT_ResourceMgr( app, resVarTemplate )
167 setCurrentFormat( "xml" );
168 setOption( "translators", QString( "%P_msg_%L.qm|%P_icons.qm|%P_images.qm" ) );
169 setDefaultPixmap( QPixmap( pixmap_not_found_xpm ) );
171 static void initResourceMgr()
173 if ( myExtAppName.isNull() || myExtAppVersion.isNull() ) {
174 SALOME_ResourceMgr resMgr( "SalomeApp", QString( "%1Config" ) );
175 resMgr.loadLanguage( "LightApp", "en" );
176 resMgr.loadLanguage( "SalomeApp", "en" );
178 myExtAppName = QObject::tr( "APP_NAME" ).stripWhiteSpace();
179 if ( myExtAppName == "APP_NAME" || myExtAppName.lower() == "salome" )
180 myExtAppName = "SalomeApp";
181 myExtAppVersion = QObject::tr( "APP_VERSION" );
182 if ( myExtAppVersion == "APP_VERSION" ) {
183 if ( myExtAppName != "SalomeApp" )
184 myExtAppVersion = "";
185 else myExtAppVersion = salomeVersion();
189 QString version() const { return myExtAppVersion; }
192 QString userFileName( const QString& appName, const bool for_load ) const
194 if ( version().isNull() ) return "";
195 return SUIT_ResourceMgr::userFileName( myExtAppName, for_load );
198 virtual int userFileId( const QString& _fname ) const
200 if ( !myExtAppName.isEmpty() ) {
201 QRegExp exp( QString( "\\.%1rc\\.([a-zA-Z0-9.]+)$" ).arg( myExtAppName ) );
202 QRegExp vers_exp( "^([0-9]+)([A-Za-z]?)([0-9]*)$" );
204 QString fname = QFileInfo( _fname ).fileName();
205 if( exp.exactMatch( fname ) ) {
206 QStringList vers = QStringList::split( ".", exp.cap( 1 ) );
207 int major=0, minor=0;
208 major = vers[0].toInt();
209 minor = vers[1].toInt();
210 if( vers_exp.search( vers[2] )==-1 )
212 int release = 0, dev1 = 0, dev2 = 0;
213 release = vers_exp.cap( 1 ).toInt();
214 dev1 = vers_exp.cap( 2 )[ 0 ].latin1();
215 dev2 = vers_exp.cap( 3 ).toInt();
217 int dev = dev1*100+dev2, id = major;
219 id*=100; id+=release;
221 if ( dev > 0 ) id+=dev-10000;
230 static QString myExtAppName;
231 static QString myExtAppVersion;
234 QString SALOME_ResourceMgr::myExtAppName = QString::null;
235 QString SALOME_ResourceMgr::myExtAppVersion = QString::null;
237 class SALOME_Session : public SUIT_Session
240 SALOME_Session() : SUIT_Session() {}
241 virtual ~SALOME_Session() {}
244 virtual SUIT_ResourceMgr* createResourceMgr( const QString& appName ) const
246 SALOME_ResourceMgr::initResourceMgr();
247 SALOME_ResourceMgr* resMgr = new SALOME_ResourceMgr( appName, QString( "%1Config" ) );
252 class SALOME_QApplication : public QApplication
255 SALOME_QApplication( int& argc, char** argv ) : QApplication( argc, argv ), myHandler ( 0 ) {}
257 virtual bool notify( QObject* receiver, QEvent* e )
259 #if (OCC_VERSION_MAJOR << 16 | OCC_VERSION_MINOR << 8 | OCC_VERSION_MAINTENANCE) < 0x060101
260 // Disable GUI user actions while python command is executed
261 if (SUIT_Session::IsPythonExecuted()) {
262 // Disable mouse and keyboard events
263 QEvent::Type aType = e->type();
264 if (aType == QEvent::MouseButtonPress || aType == QEvent::MouseButtonRelease ||
265 aType == QEvent::MouseButtonDblClick || aType == QEvent::MouseMove ||
266 aType == QEvent::Wheel || aType == QEvent::ContextMenu ||
267 aType == QEvent::KeyPress || aType == QEvent::KeyRelease ||
268 aType == QEvent::Accel || aType == QEvent::AccelOverride)
273 return myHandler ? myHandler->handle( receiver, e ) :
274 QApplication::notify( receiver, e );
276 SUIT_ExceptionHandler* handler() const { return myHandler; }
277 void setHandler( SUIT_ExceptionHandler* h ) { myHandler = h; }
280 SUIT_ExceptionHandler* myHandler;
283 // class which calls SALOME::Session::GetInterface() from another thread
284 // to avoid mutual lock ( if called from the same thread as main()
285 class GetInterfaceThread : public QThread
288 GetInterfaceThread( SALOME::Session_var s ) : session ( s )
295 if ( !CORBA::is_nil( session ) )
296 session->GetInterface();
298 printf( "\nFATAL ERROR: SALOME::Session object is nil! Can not display GUI\n\n" );
301 SALOME::Session_var session;
304 // returns true if 'str' is found in argv
305 bool isFound( const char* str, int argc, char** argv )
307 for ( int i = 1; i <= ( argc-1 ); i++ )
308 if ( !strcmp( argv[i], str ) )
315 QString fileName( ::getenv ("OMNIORB_CONFIG") );
317 if ( !fileName.isEmpty() )
319 QFile aFile( fileName );
320 if ( aFile.open(IO_ReadOnly) ) {
321 QRegExp re("InitRef = .*:([0-9]+)$");
322 QTextStream stream ( &aFile );
323 while ( !stream.atEnd() ) {
324 QString textLine = stream.readLine();
325 if ( re.search( textLine ) > -1 )
326 portNumber = re.cap(1);
332 if ( !portNumber.isEmpty() )
335 cmd = QString( "ps -eo pid,command | grep -v grep | grep -E \"omniNames.*%1\" | awk '{cmd=sprintf(\"kill -9 %s\",$1); system(cmd)}'" ).arg( portNumber );
336 system ( cmd.latin1() );
339 /////////////////// NPAL 18309 (Kill Notifd) ////////////////////////////
340 if ( !portNumber.isEmpty() )
342 QString cmd = QString("import pickle, os; ");
343 cmd += QString("from killSalomeWithPort import getPiDict; ");
344 cmd += QString("filedict=getPiDict(%1); ").arg(portNumber);
345 cmd += QString("f=open(filedict, 'r'); ");
346 cmd += QString("pids=pickle.load(f); ");
347 cmd += QString("m={}; ");
348 cmd += QString("[ m.update(i) for i in pids ]; ");
349 cmd += QString("pids=filter(lambda a: 'notifd' in m[a], m.keys()); ");
350 cmd += QString("[ os.kill(pid, 9) for pid in pids ]; ");
351 cmd += QString("os.remove(filedict); ");
352 cmd = QString("python -c \"%1\" > /dev/null").arg(cmd);
353 system( cmd.latin1() );
358 // shutdown standalone servers
359 void shutdownServers( SALOME_NamingService* theNS )
361 // get each Container from NamingService => shutdown it
362 // (the order is inverse to the order of servers initialization)
364 CORBA::Object_var objS = theNS->Resolve("/Kernel/Session");
365 SALOME::Session_var session = SALOME::Session::_narrow(objS);
366 if (!CORBA::is_nil(session)) {
369 string hostname = GetHostname();
370 //string containerName = "/Containers/" + hostname;
372 // 1) SuperVisionContainer
373 //string containerNameSV = containerName + "/SuperVisionContainer";
374 //CORBA::Object_var objSV = theNS->Resolve(containerNameSV.c_str());
375 //Engines::Container_var SVcontainer = Engines::Container::_narrow(objSV) ;
376 //if ( !CORBA::is_nil(SVcontainer) && ( session->getPID() != SVcontainer->getPID() ) )
377 // SVcontainer->Shutdown();
379 // 2) FactoryServerPy
380 //string containerNameFSP = containerName + "/FactoryServerPy";
381 //CORBA::Object_var objFSP = theNS->Resolve(containerNameFSP.c_str());
382 //Engines::Container_var FSPcontainer = Engines::Container::_narrow(objFSP) ;
383 //if ( !CORBA::is_nil(FSPcontainer) && ( session->getPID() != FSPcontainer->getPID() ) )
384 // FSPcontainer->Shutdown();
387 //string containerNameFS = containerName + "/FactoryServer";
388 //CORBA::Object_var objFS = theNS->Resolve(containerNameFS.c_str());
389 //Engines::Container_var FScontainer = Engines::Container::_narrow(objFS) ;
390 //if ( !CORBA::is_nil(FScontainer) && ( session->getPID() != FScontainer->getPID() ) )
391 // FScontainer->Shutdown();
393 // 4) ContainerManager
394 //CORBA::Object_var objCM=theNS->Resolve("/ContainerManager");
395 //Engines::ContainerManager_var contMan=Engines::ContainerManager::_narrow(objCM);
396 //if ( !CORBA::is_nil(contMan) && ( session->getPID() != contMan->getPID() ) )
397 // contMan->ShutdownWithExit();
400 CORBA::Object_var objSL = theNS->Resolve("/SalomeLauncher");
401 Engines::SalomeLauncher_var launcher = Engines::SalomeLauncher::_narrow(objSL);
402 if (!CORBA::is_nil(launcher) && (session->getPID() != launcher->getPID()))
403 launcher->Shutdown();
405 // 5) ConnectionManager
406 CORBA::Object_var objCnM=theNS->Resolve("/ConnectionManager");
407 Engines::ConnectionManager_var connMan=Engines::ConnectionManager::_narrow(objCnM);
408 if ( !CORBA::is_nil(connMan) && ( session->getPID() != connMan->getPID() ) )
409 connMan->ShutdownWithExit();
412 CORBA::Object_var objSDS = theNS->Resolve("/myStudyManager");
413 SALOMEDS::StudyManager_var studyManager = SALOMEDS::StudyManager::_narrow(objSDS) ;
414 if ( !CORBA::is_nil(studyManager) && ( session->getPID() != studyManager->getPID() ) )
415 studyManager->Shutdown();
418 CORBA::Object_var objMC=theNS->Resolve("/Kernel/ModulCatalog");
419 SALOME_ModuleCatalog::ModuleCatalog_var catalog = SALOME_ModuleCatalog::ModuleCatalog::_narrow(objMC);
420 if ( !CORBA::is_nil(catalog) && ( session->getPID() != catalog->getPID() ) )
424 CORBA::Object_var objR = theNS->Resolve("/Registry");
425 Registry::Components_var registry = Registry::Components::_narrow(objR);
426 if ( !CORBA::is_nil(registry) && ( session->getPID() != registry->getPID() ) )
427 registry->Shutdown();
434 // ---------------------------- MAIN -----------------------
435 int main( int argc, char **argv )
437 // Install Qt debug messages handler
438 qInstallMsgHandler( MessageOutput );
440 // Create Qt application instance;
441 // this should be done the very first!
442 SALOME_QApplication _qappl( argc, argv );
443 ASSERT( QObject::connect( &_qappl, SIGNAL( lastWindowClosed() ), &_qappl, SLOT( quit() ) ) );
445 // Add application library path (to search style plugin etc...)
446 QString path = QDir::convertSeparators( SUIT_Tools::addSlash( QString( ::getenv( "GUI_ROOT_DIR" ) ) ) + QString( "bin/salome" ) );
447 _qappl.addLibraryPath( path );
449 // Set SALOME style to the application
450 _qappl.setStyle( "salome" );
452 bool isGUI = isFound( "GUI", argc, argv );
453 bool isSplash = isFound( "SPLASH", argc, argv );
454 // Show splash screen (only if both the "GUI" and "SPLASH" parameters are set)
455 QtxSplash* splash = 0;
456 if ( isGUI && isSplash ) {
457 // ...create resource manager
458 SUIT_ResourceMgr resMgr( "SalomeApp", QString( "%1Config" ) );
459 resMgr.setCurrentFormat( "xml" );
460 resMgr.loadLanguage( "LightApp", "en" );
461 // ...get splash preferences
463 resMgr.value( "splash", "image", splashIcon );
464 QPixmap px( splashIcon );
465 if ( px.isNull() ) // try to get splash pixmap from resources
466 px = resMgr.loadPixmap( "LightApp", QObject::tr( "ABOUT_SPLASH" ) );
467 if ( !px.isNull() ) {
468 // ...set splash pixmap
469 splash = QtxSplash::splash( px );
472 if ( resMgr.value( "splash", "margin", splashMargin ) && splashMargin > 0 ) {
473 splash->setMargin( splashMargin );
475 // ...set splash text colors
476 QString splashTextColors;
477 if ( resMgr.value( "splash", "text_colors", splashTextColors ) && !splashTextColors.isEmpty() ) {
478 QStringList colors = QStringList::split( "|", splashTextColors );
480 if ( colors.count() > 0 ) c1 = QColor( colors[0] );
481 if ( colors.count() > 1 ) c2 = QColor( colors[1] );
482 splash->setTextColors( c1, c2 );
485 splash->setTextColors( Qt::white, Qt::black );
487 // ...set splash progress colors
488 QString splashProgressColors;
489 if ( resMgr.value( "splash", "progress_colors", splashProgressColors ) && !splashProgressColors.isEmpty() ) {
490 QStringList colors = QStringList::split( "|", splashProgressColors );
492 int gradType = QtxSplash::Vertical;
493 if ( colors.count() > 0 ) c1 = QColor( colors[0] );
494 if ( colors.count() > 1 ) c2 = QColor( colors[1] );
495 if ( colors.count() > 2 ) gradType = colors[2].toInt();
496 splash->setProgressColors( c1, c2, gradType );
498 // ...set splash text font
499 QFont f = splash->font();
501 splash->setFont( f );
502 // ...show splash initial status
504 if ( resMgr.value( "splash", "info", splashInfo, false ) && !splashInfo.isEmpty() ) {
505 splashInfo.replace( QRegExp( "%A" ), QObject::tr( "APP_NAME" ) );
506 splashInfo.replace( QRegExp( "%V" ), QObject::tr( "ABOUT_VERSION" ).arg( salomeVersion() ) );
507 splashInfo.replace( QRegExp( "%L" ), QObject::tr( "ABOUT_LICENSE" ) );
508 splashInfo.replace( QRegExp( "%C" ), QObject::tr( "ABOUT_COPYRIGHT" ) );
509 splashInfo.replace( QRegExp( "\\\\n" ), "\n" );
510 splash->message( splashInfo );
512 // ...set 'hide on click' flag
514 splash->setHideOnClick( true );
518 qApp->processEvents();
526 PortableServer::POA_var poa;
528 SUIT_Session* aGUISession = 0;
529 SALOME_NamingService* _NS = 0;
530 GetInterfaceThread* guiThread = 0;
531 Session_ServerLauncher* myServerLauncher = 0;
534 // ...initialize Python (only once)
536 char* _argv[] = {""};
537 KERNEL_PYTHON::init_python( _argc,_argv );
538 PyEval_RestoreThread( KERNEL_PYTHON::_gtstate );
539 if ( !KERNEL_PYTHON::salome_shared_modules_module ) // import only once
540 KERNEL_PYTHON::salome_shared_modules_module = PyImport_ImportModule( "salome_shared_modules" );
541 if ( !KERNEL_PYTHON::salome_shared_modules_module ) {
542 INFOS( "salome_shared_modules_module == NULL" );
545 PyEval_ReleaseThread( KERNEL_PYTHON::_gtstate );
547 // ...create ORB, get RootPOA object, NamingService, etc.
548 ORB_INIT &init = *SINGLETON_<ORB_INIT>::Instance();
549 ASSERT( SINGLETON_<ORB_INIT>::IsAlreadyExisting() );
551 orb = init( orbArgc, argv );
553 // ...install SALOME thread event handler
554 SALOME_Event::GetSessionThread();
556 CORBA::Object_var obj = orb->resolve_initial_references( "RootPOA" );
557 poa = PortableServer::POA::_narrow( obj );
559 PortableServer::POAManager_var pman = poa->the_POAManager();
561 MESSAGE( "pman->activate()" );
563 _NS = new SALOME_NamingService( orb );
567 catch ( SALOME_Exception& e ) {
568 INFOS( "run(): SALOME::SALOME_Exception is caught: "<<e.what() );
570 catch ( CORBA::SystemException& e ) {
571 INFOS( "Caught CORBA::SystemException." );
573 catch ( CORBA::Exception& e ) {
574 INFOS( "Caught CORBA::Exception." );
577 CORBA::TypeCode_var tc = tmp.type();
578 const char *p = tc->name();
579 INFOS ( "run(): CORBA exception of the kind : "<<p<< " is caught" );
581 catch ( exception& e ) {
582 INFOS( "run(): An exception has been caught: " <<e.what() );
585 INFOS( "Caught unknown exception." );
588 QMutex _GUIMutex, _SessionMutex, _SplashMutex;
589 QWaitCondition _ServerLaunch, _SessionStarted, _SplashStarted;
591 // lock session mutex to ensure that GetInterface is not called
592 // until all initialization is done
593 _SessionMutex.lock();
596 // Start embedded servers launcher (Registry, SALOMEDS, etc.)
597 // ...lock mutex to block embedded servers launching thread until wait( mutex )
599 // ...create launcher
600 myServerLauncher = new Session_ServerLauncher( argc, argv, orb, poa, &_GUIMutex, &_ServerLaunch, &_SessionMutex, &_SessionStarted );
601 // ...block this thread until launcher is ready
602 _ServerLaunch.wait( &_GUIMutex );
604 // Start servers check thread (splash)
606 // ...lock mutex to block splash thread until wait( mutex )
608 // ...create servers checking thread
609 Session_ServerCheck sc( &_SplashMutex, &_SplashStarted );
610 // ...block this thread until servers checking is finished
611 _SplashStarted.wait( &_SplashMutex );
612 // ...unlock mutex 'cause it is no more needed
613 _SplashMutex.unlock();
614 // get servers checking thread status
615 result = splash->error();
616 QString info = splash->message().isEmpty() ? "%1" : QString( "%1\n%2" ).arg( splash->message() );
617 splash->setStatus( info.arg( "Activating desktop..." ) );
620 // Finalize embedded servers launcher
621 // ...block this thread until launcher is finished
622 _ServerLaunch.wait( &_GUIMutex );
623 // ...unlock mutex 'cause it is no more needed
627 bool shutdown = false;
629 // Launch GUI activator
631 // ...retrieve Session interface reference
632 CORBA::Object_var obj = _NS->Resolve( "/Kernel/Session" );
633 SALOME::Session_var session = SALOME::Session::_narrow( obj ) ;
634 ASSERT ( ! CORBA::is_nil( session ) );
635 // ...create GUI launcher
636 MESSAGE( "Session activated, Launch IAPP..." );
637 guiThread = new GetInterfaceThread( session );
641 // Allow multiple activation/deactivation of GUI
643 MESSAGE( "waiting wakeAll()" );
644 _SessionStarted.wait( &_SessionMutex ); // to be reseased by Launch server thread when ready:
645 // atomic operation lock - unlock on mutex
646 // unlock mutex: serverThread runs, calls _ServerLaunch->wakeAll()
647 // this thread wakes up, and lock mutex
649 _SessionMutex.unlock();
651 // SUIT_Session creation
652 aGUISession = new SALOME_Session();
654 // Load SalomeApp dynamic library
655 MESSAGE( "creation SUIT_Application" );
656 SUIT_Application* aGUIApp = aGUISession->startApplication( "SalomeApp", 0, 0 );
659 if ( !isFound( "noexcepthandler", argc, argv ) )
660 _qappl.setHandler( aGUISession->handler() ); // after loading SalomeApp application
661 // aGUISession contains SalomeApp_ExceptionHandler
663 MESSAGE( "run(): starting the main event loop" );
666 splash->finish( aGUIApp->desktop() );
668 result = _qappl.exec();
674 if ( result == SUIT_Session::NORMAL ) { // desktop is closed by user from GUI
675 shutdown = aGUISession->exitFlags();
683 // Prepare _GUIMutex for a new GUI activation
684 _SessionMutex.lock();
688 // unlock Session mutex
689 _SessionMutex.unlock();
692 shutdownServers( _NS );
694 if ( myServerLauncher )
695 myServerLauncher->KillAll(); // kill embedded servers
699 delete myServerLauncher;
702 PyGILState_STATE gstate = PyGILState_Ensure();
711 //////////////////////////////////////////////////////////////
712 // VSR: silently skip exception:
713 // CORBA.BAD_INV_ORDER.BAD_INV_ORDER_ORBHasShutdown
714 // exception is raised when orb->destroy() is called and
715 // cpp continer is launched in the embedded mode
716 //////////////////////////////////////////////////////////////
717 // std::cerr << "Caught unexpected exception on destroy : ignored !!" << std::endl;