xref: /trunk/main/automation/source/communi/communi.cxx (revision cdf0e10c4e3984b49a9502b011690b615761d4a3)
1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_automation.hxx"
30 #include <stdio.h>
31 #if OSL_DEBUG_LEVEL > 1
32 #define DEBUGPRINTF(x) { printf(x); fflush( stdout ); }
33 #else
34 #define DEBUGPRINTF(x)
35 #endif
36 #include <tools/debug.hxx>
37 #include <vcl/svapp.hxx>
38 #include <vos/socket.hxx>
39 #include <tools/stream.hxx>
40 #include <vcl/timer.hxx>
41 #include <tools/fsys.hxx>
42 
43 #include <automation/communi.hxx>
44 
45 
46 /*  Um den Destruktor protected zu machen wurde unten das delete entfernt.
47     Die Methode wird ohnehin hucht benutzt.
48 //              delete *((AE*)pData+n);
49 */
50 
51 #undef  SV_IMPL_PTRARR_SORT
52 #define SV_IMPL_PTRARR_SORT( nm,AE )\
53 _SV_IMPL_SORTAR_ALG( nm,AE )\
54     void nm::DeleteAndDestroy( sal_uInt16 nP, sal_uInt16 nL ) { \
55         if( nL ) {\
56             DBG_ASSERT( nP < nA && nP + nL <= nA, "ERR_VAR_DEL" );\
57             for( sal_uInt16 n=nP; n < nP + nL; n++ ) \
58                 DBG_ERROR("Das Element der Liste wurde nicht gel�scht"); \
59             SvPtrarr::Remove( nP, nL ); \
60         } \
61     } \
62 _SV_SEEK_PTR( nm, AE )
63 
64 
65 
66 
67 SV_IMPL_PTRARR_SORT( CommunicationLinkList, CommunicationLink* );
68 
69 vos::OMutex *pMPostUserEvent=NULL;      // Notwendig, da nicht threadfest
70 
71 CommunicationLinkViaSocket::CommunicationLinkViaSocket( CommunicationManager *pMan, vos::OStreamSocket *pSocket )
72 : SimpleCommunicationLinkViaSocket( pMan, pSocket )
73 , nConnectionClosedEventId( 0 )
74 , nDataReceivedEventId( 0 )
75 , bShutdownStarted( sal_False )
76 , bDestroying( sal_False )
77 {
78     SetPutDataReceivedHdl(LINK( this, CommunicationLinkViaSocket, PutDataReceivedHdl ));
79     if ( !pMPostUserEvent )
80         pMPostUserEvent = new vos::OMutex;
81     // this is necassary to prevent the running thread from sending the close event
82     // before the open event has been sent.
83     StartCallback();
84 
85     create();
86 }
87 
88 CommunicationLinkViaSocket::~CommunicationLinkViaSocket()
89 {
90     bDestroying = sal_True;
91     StopCommunication();
92     while ( nConnectionClosedEventId || nDataReceivedEventId )
93         GetpApp()->Yield();
94     {
95         vos::OGuard aGuard( aMConnectionClosed );
96         if ( nConnectionClosedEventId )
97         {
98             GetpApp()->RemoveUserEvent( nConnectionClosedEventId );
99             nConnectionClosedEventId = 0;
100             INFO_MSG( CByteString("Event gel�scht"),
101                 CByteString( "ConnectionClosedEvent aus Queue gel�scht"),
102                 CM_MISC, NULL );
103         }
104     }
105     {
106         vos::OGuard aGuard( aMDataReceived );
107         if ( nDataReceivedEventId )
108         {
109             GetpApp()->RemoveUserEvent( nDataReceivedEventId );
110             nDataReceivedEventId = 0;
111             delete GetServiceData();
112             INFO_MSG( CByteString("Event gel�scht"),
113                 CByteString( "DataReceivedEvent aus Queue gel�scht"),
114                 CM_MISC, NULL );
115         }
116     }
117 }
118 
119 sal_Bool CommunicationLinkViaSocket::ShutdownCommunication()
120 {
121     if ( isRunning() )
122     {
123 
124         terminate();
125         if ( GetStreamSocket() )
126             GetStreamSocket()->shutdown();
127 
128         if ( GetStreamSocket() )    // Mal wieder nach oben verschoben, da sonst nicht vom Read runtergesprungen wird.
129             GetStreamSocket()->close();
130 
131         resume();   // So da� das run auch die Schleife verlassen kann
132 
133         join();
134 
135         vos::OStreamSocket *pTempSocket = GetStreamSocket();
136         SetStreamSocket( NULL );
137         delete pTempSocket;
138 
139 //      ConnectionClosed();     Wird am Ende des Thread gerufen
140 
141     }
142     else
143     {
144         join();
145     }
146 
147     return sal_True;
148 }
149 
150 sal_Bool CommunicationLinkViaSocket::StopCommunication()
151 {
152     if ( !bShutdownStarted )
153     {
154         return SimpleCommunicationLinkViaSocket::StopCommunication();
155     }
156     else
157     {
158         WaitForShutdown();
159         return sal_True;
160     }
161 }
162 
163 
164 IMPL_LINK( CommunicationLinkViaSocket, ShutdownLink, void*, EMPTYARG )
165 {
166     if ( !IsCommunicationError() )
167         ShutdownCommunication();
168     return 0;
169 }
170 
171 
172 void CommunicationLinkViaSocket::WaitForShutdown()
173 {
174     if ( !bShutdownStarted )
175     {
176         aShutdownTimer.SetTimeout( 30000 );     // Should be 30 Seconds
177         aShutdownTimer.SetTimeoutHdl( LINK( this, CommunicationLinkViaSocket, ShutdownLink ) );
178         aShutdownTimer.Start();
179         bShutdownStarted = sal_True;
180     }
181     if ( bDestroying )
182     {
183         while ( pMyManager && aShutdownTimer.IsActive() )
184         {
185             if ( IsCommunicationError() )
186                 return;
187             GetpApp()->Yield();
188         }
189         ShutdownCommunication();
190     }
191 }
192 
193 sal_Bool CommunicationLinkViaSocket::IsCommunicationError()
194 {
195     return !isRunning() || SimpleCommunicationLinkViaSocket::IsCommunicationError();
196 }
197 
198 void CommunicationLinkViaSocket::run()
199 {
200     sal_Bool bWasError = sal_False;
201     while ( schedule() && !bWasError && GetStreamSocket() )
202     {
203         if ( bWasError |= !DoReceiveDataStream() )
204             continue;
205 
206         TimeValue sNochEins = {0, 1000000};
207         while ( schedule() && bIsInsideCallback )   // solange der letzte Callback nicht beendet ist
208             sleep( sNochEins );
209         SetNewPacketAsCurrent();
210         StartCallback();
211         {
212             vos::OGuard aGuard( aMDataReceived );
213             vos::OGuard aGuard2( *pMPostUserEvent );
214             mlPutDataReceived.Call(this);
215         }
216     }
217     TimeValue sNochEins = {0, 1000000};
218     while ( schedule() && bIsInsideCallback )   // solange der letzte Callback nicht beendet ist
219         sleep( sNochEins );
220 
221     StartCallback();
222     {
223         vos::OGuard aGuard( aMConnectionClosed );
224         vos::OGuard aGuard2( *pMPostUserEvent );
225         nConnectionClosedEventId = GetpApp()->PostUserEvent( LINK( this, CommunicationLinkViaSocket, ConnectionClosed ) );
226     }
227 }
228 
229 sal_Bool CommunicationLinkViaSocket::DoTransferDataStream( SvStream *pDataStream, CMProtocol nProtocol )
230 {
231     if ( !isRunning() )
232         return sal_False;
233 
234     return SimpleCommunicationLinkViaSocket::DoTransferDataStream( pDataStream, nProtocol );
235 }
236 
237 /// Dies ist ein virtueller Link!!!
238 long CommunicationLinkViaSocket::ConnectionClosed( void* EMPTYARG )
239 {
240     {
241         vos::OGuard aGuard( aMConnectionClosed );
242         nConnectionClosedEventId = 0;   // Achtung!! alles andere mu� oben gemacht werden.
243     }
244     ShutdownCommunication();
245     return CommunicationLink::ConnectionClosed( );
246 }
247 
248 /// Dies ist ein virtueller Link!!!
249 long CommunicationLinkViaSocket::DataReceived( void* EMPTYARG )
250 {
251     {
252         vos::OGuard aGuard( aMDataReceived );
253         nDataReceivedEventId = 0;   // Achtung!! alles andere mu� oben gemacht werden.
254     }
255     return CommunicationLink::DataReceived( );
256 }
257 
258 IMPL_LINK( CommunicationLinkViaSocket, PutDataReceivedHdl, CommunicationLinkViaSocket*, EMPTYARG )
259 {
260     nDataReceivedEventId = GetpApp()->PostUserEvent( LINK( this, CommunicationLink, DataReceived ) );
261     return 0;
262 }
263 
264 
265 
266 MultiCommunicationManager::MultiCommunicationManager( sal_Bool bUseMultiChannel )
267 : CommunicationManager( bUseMultiChannel )
268 , bGracefullShutdown( sal_True )
269 {
270     ActiveLinks = new CommunicationLinkList;
271     InactiveLinks = new CommunicationLinkList;
272 }
273 
274 MultiCommunicationManager::~MultiCommunicationManager()
275 {
276     StopCommunication();
277 
278     if ( bGracefullShutdown )   // first try to collect all callbacks for closing channels
279     {
280         Timer aTimeout;
281         aTimeout.SetTimeout( 40000 );
282         aTimeout.Start();
283         sal_uInt16 nLinkCount = 0;
284         sal_uInt16 nNewLinkCount = 0;
285         while ( aTimeout.IsActive() )
286         {
287             GetpApp()->Yield();
288             nNewLinkCount = GetCommunicationLinkCount();
289             if ( nNewLinkCount == 0 )
290                 aTimeout.Stop();
291             if ( nNewLinkCount != nLinkCount )
292             {
293                 aTimeout.Start();
294                 nLinkCount = nNewLinkCount;
295             }
296         }
297     }
298 
299     // Alles weghauen, was nicht rechtzeitig auf die B�ume gekommen ist
300     // Was bei StopCommunication �brig geblieben ist, da es sich asynchron austragen wollte
301     sal_uInt16 i = ActiveLinks->Count();
302     while ( i-- )
303     {
304         CommunicationLinkRef rTempLink = ActiveLinks->GetObject( i );
305         ActiveLinks->Remove( i );
306         rTempLink->InvalidateManager();
307         rTempLink->ReleaseReference();
308     }
309     delete ActiveLinks;
310 
311     /// Die Links zwischen ConnectionClosed und Destruktor.
312     /// Hier NICHT gerefcounted, da sie sich sonst im Kreis festhaten w�rden,
313     /// da die Links sich erst in ihrem Destruktor austragen
314     i = InactiveLinks->Count();
315     while ( i-- )
316     {
317         CommunicationLinkRef rTempLink = InactiveLinks->GetObject( i );
318         InactiveLinks->Remove( i );
319         rTempLink->InvalidateManager();
320     }
321     delete InactiveLinks;
322 }
323 
324 sal_Bool MultiCommunicationManager::StopCommunication()
325 {
326     // Alle Verbindungen abbrechen
327     // ConnectionClosed entfernt die Links aus der Liste. Je nach Implementation syncron
328     // oder asyncron. Daher Von oben nach unten Abr�umen, so da� sich nichts verschiebt.
329     sal_uInt16 i = ActiveLinks->Count();
330     int nFail = 0;
331     while ( i )
332     {
333         if ( !ActiveLinks->GetObject(i-1)->StopCommunication() )
334             nFail++;    // Hochz�hlen, da Verbindung sich nicht (sofort) beenden l�sst.
335         i--;
336     }
337 
338     return nFail == 0;
339 }
340 
341 sal_Bool MultiCommunicationManager::IsLinkValid( CommunicationLink* pCL )
342 {
343     if ( ActiveLinks->Seek_Entry( pCL ) )
344         return sal_True;
345     else
346         return sal_False;
347 }
348 
349 sal_uInt16 MultiCommunicationManager::GetCommunicationLinkCount()
350 {
351     return ActiveLinks->Count();
352 }
353 
354 CommunicationLinkRef MultiCommunicationManager::GetCommunicationLink( sal_uInt16 nNr )
355 {
356     return ActiveLinks->GetObject( nNr );
357 }
358 
359 void MultiCommunicationManager::CallConnectionOpened( CommunicationLink* pCL )
360 {
361     CommunicationLinkRef rHold(pCL);    // H�lt den Zeiger bis zum Ende des calls
362     ActiveLinks->C40_PTR_INSERT(CommunicationLink, pCL);
363     rHold->AddRef();
364 
365     CommunicationManager::CallConnectionOpened( pCL );
366 }
367 
368 void MultiCommunicationManager::CallConnectionClosed( CommunicationLink* pCL )
369 {
370     CommunicationLinkRef rHold(pCL);    // H�lt denm Zeiger bis zum Ende des calls
371 
372     CommunicationManager::CallConnectionClosed( pCL );
373 
374     sal_uInt16 nPos;
375     if ( ActiveLinks->Seek_Entry( pCL, &nPos ) )
376     {
377         InactiveLinks->C40_PTR_INSERT(CommunicationLink, pCL);  // Ohne Reference
378         ActiveLinks->Remove( nPos );
379     }
380     pCL->ReleaseReference();
381 
382     bIsCommunicationRunning = ActiveLinks->Count() > 0;
383 //  delete pCL;
384 #if OSL_DEBUG_LEVEL > 1
385         rHold->bFlag = sal_True;
386 #endif
387 }
388 
389 void MultiCommunicationManager::DestroyingLink( CommunicationLink *pCL )
390 {
391     sal_uInt16 nPos;
392     if ( InactiveLinks->Seek_Entry( pCL, &nPos ) )
393         InactiveLinks->Remove( nPos );
394     pCL->InvalidateManager();
395 }
396 
397 
398 
399 CommunicationManagerClient::CommunicationManagerClient( sal_Bool bUseMultiChannel )
400 : MultiCommunicationManager( bUseMultiChannel )
401 {
402     ByteString aApplication("Something inside ");
403     aApplication.Append( ByteString( DirEntry( Application::GetAppFileName() ).GetName(), gsl_getSystemTextEncoding() ) );
404     SetApplication( aApplication );
405 }
406 
407 
408 
409 CommunicationManagerServerViaSocket::CommunicationManagerServerViaSocket( sal_uLong nPort, sal_uInt16 nMaxCon, sal_Bool bUseMultiChannel )
410 : CommunicationManagerServer( bUseMultiChannel )
411 , nPortToListen( nPort )
412 , nMaxConnections( nMaxCon )
413 , pAcceptThread( NULL )
414 {
415 }
416 
417 CommunicationManagerServerViaSocket::~CommunicationManagerServerViaSocket()
418 {
419     StopCommunication();
420 }
421 
422 sal_Bool CommunicationManagerServerViaSocket::StartCommunication()
423 {
424     if ( !pAcceptThread )
425         pAcceptThread = new CommunicationManagerServerAcceptThread( this, nPortToListen, nMaxConnections );
426     return sal_True;
427 }
428 
429 
430 sal_Bool CommunicationManagerServerViaSocket::StopCommunication()
431 {
432     // Erst den Acceptor anhalten
433     delete pAcceptThread;
434     pAcceptThread = NULL;
435 
436     // Dann alle Verbindungen kappen
437     return CommunicationManagerServer::StopCommunication();
438 }
439 
440 
441 void CommunicationManagerServerViaSocket::AddConnection( CommunicationLink *pNewConnection )
442 {
443     CallConnectionOpened( pNewConnection );
444 }
445 
446 
447 CommunicationManagerServerAcceptThread::CommunicationManagerServerAcceptThread( CommunicationManagerServerViaSocket* pServer, sal_uLong nPort, sal_uInt16 nMaxCon )
448 : pMyServer( pServer )
449 , pAcceptorSocket( NULL )
450 , nPortToListen( nPort )
451 , nMaxConnections( nMaxCon )
452 , nAddConnectionEventId( 0 )
453 , xmNewConnection( NULL )
454 {
455     if ( !pMPostUserEvent )
456         pMPostUserEvent = new vos::OMutex;
457     create();
458 }
459 
460 
461 CommunicationManagerServerAcceptThread::~CommunicationManagerServerAcceptThread()
462 {
463 #ifndef aUNX        // Weil das Accept nicht abgebrochen werden kann, so terminiert wenigstens das Prog
464     // #62855# pl: gilt auch bei anderen Unixen
465     // die richtige Loesung waere natuerlich, etwas auf die pipe zu schreiben,
466     // was der thread als Abbruchbedingung erkennt
467     // oder wenigstens ein kill anstatt join
468     terminate();
469     if ( pAcceptorSocket )
470         pAcceptorSocket->close();   // Dann das Accept unterbrechen
471 
472     join();     // Warten bis fertig
473 
474     if ( pAcceptorSocket )
475     {
476         delete pAcceptorSocket;
477         pAcceptorSocket = NULL;
478     }
479 #else
480     DEBUGPRINTF ("Destructor CommunicationManagerServerAcceptThread �bersprungen!!!! (wegen Solaris BUG)\n");
481 #endif
482     {
483         vos::OGuard aGuard( aMAddConnection );
484         if ( nAddConnectionEventId )
485         {
486             GetpApp()->RemoveUserEvent( nAddConnectionEventId );
487             nAddConnectionEventId = 0;
488             CommunicationLinkRef xNewConnection = GetNewConnection();
489             INFO_MSG( CByteString("Event gel�scht"),
490                 CByteString( "AddConnectionEvent aus Queue gel�scht"),
491                 CM_MISC, xNewConnection );
492             xNewConnection->InvalidateManager();
493             xNewConnection.Clear(); // sollte das Objekt hier l�schen
494         }
495     }
496 }
497 
498 void CommunicationManagerServerAcceptThread::run()
499 {
500     if ( !nPortToListen )
501         return;
502 
503     pAcceptorSocket = new vos::OAcceptorSocket();
504     vos::OInetSocketAddr Addr;
505     Addr.setPort( nPortToListen );
506     pAcceptorSocket->setReuseAddr( 1 );
507     if ( !pAcceptorSocket->bind( Addr ) )
508     {
509         return;
510     }
511     if ( !pAcceptorSocket->listen( nMaxConnections ) )
512     {
513         return;
514     }
515 
516 
517     vos::OStreamSocket *pStreamSocket = NULL;
518 
519     while ( schedule() )
520     {
521         pStreamSocket = new vos::OStreamSocket;
522         switch ( pAcceptorSocket->acceptConnection( *pStreamSocket ) )
523         {
524         case vos::ISocketTypes::TResult_Ok:
525             {
526                 pStreamSocket->setTcpNoDelay( 1 );
527 
528                 TimeValue sNochEins = {0, 100};
529                 while ( schedule() && xmNewConnection.Is() )    // Solange die letzte Connection nicht abgeholt wurde warten wir
530                     sleep( sNochEins );
531                 xmNewConnection = new CommunicationLinkViaSocket( pMyServer, pStreamSocket );
532                 xmNewConnection->StartCallback();
533                 {
534                     vos::OGuard aGuard( aMAddConnection );
535                     vos::OGuard aGuard2( *pMPostUserEvent );
536                     nAddConnectionEventId = GetpApp()->PostUserEvent( LINK( this, CommunicationManagerServerAcceptThread, AddConnection ) );
537                 }
538             }
539             break;
540         case vos::ISocketTypes::TResult_TimedOut:
541             delete pStreamSocket;
542             pStreamSocket = NULL;
543             break;
544         case vos::ISocketTypes::TResult_Error:
545             delete pStreamSocket;
546             pStreamSocket = NULL;
547             break;
548 
549         case vos::ISocketTypes::TResult_Interrupted:
550         case vos::ISocketTypes::TResult_InProgress:
551             break;  // -Wall not handled...
552         }
553     }
554 }
555 
556 
557 IMPL_LINK( CommunicationManagerServerAcceptThread, AddConnection, void*, EMPTYARG )
558 {
559     {
560         vos::OGuard aGuard( aMAddConnection );
561         nAddConnectionEventId = 0;
562     }
563     pMyServer->AddConnection( xmNewConnection );
564     xmNewConnection.Clear();
565     return 1;
566 }
567 
568 
569 #define GETSET(aVar, KeyName, Dafault)                 \
570     aVar = aConf.ReadKey(KeyName,"No Entry");          \
571     if ( aVar == "No Entry" )                          \
572     {                                                  \
573         aVar = Dafault;                                \
574         aConf.WriteKey(KeyName, aVar);                 \
575     }
576 
577 
578 CommunicationManagerClientViaSocket::CommunicationManagerClientViaSocket( ByteString aHost, sal_uLong nPort, sal_Bool bUseMultiChannel )
579 : CommunicationManagerClient( bUseMultiChannel )
580 , aHostToTalk( aHost )
581 , nPortToTalk( nPort )
582 {
583 }
584 
585 CommunicationManagerClientViaSocket::CommunicationManagerClientViaSocket( sal_Bool bUseMultiChannel )
586 : CommunicationManagerClient( bUseMultiChannel )
587 , aHostToTalk( "" )
588 , nPortToTalk( 0 )
589 {
590 }
591 
592 CommunicationManagerClientViaSocket::~CommunicationManagerClientViaSocket()
593 {
594 }
595 
596 
597