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