blob: b0df42903c8e9fcbef6b0a04b10430c4b8afa620 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_automation.hxx"
#include <stdio.h>
#if OSL_DEBUG_LEVEL > 1
#define DEBUGPRINTF(x) { printf(x); fflush( stdout ); }
#else
#define DEBUGPRINTF(x)
#endif
#include <tools/debug.hxx>
#include <vcl/svapp.hxx>
#include <vos/socket.hxx>
#include <tools/stream.hxx>
#include <vcl/timer.hxx>
#include <tools/fsys.hxx>
#include <automation/communi.hxx>
/* Um den Destruktor protected zu machen wurde unten das delete entfernt.
Die Methode wird ohnehin hucht benutzt.
// delete *((AE*)pData+n);
*/
#undef SV_IMPL_PTRARR_SORT
#define SV_IMPL_PTRARR_SORT( nm,AE )\
_SV_IMPL_SORTAR_ALG( nm,AE )\
void nm::DeleteAndDestroy( sal_uInt16 nP, sal_uInt16 nL ) { \
if( nL ) {\
DBG_ASSERT( nP < nA && nP + nL <= nA, "ERR_VAR_DEL" );\
for( sal_uInt16 n=nP; n < nP + nL; n++ ) \
DBG_ERROR("Das Element der Liste wurde nicht gelöscht"); \
SvPtrarr::Remove( nP, nL ); \
} \
} \
_SV_SEEK_PTR( nm, AE )
SV_IMPL_PTRARR_SORT( CommunicationLinkList, CommunicationLink* );
vos::OMutex *pMPostUserEvent=NULL; // Notwendig, da nicht threadfest
CommunicationLinkViaSocket::CommunicationLinkViaSocket( CommunicationManager *pMan, vos::OStreamSocket *pSocket )
: SimpleCommunicationLinkViaSocket( pMan, pSocket )
, nConnectionClosedEventId( 0 )
, nDataReceivedEventId( 0 )
, bShutdownStarted( sal_False )
, bDestroying( sal_False )
{
SetPutDataReceivedHdl(LINK( this, CommunicationLinkViaSocket, PutDataReceivedHdl ));
if ( !pMPostUserEvent )
pMPostUserEvent = new vos::OMutex;
// this is necassary to prevent the running thread from sending the close event
// before the open event has been sent.
StartCallback();
create();
}
CommunicationLinkViaSocket::~CommunicationLinkViaSocket()
{
bDestroying = sal_True;
StopCommunication();
while ( nConnectionClosedEventId || nDataReceivedEventId )
GetpApp()->Yield();
{
vos::OGuard aGuard( aMConnectionClosed );
if ( nConnectionClosedEventId )
{
GetpApp()->RemoveUserEvent( nConnectionClosedEventId );
nConnectionClosedEventId = 0;
INFO_MSG( CByteString("Event gelöscht"),
CByteString( "ConnectionClosedEvent aus Queue gelöscht"),
CM_MISC, NULL );
}
}
{
vos::OGuard aGuard( aMDataReceived );
if ( nDataReceivedEventId )
{
GetpApp()->RemoveUserEvent( nDataReceivedEventId );
nDataReceivedEventId = 0;
delete GetServiceData();
INFO_MSG( CByteString("Event gelöscht"),
CByteString( "DataReceivedEvent aus Queue gelöscht"),
CM_MISC, NULL );
}
}
}
sal_Bool CommunicationLinkViaSocket::ShutdownCommunication()
{
if ( isRunning() )
{
terminate();
if ( GetStreamSocket() )
GetStreamSocket()->shutdown();
if ( GetStreamSocket() ) // Mal wieder nach oben verschoben, da sonst nicht vom Read runtergesprungen wird.
GetStreamSocket()->close();
resume(); // So daß das run auch die Schleife verlassen kann
join();
vos::OStreamSocket *pTempSocket = GetStreamSocket();
SetStreamSocket( NULL );
delete pTempSocket;
// ConnectionClosed(); Wird am Ende des Thread gerufen
}
else
{
join();
}
return sal_True;
}
sal_Bool CommunicationLinkViaSocket::StopCommunication()
{
if ( !bShutdownStarted )
{
return SimpleCommunicationLinkViaSocket::StopCommunication();
}
else
{
WaitForShutdown();
return sal_True;
}
}
IMPL_LINK( CommunicationLinkViaSocket, ShutdownLink, void*, EMPTYARG )
{
if ( !IsCommunicationError() )
ShutdownCommunication();
return 0;
}
void CommunicationLinkViaSocket::WaitForShutdown()
{
if ( !bShutdownStarted )
{
aShutdownTimer.SetTimeout( 30000 ); // Should be 30 Seconds
aShutdownTimer.SetTimeoutHdl( LINK( this, CommunicationLinkViaSocket, ShutdownLink ) );
aShutdownTimer.Start();
bShutdownStarted = sal_True;
}
if ( bDestroying )
{
while ( pMyManager && aShutdownTimer.IsActive() )
{
if ( IsCommunicationError() )
return;
GetpApp()->Yield();
}
ShutdownCommunication();
}
}
sal_Bool CommunicationLinkViaSocket::IsCommunicationError()
{
return !isRunning() || SimpleCommunicationLinkViaSocket::IsCommunicationError();
}
void CommunicationLinkViaSocket::run()
{
sal_Bool bWasError = sal_False;
while ( schedule() && !bWasError && GetStreamSocket() )
{
bWasError |= !DoReceiveDataStream();
if( bWasError)
continue;
TimeValue sNochEins = {0, 1000000};
while ( schedule() && bIsInsideCallback ) // solange der letzte Callback nicht beendet ist
sleep( sNochEins );
SetNewPacketAsCurrent();
StartCallback();
{
vos::OGuard aGuard( aMDataReceived );
vos::OGuard aGuard2( *pMPostUserEvent );
mlPutDataReceived.Call(this);
}
}
TimeValue sNochEins = {0, 1000000};
while ( schedule() && bIsInsideCallback ) // solange der letzte Callback nicht beendet ist
sleep( sNochEins );
StartCallback();
{
vos::OGuard aGuard( aMConnectionClosed );
vos::OGuard aGuard2( *pMPostUserEvent );
nConnectionClosedEventId = GetpApp()->PostUserEvent( LINK( this, CommunicationLinkViaSocket, ConnectionClosed ) );
}
}
sal_Bool CommunicationLinkViaSocket::DoTransferDataStream( SvStream *pDataStream, CMProtocol nProtocol )
{
if ( !isRunning() )
return sal_False;
return SimpleCommunicationLinkViaSocket::DoTransferDataStream( pDataStream, nProtocol );
}
/// Dies ist ein virtueller Link!!!
long CommunicationLinkViaSocket::ConnectionClosed( void* EMPTYARG )
{
{
vos::OGuard aGuard( aMConnectionClosed );
nConnectionClosedEventId = 0; // Achtung!! alles andere muß oben gemacht werden.
}
ShutdownCommunication();
return CommunicationLink::ConnectionClosed( );
}
/// Dies ist ein virtueller Link!!!
long CommunicationLinkViaSocket::DataReceived( void* EMPTYARG )
{
{
vos::OGuard aGuard( aMDataReceived );
nDataReceivedEventId = 0; // Achtung!! alles andere muß oben gemacht werden.
}
return CommunicationLink::DataReceived( );
}
IMPL_LINK( CommunicationLinkViaSocket, PutDataReceivedHdl, CommunicationLinkViaSocket*, EMPTYARG )
{
nDataReceivedEventId = GetpApp()->PostUserEvent( LINK( this, CommunicationLink, DataReceived ) );
return 0;
}
MultiCommunicationManager::MultiCommunicationManager( sal_Bool bUseMultiChannel )
: CommunicationManager( bUseMultiChannel )
, bGracefullShutdown( sal_True )
{
ActiveLinks = new CommunicationLinkList;
InactiveLinks = new CommunicationLinkList;
}
MultiCommunicationManager::~MultiCommunicationManager()
{
StopCommunication();
if ( bGracefullShutdown ) // first try to collect all callbacks for closing channels
{
Timer aTimeout;
aTimeout.SetTimeout( 40000 );
aTimeout.Start();
sal_uInt16 nLinkCount = 0;
sal_uInt16 nNewLinkCount = 0;
while ( aTimeout.IsActive() )
{
GetpApp()->Yield();
nNewLinkCount = GetCommunicationLinkCount();
if ( nNewLinkCount == 0 )
aTimeout.Stop();
if ( nNewLinkCount != nLinkCount )
{
aTimeout.Start();
nLinkCount = nNewLinkCount;
}
}
}
// Alles weghauen, was nicht rechtzeitig auf die Bäume gekommen ist
// Was bei StopCommunication übrig geblieben ist, da es sich asynchron austragen wollte
sal_uInt16 i = ActiveLinks->Count();
while ( i-- )
{
CommunicationLinkRef rTempLink = ActiveLinks->GetObject( i );
ActiveLinks->Remove( i );
rTempLink->InvalidateManager();
rTempLink->ReleaseReference();
}
delete ActiveLinks;
/// Die Links zwischen ConnectionClosed und Destruktor.
/// Hier NICHT gerefcounted, da sie sich sonst im Kreis festhaten würden,
/// da die Links sich erst in ihrem Destruktor austragen
i = InactiveLinks->Count();
while ( i-- )
{
CommunicationLinkRef rTempLink = InactiveLinks->GetObject( i );
InactiveLinks->Remove( i );
rTempLink->InvalidateManager();
}
delete InactiveLinks;
}
sal_Bool MultiCommunicationManager::StopCommunication()
{
// Alle Verbindungen abbrechen
// ConnectionClosed entfernt die Links aus der Liste. Je nach Implementation syncron
// oder asyncron. Daher Von oben nach unten Abräumen, so daß sich nichts verschiebt.
sal_uInt16 i = ActiveLinks->Count();
int nFail = 0;
while ( i )
{
if ( !ActiveLinks->GetObject(i-1)->StopCommunication() )
nFail++; // Hochzählen, da Verbindung sich nicht (sofort) beenden lässt.
i--;
}
return nFail == 0;
}
sal_Bool MultiCommunicationManager::IsLinkValid( CommunicationLink* pCL )
{
if ( ActiveLinks->Seek_Entry( pCL ) )
return sal_True;
else
return sal_False;
}
sal_uInt16 MultiCommunicationManager::GetCommunicationLinkCount()
{
return ActiveLinks->Count();
}
CommunicationLinkRef MultiCommunicationManager::GetCommunicationLink( sal_uInt16 nNr )
{
return ActiveLinks->GetObject( nNr );
}
void MultiCommunicationManager::CallConnectionOpened( CommunicationLink* pCL )
{
CommunicationLinkRef rHold(pCL); // Hält den Zeiger bis zum Ende des calls
ActiveLinks->C40_PTR_INSERT(CommunicationLink, pCL);
rHold->AddRef();
CommunicationManager::CallConnectionOpened( pCL );
}
void MultiCommunicationManager::CallConnectionClosed( CommunicationLink* pCL )
{
CommunicationLinkRef rHold(pCL); // Hält denm Zeiger bis zum Ende des calls
CommunicationManager::CallConnectionClosed( pCL );
sal_uInt16 nPos;
if ( ActiveLinks->Seek_Entry( pCL, &nPos ) )
{
InactiveLinks->C40_PTR_INSERT(CommunicationLink, pCL); // Ohne Reference
ActiveLinks->Remove( nPos );
}
pCL->ReleaseReference();
bIsCommunicationRunning = ActiveLinks->Count() > 0;
// delete pCL;
#if OSL_DEBUG_LEVEL > 1
rHold->bFlag = sal_True;
#endif
}
void MultiCommunicationManager::DestroyingLink( CommunicationLink *pCL )
{
sal_uInt16 nPos;
if ( InactiveLinks->Seek_Entry( pCL, &nPos ) )
InactiveLinks->Remove( nPos );
pCL->InvalidateManager();
}
CommunicationManagerClient::CommunicationManagerClient( sal_Bool bUseMultiChannel )
: MultiCommunicationManager( bUseMultiChannel )
{
ByteString aApplication("Something inside ");
aApplication.Append( ByteString( DirEntry( Application::GetAppFileName() ).GetName(), gsl_getSystemTextEncoding() ) );
SetApplication( aApplication );
}
CommunicationManagerServerViaSocket::CommunicationManagerServerViaSocket( sal_uLong nPort, sal_uInt16 nMaxCon, sal_Bool bUseMultiChannel )
: CommunicationManagerServer( bUseMultiChannel )
, nPortToListen( nPort )
, nMaxConnections( nMaxCon )
, pAcceptThread( NULL )
{
}
CommunicationManagerServerViaSocket::~CommunicationManagerServerViaSocket()
{
StopCommunication();
}
sal_Bool CommunicationManagerServerViaSocket::StartCommunication()
{
if ( !pAcceptThread )
pAcceptThread = new CommunicationManagerServerAcceptThread( this, nPortToListen, nMaxConnections );
return sal_True;
}
sal_Bool CommunicationManagerServerViaSocket::StopCommunication()
{
// Erst den Acceptor anhalten
delete pAcceptThread;
pAcceptThread = NULL;
// Dann alle Verbindungen kappen
return CommunicationManagerServer::StopCommunication();
}
void CommunicationManagerServerViaSocket::AddConnection( CommunicationLink *pNewConnection )
{
CallConnectionOpened( pNewConnection );
}
CommunicationManagerServerAcceptThread::CommunicationManagerServerAcceptThread( CommunicationManagerServerViaSocket* pServer, sal_uLong nPort, sal_uInt16 nMaxCon )
: pMyServer( pServer )
, pAcceptorSocket( NULL )
, nPortToListen( nPort )
, nMaxConnections( nMaxCon )
, nAddConnectionEventId( 0 )
, xmNewConnection( NULL )
{
if ( !pMPostUserEvent )
pMPostUserEvent = new vos::OMutex;
create();
}
CommunicationManagerServerAcceptThread::~CommunicationManagerServerAcceptThread()
{
#ifndef aUNX // Weil das Accept nicht abgebrochen werden kann, so terminiert wenigstens das Prog
// #62855# pl: gilt auch bei anderen Unixen
// die richtige Loesung waere natuerlich, etwas auf die pipe zu schreiben,
// was der thread als Abbruchbedingung erkennt
// oder wenigstens ein kill anstatt join
terminate();
if ( pAcceptorSocket )
pAcceptorSocket->close(); // Dann das Accept unterbrechen
join(); // Warten bis fertig
if ( pAcceptorSocket )
{
delete pAcceptorSocket;
pAcceptorSocket = NULL;
}
#else
DEBUGPRINTF ("Destructor CommunicationManagerServerAcceptThread Übersprungen!!!! (wegen Solaris BUG)\n");
#endif
{
vos::OGuard aGuard( aMAddConnection );
if ( nAddConnectionEventId )
{
GetpApp()->RemoveUserEvent( nAddConnectionEventId );
nAddConnectionEventId = 0;
CommunicationLinkRef xNewConnection = GetNewConnection();
INFO_MSG( CByteString("Event gelöscht"),
CByteString( "AddConnectionEvent aus Queue gelöscht"),
CM_MISC, xNewConnection );
xNewConnection->InvalidateManager();
xNewConnection.Clear(); // sollte das Objekt hier löschen
}
}
}
void CommunicationManagerServerAcceptThread::run()
{
if ( !nPortToListen )
return;
pAcceptorSocket = new vos::OAcceptorSocket();
vos::OInetSocketAddr Addr;
Addr.setPort( nPortToListen );
pAcceptorSocket->setReuseAddr( 1 );
if ( !pAcceptorSocket->bind( Addr ) )
{
return;
}
if ( !pAcceptorSocket->listen( nMaxConnections ) )
{
return;
}
vos::OStreamSocket *pStreamSocket = NULL;
while ( schedule() )
{
pStreamSocket = new vos::OStreamSocket;
switch ( pAcceptorSocket->acceptConnection( *pStreamSocket ) )
{
case vos::ISocketTypes::TResult_Ok:
{
pStreamSocket->setTcpNoDelay( 1 );
TimeValue sNochEins = {0, 100};
while ( schedule() && xmNewConnection.Is() ) // Solange die letzte Connection nicht abgeholt wurde warten wir
sleep( sNochEins );
xmNewConnection = new CommunicationLinkViaSocket( pMyServer, pStreamSocket );
xmNewConnection->StartCallback();
{
vos::OGuard aGuard( aMAddConnection );
vos::OGuard aGuard2( *pMPostUserEvent );
nAddConnectionEventId = GetpApp()->PostUserEvent( LINK( this, CommunicationManagerServerAcceptThread, AddConnection ) );
}
}
break;
case vos::ISocketTypes::TResult_TimedOut:
delete pStreamSocket;
pStreamSocket = NULL;
break;
case vos::ISocketTypes::TResult_Error:
delete pStreamSocket;
pStreamSocket = NULL;
break;
case vos::ISocketTypes::TResult_Interrupted:
case vos::ISocketTypes::TResult_InProgress:
break; // -Wall not handled...
}
}
}
IMPL_LINK( CommunicationManagerServerAcceptThread, AddConnection, void*, EMPTYARG )
{
{
vos::OGuard aGuard( aMAddConnection );
nAddConnectionEventId = 0;
}
pMyServer->AddConnection( xmNewConnection );
xmNewConnection.Clear();
return 1;
}
#define GETSET(aVar, KeyName, Dafault) \
aVar = aConf.ReadKey(KeyName,"No Entry"); \
if ( aVar == "No Entry" ) \
{ \
aVar = Dafault; \
aConf.WriteKey(KeyName, aVar); \
}
CommunicationManagerClientViaSocket::CommunicationManagerClientViaSocket( ByteString aHost, sal_uLong nPort, sal_Bool bUseMultiChannel )
: CommunicationManagerClient( bUseMultiChannel )
, aHostToTalk( aHost )
, nPortToTalk( nPort )
{
}
CommunicationManagerClientViaSocket::CommunicationManagerClientViaSocket( sal_Bool bUseMultiChannel )
: CommunicationManagerClient( bUseMultiChannel )
, aHostToTalk( "" )
, nPortToTalk( 0 )
{
}
CommunicationManagerClientViaSocket::~CommunicationManagerClientViaSocket()
{
}