Salome HOME
try () catch () mechanism modified.
[modules/gui.git] / src / Session / SALOME_Session_Server.cxx
1 // SALOME Session : implementation of Session.idl
2 //
3 // Copyright (C) 2003 OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
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.
10 //
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.
15 //
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
19 //
20 // See http://www.opencascade.org/SALOME/ or email : webmaster.salome@opencascade.org
21 //
22 //
23 //
24 // File : SALOME_Session_Server.cxx
25 // Author : Paul RASCLE, EDF
26 // Module : SALOME
27
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"
33
34 #include "InquireServersQThread.h" // splash
35
36 #include <iostream>
37 #include <unistd.h>
38
39 #include <qdir.h>
40 #include <qfile.h>
41 #include <qapplication.h>
42 #include <qwaitcondition.h>
43
44 #include "Utils_SALOME_Exception.hxx"
45 #include "Utils_CorbaException.hxx"
46 #include "SALOME_Event.hxx"
47
48 #include <SALOMEconfig.h>
49 #include CORBA_SERVER_HEADER(SALOME_Session)
50 #include CORBA_SERVER_HEADER(SALOMEDS)
51
52 #include <utilities.h>
53 #include "Session_Session_i.hxx"
54 #include "Session_ServerLauncher.hxx"
55
56 #include "SUIT_Tools.h"
57 #include "SUIT_Session.h"
58 #include "SUIT_Application.h"
59 #include "SUIT_MessageBox.h"
60
61 #include "SUIT_ExceptionHandler.h"
62
63 extern "C" int HandleSignals( QApplication *theQApplication );
64
65 /*! - read arguments, define list of server to launch with their arguments.
66  * - wait for naming service
67  * - create and run a thread for launch of all servers
68  *
69 */
70
71 //! CORBA server for SALOME Session
72 /*!
73  * SALOME_Session Server launches a SALOME session servant.
74  * The servant registers to the Naming Service.
75  * See SALOME_Session.idl for interface specification.
76  *
77  * Main services offered by the servant are:
78  * - launch GUI
79  * - stop Session ( must be idle )
80  * - get session state
81  */
82
83 QString salomeVersion()
84 {
85   QString path( ::getenv( "GUI_ROOT_DIR" ) );
86   if ( !path.isEmpty() )
87     path += QDir::separator();
88   path += QString( "bin/salome/VERSION" );
89
90   QFile vf( path );
91   if ( !vf.open( IO_ReadOnly ) )
92     return QString::null;
93
94   QString line;
95   vf.readLine( line, 1024 );
96   vf.close();
97
98   if ( line.isEmpty() )
99     return QString::null;
100
101   while ( !line.isEmpty() && line.at( line.length() - 1 ) == QChar( '\n' ) )
102     line.remove( line.length() - 1, 1 );
103
104   QString ver;
105   int idx = line.findRev( ":" );
106   if ( idx != -1 )
107     ver = line.mid( idx + 1 ).stripWhiteSpace();
108
109   return ver;
110 }
111
112 PyObject* salome_shared_modules_module = 0;
113
114 void MessageOutput( QtMsgType type, const char* msg )
115 {
116   switch ( type )
117   {
118   case QtDebugMsg:
119     MESSAGE( "Debug: " << msg );
120     break;
121   case QtWarningMsg:
122     MESSAGE( "Warning: " << msg );
123     break;
124   case QtFatalMsg:
125     MESSAGE( "Fatal: " << msg );
126     break;
127   }
128 }
129
130 class SALOME_Session : public SUIT_Session
131 {
132 public:
133   SALOME_Session() : SUIT_Session() {}
134   virtual ~SALOME_Session() {}
135
136 protected:
137   virtual SUIT_ResourceMgr* createResourceMgr( const QString& appName ) const
138   {
139     SUIT_ResourceMgr* resMgr = new SUIT_ResourceMgr( appName, QString( "%1Config" ) );
140     resMgr->setVersion( salomeVersion() );
141     resMgr->setCurrentFormat( "xml" );
142     resMgr->setOption( "translators", QString( "%P_msg_%L.qm|%P_icons.qm|%P_images.qm" ) );
143     return resMgr;
144   }
145 };
146
147 class SALOME_QApplication : public QApplication
148 {
149 public:
150   SALOME_QApplication( int& argc, char** argv ) : QApplication( argc, argv ), myHandler ( 0 ) {}
151
152   virtual bool notify( QObject* receiver, QEvent* e )
153   {
154     return myHandler ? myHandler->handle( receiver, e ) :
155       QApplication::notify( receiver, e );
156   }
157   SUIT_ExceptionHandler* handler() const { return myHandler; }
158   void setHandler( SUIT_ExceptionHandler* h ) { myHandler = h; }
159
160 private:
161   SUIT_ExceptionHandler* myHandler;
162 };
163
164 // class which calls SALOME::Session::GetInterface() from another thread
165 // to avoid mutual lock ( if called from the same thread as main()
166 class GetInterfaceThread : public QThread
167 {
168 public:
169   GetInterfaceThread( SALOME::Session_var s ) : session ( s ) {}
170 protected:
171   virtual void run()
172   {
173     if ( !CORBA::is_nil( session ) )
174       session->GetInterface();
175     else
176       printf( "\nFATAL ERROR: SALOME::Session object is nil! Can not display GUI\n\n" );
177   }
178 private:
179   SALOME::Session_var session;
180 };
181
182 // returns true if 'str' is found in argv
183 bool isFound( const char* str, int argc, char** argv )
184 {
185   for ( int i = 1; i <= ( argc-1 ); i++ )
186     if ( !strcmp( argv[i], str ) )
187       return true;
188   return false;
189 }
190
191 // ---------------------------- MAIN -----------------------
192 int main( int argc, char **argv )
193 {
194   qInstallMsgHandler( MessageOutput );
195
196   // QApplication should be create before all other operations
197   // When uses QApplication::libraryPaths() ( example, QFile::encodeName() )
198   // qApp used for detection of the executable dir path.
199   SALOME_QApplication _qappl( argc, argv );
200   ASSERT( QObject::connect( &_qappl, SIGNAL( lastWindowClosed() ), &_qappl, SLOT( quit() ) ) );
201
202   QString path = QDir::convertSeparators( SUIT_Tools::addSlash( QString( ::getenv( "GUI_ROOT_DIR" ) ) ) + QString( "bin/salome" ) );
203   _qappl.addLibraryPath( path );
204   
205   _qappl.setStyle( "salome" );
206
207   int result = -1;
208
209   CORBA::ORB_var orb;
210   PortableServer::POA_var poa;
211
212   SUIT_Session* aGUISession = 0;
213   SALOME_NamingService* _NS = 0;
214   GetInterfaceThread* guiThread = 0;
215   SALOMETraceCollector* myThreadTrace = 0;
216   Session_ServerLauncher* myServerLauncher = 0;
217
218   try {
219     
220     // Python initialisation : only once
221
222     int _argc = 1;
223     char* _argv[] = {""};
224     KERNEL_PYTHON::init_python( _argc,_argv );
225     PyEval_RestoreThread( KERNEL_PYTHON::_gtstate );
226     if ( !KERNEL_PYTHON::salome_shared_modules_module ) // import only once
227       KERNEL_PYTHON::salome_shared_modules_module = PyImport_ImportModule( "salome_shared_modules" );
228     if ( !KERNEL_PYTHON::salome_shared_modules_module )
229     {
230       INFOS( "salome_shared_modules_module == NULL" );
231       PyErr_Print();
232       PyErr_Clear();
233     }
234     PyEval_ReleaseThread( KERNEL_PYTHON::_gtstate );
235
236     // Create ORB, get RootPOA object, NamingService, etc.
237     ORB_INIT &init = *SINGLETON_<ORB_INIT>::Instance();
238     ASSERT( SINGLETON_<ORB_INIT>::IsAlreadyExisting() );
239     int orbArgc = 1;
240     orb = init( orbArgc, argv );
241     myThreadTrace = SALOMETraceCollector::instance( orb );
242
243     // Install SALOME thread event handler
244     SALOME_Event::GetSessionThread();
245
246     CORBA::Object_var obj = orb->resolve_initial_references( "RootPOA" );
247     poa = PortableServer::POA::_narrow( obj );
248
249     PortableServer::POAManager_var pman = poa->the_POAManager();
250     pman->activate() ;
251     INFOS( "pman->activate()" );
252
253     _NS = new SALOME_NamingService( orb );
254
255     result = 0;
256   }
257   catch ( SALOME_Exception& e ) {
258     INFOS( "run(): SALOME::SALOME_Exception is caught: "<<e.what() );
259   }
260   catch ( CORBA::SystemException& e ) {
261     INFOS( "Caught CORBA::SystemException." );
262   }
263   catch ( CORBA::Exception& e ) {
264     INFOS( "Caught CORBA::Exception." );
265     CORBA::Any tmp;
266     tmp<<= e;
267     CORBA::TypeCode_var tc = tmp.type();
268     const char *p = tc->name();
269     INFOS ( "run(): CORBA exception of the kind : "<<p<< " is caught" );
270   }
271   catch ( exception& e ) {
272     INFOS( "run(): An exception has been caught: " <<e.what() );
273   }
274   catch (...) {
275     INFOS( "Caught unknown exception." );
276   }
277
278   // CORBA Servant Launcher
279   QMutex _GUIMutex;
280   QWaitCondition _ServerLaunch, _SessionStarted;
281
282   if ( !result )
283   {
284     _GUIMutex.lock();  // to block Launch server thread until wait( mutex )
285
286     // Activate embedded CORBA servers: Registry, SALOMEDS, etc.
287     myServerLauncher = new Session_ServerLauncher( argc, argv, orb, poa, &_GUIMutex, &_ServerLaunch, &_SessionStarted );
288     myServerLauncher->start();
289
290     _ServerLaunch.wait( &_GUIMutex ); // to be reseased by Launch server thread when ready:
291     
292     // show splash screen if "SPLASH" parameter was passed ( default )
293     if ( isFound( "SPLASH", argc, argv ) )
294     {
295       // create temporary resource manager just to load splash icon
296       SUIT_ResourceMgr resMgr( "SalomeApp", QString( "%1Config" ) );
297       resMgr.setVersion( salomeVersion() );
298       resMgr.setCurrentFormat( "xml" );
299       resMgr.loadLanguage( "SalomeApp", "en" );
300
301       // create splash object: widget ( splash with progress bar ) and "pinging" thread
302       InquireServersGUI splash;
303       splash.setPixmap( resMgr.loadPixmap( "SalomeApp", QObject::tr( "ABOUT" ) ) );
304       SUIT_Tools::centerWidget( &splash, _qappl.desktop() );
305       
306       _qappl.setMainWidget( &splash );
307       QObject::connect( &_qappl, SIGNAL( lastWindowClosed() ), &_qappl, SLOT( quit() ) );
308       splash.show(); // display splash with running progress bar
309       _qappl.exec(); // wait untill splash closes ( progress runs till end or Cancel is pressed )
310       
311       result = splash.getExitStatus(); // 1 is error
312     }
313     else
314       _SessionStarted.wait();
315   }
316
317   // call Session::GetInterface() if "GUI" parameter was passed ( default )
318   if ( !result && isFound( "GUI", argc, argv ) )
319   {
320     CORBA::Object_var obj = _NS->Resolve( "/Kernel/Session" );
321     SALOME::Session_var session = SALOME::Session::_narrow( obj ) ;
322     ASSERT ( ! CORBA::is_nil( session ) );
323
324     INFOS( "Session activated, Launch IAPP..." );
325     guiThread = new GetInterfaceThread( session );
326     guiThread->start();
327   }
328
329   if ( !result )
330   {
331
332     // GUI activation
333     // Allow multiple activation/deactivation of GUI
334     while ( true )
335     {
336       MESSAGE( "waiting wakeAll()" );
337       _ServerLaunch.wait( &_GUIMutex ); // to be reseased by Launch server thread when ready:
338       // atomic operation lock - unlock on mutex
339       // unlock mutex: serverThread runs, calls _ServerLaunch->wakeAll()
340       // this thread wakes up, and lock mutex
341
342       _GUIMutex.unlock();
343
344       // SUIT_Session creation
345       aGUISession = new SALOME_Session();
346
347       // Load SalomeApp dynamic library
348       INFOS( "creation SUIT_Application" );
349       SUIT_Application* aGUIApp = aGUISession->startApplication( "SalomeApp", 0, 0 );
350       if ( aGUIApp )
351       {
352         _qappl.setHandler( aGUISession->handler() ); // after loading SalomeApp application
353                                                      // aGUISession contains SalomeApp_ExceptionHandler
354         // Run GUI loop
355         MESSAGE( "run(): starting the main event loop" );
356         result = _qappl.exec();
357
358         if ( result == SUIT_Session::FROM_GUI ) // desktop is closed by user from GUI
359           break;
360       }
361
362       delete aGUISession;
363       aGUISession = 0;
364
365       // Prepare _GUIMutex for a new GUI activation
366       _GUIMutex.lock();
367     }
368   }
369
370   if ( myServerLauncher )
371     myServerLauncher->KillAll(); // kill embedded servers
372
373   delete aGUISession;
374   delete guiThread;
375   delete myServerLauncher;
376   delete _NS;
377   delete myThreadTrace;
378
379   return result;
380 }