Multiple simultaneous request support to Communication
authorOssi Jormakka <ossi.jormakka@ixonos.com>
Wed, 20 May 2009 07:58:52 +0000 (10:58 +0300)
committerOssi Jormakka <ossi.jormakka@ixonos.com>
Wed, 20 May 2009 07:58:52 +0000 (10:58 +0300)
QtMeetings.pro
resources/xml/errortable.xml
src/IO/Communication/Communication.cpp
src/IO/Communication/Communication.h
src/IO/Communication/CommunicationManager.cpp
src/IO/Communication/CommunicationManager.h
src/main.cpp
tests/IO/Communication/TestCommunication.cpp
tests/IO/Communication/TestCommunication.h

index 38adacb..75a7e9a 100644 (file)
@@ -86,7 +86,7 @@ RESOURCES += resources/BusinessLogic.qrc \
 CONFIG += link_pkgconfig
 PKGCONFIG += libalarm
 
-DEFINES += QT_NO_DEBUG_OUTPUT
+//DEFINES += DEBUG_OUTPUT_TO_FILE
 
 executable.files = qtmeetings
 executable.path = /usr/bin/
index d611fda..4afbc58 100644 (file)
@@ -9,10 +9,11 @@
     <error code="104">Communication: Server closed the connection unexpectedly</error>
     <error code="105">Communication: Server sent an invalid HTTP header</error>
     <error code="106">Communication: Server sent an invalid content length</error>
-    <error code="107">Communication: Invalid username or password</error>
+    <error code="107">Communication: The request was aborted</error>
     <error code="108">Communication: SOAP error when fetching meetings</error>
     <error code="109">Communication: SOAP error when fetching meeting details</error>
     <error code="110">Communication error</error>
+    <error code="111">Communication: Invalid username or password</error>
     <error code="200">Failed to change the operation mode. Error creating internal file storage.</error>
     <error code="201">Failed to change the operation mode. Error removing old alarm events.</error>
        <error code="202">Failed to change the operation mode. Error sending alarm events. %1</error>
index 82b847e..5c3b924 100644 (file)
@@ -9,7 +9,6 @@ Communication::Communication( const ConnectionSettings &aConnection ) :
        iConnectionSettings = new ConnectionSettings( aConnection );
 
        iHttp = new QHttp( iConnectionSettings->serverUrl().toString(), QHttp::ConnectionModeHttps );
-       iResponse = new QByteArray();
 
        connect( iHttp,
                         SIGNAL( readyRead( const QHttpResponseHeader& ) ),
@@ -46,32 +45,42 @@ Communication::~Communication()
 {
        delete iConnectionSettings;
        delete iHttp;
-       delete iResponse;
+       QList<QByteArray*> responses = iResponses.values();
+       while(!responses.isEmpty())
+               delete responses.takeFirst();
 }
 
 void Communication::processResponse( const QHttpResponseHeader& aHeader )
 {
        if ( aHeader.statusCode() == 200 )
        {
-               iResponse->append( iHttp->readAll() );
+               if( iResponses.contains( iCurrentRequest ) )
+               {
+                       QByteArray* response = iResponses.value( iCurrentRequest, NULL );
+                       response->append( iHttp->readAll() );
+               }
        }
 }
 
 void Communication::handleRequestStarted( int aRequestId )
 {
-       if( aRequestId != 0 && iCurrentRequest != 0 &&
-           aRequestId == iCurrentRequest )
+       if( iResponses.contains( aRequestId ) )
        {
-               emit requestStarted( iCurrentRequest ); 
+               iCurrentRequest = aRequestId;
+               emit requestStarted( aRequestId );      
        }
 }
 
 void Communication::handleResults( int aId, bool /*aError*/ )
 {
-       if( aId == iCurrentRequest )
+       if( iCurrentRequest != 0 && aId == iCurrentRequest )
        {
+               iAuthFailCount = 0;
                iCurrentRequest = 0;
-               emit requestFinished( aId, iHttp->error() );
+               int err = (int)iHttp->error();
+               if( iHttp->error() == QHttp::Aborted && iAuthFailCount > 1 )
+                       err = 11;
+               emit requestFinished( aId, err );
        }
 }
 
@@ -88,24 +97,34 @@ void Communication::handleAuthentication( const QString& /*aHost*/, quint16 /*aP
 
 int Communication::request( const QString &aCommand, const QByteArray &aContent )
 {
-       //TODO: This is just temporarily here to implement multiple request support     
-       if( iCurrentRequest )
+       if( iAuthFailCount > 1 )
                return 0;
+       
        QHttpRequestHeader header( QString( "POST" ), QString( "/ews/exchange.asmx" ) );
        header.setValue( "Host", iConnectionSettings->serverUrl().toString() );
        QString command = aCommand;
        header.setContentType( command );
 
-       iAuthFailCount = 0;
-       iResponse->clear();
-       iCurrentRequest = iHttp->request( header, aContent );
+       int id = iHttp->request( header, aContent );
+       if( iResponses.contains( id ) )
+       {
+               QByteArray* response = iResponses.value( id, NULL );
+               response->clear();
+       }
+       else
+       {
+               iResponses.insert( id, new QByteArray() );
+       }
        
-       return iCurrentRequest;
+       return id;
 }
 
-const QByteArray& Communication::response( int /*aRequestId*/ )
+QByteArray* Communication::response( int aRequestId )
 {
-       return *iResponse;
+       if( !iResponses.contains( aRequestId ) )
+               return NULL;
+       QByteArray* response = iResponses.take( aRequestId );
+       return response;
 }
 
 void Communication::handleReadProgress( int aDone, int aTotal )
index 9fd8523..41dedb5 100644 (file)
@@ -3,6 +3,7 @@
 
 #include <QObject>
 #include <QHttp>
+#include <QHash>
 
 class QByteArray;
 class ConnectionSettings;
@@ -11,8 +12,6 @@ class ConnectionSettings;
 /*!
  *  This class uses the QHttp class to make HTTP requests. HTTP responses are
  *  returned as QByteArray.
- * 
- *  NOTE! Currently this class does NOT support multiple simultaneuos requests.
  */
 class Communication : public QObject
 {
@@ -29,11 +28,9 @@ public:
        virtual ~Communication();
        //! Returns the response of a request identified by aRequestId.
        /*! 
-        *  NOTE! Currently the last request response is returned as the multiple
-        *  request support is not implemented.
         *  \param aRequestId Request id number.
        */
-       const QByteArray& response( int aRequestId );
+       QByteArray* response( int aRequestId );
 
        //! Makes a HTTP request to the server defined in the constructor.
        /*! 
@@ -61,7 +58,7 @@ signals:
        *   \param aRequestId Request id number.
        *   \param aError Error code of the request finished.
        */
-       void requestFinished( int aRequestId, QHttp::Error aError );
+       void requestFinished( int aRequestId, int aError );
 
 protected slots:
        //! Connected to QHttp::readyRead to read the response buffer.
@@ -85,9 +82,9 @@ private:
        */
        QHttp* iHttp;
        /*!
-        * Response buffer
+        * QHash of requests and response buffers
        */
-       QByteArray* iResponse;
+       QHash<int, QByteArray*> iResponses;
        /*!
         * ID number of a request currently ongoing
        */
index 4212f68..0a2ce77 100644 (file)
@@ -23,9 +23,9 @@ CommunicationManager::CommunicationManager( const ConnectionSettings &aConnectio
                                 SLOT( readProgress( int, int, int ) )
                                );
                connect( iFetchingCommunication,
-                                SIGNAL( requestFinished( int, QHttp::Error ) ),
+                                SIGNAL( requestFinished( int, int ) ),
                                 this,
-                                SLOT( requestFinished( int, QHttp::Error ) )
+                                SLOT( requestFinished( int, int ) )
                                );
        }
 }
@@ -41,6 +41,10 @@ CommunicationManager::~CommunicationManager()
 
 void CommunicationManager::fetchMeetings( const QDateTime &aFrom, const QDateTime &aUntil, const Room &aIn )
 {
+       //prevent making multiple simultaneous user availibility requests
+       const RequestData* rd = findRequest( GetUserAvailability );
+       if( rd )
+               return;
        ReqMsgGetUserAvailability msg;
        msg.setTimeZone();
        msg.setTimeWindow( aFrom, aUntil );
@@ -102,28 +106,27 @@ void CommunicationManager::getMeetingDetails( Meeting &aMeeting )
        }
 }
 
-void CommunicationManager::requestFinished( int aRequestId, QHttp::Error aError )
+void CommunicationManager::requestFinished( int aRequestId, int aError )
 {
        RequestData* rd = takeRequest( aRequestId );
-       qDebug() << "CommunicationManager::requestFinished: id: " << aRequestId << " error: " << (int)aError;
+       QByteArray* response = iFetchingCommunication->response( aRequestId );
+       qDebug() << "CommunicationManager::requestFinished: id: " << aRequestId << " error: " << aError;
 
-       if( aError != QHttp::NoError || rd == NULL )
+       if( aError != (int)(QHttp::NoError) || rd == NULL || response == NULL )
        {
-               int err = (int)aError;
-               if( rd == NULL )
+               int err = aError;
+               if( rd == NULL || response == NULL )
                        err = 10;
                delete rd;
                emit error( ERROR_BASE+(int)err, CommunicationManager::FetchingCommunication );
                return;
        }
 
-       const QByteArray& response = iFetchingCommunication->response( aRequestId );
-
        switch( rd->type )
        {
        case GetUserAvailability:
                {
-               ResMsgGetUserAvailability msg( response );
+               ResMsgGetUserAvailability msg( *response );
        
                while ( !iMeetings.isEmpty() )
                        delete iMeetings.takeFirst();
@@ -137,7 +140,7 @@ void CommunicationManager::requestFinished( int aRequestId, QHttp::Error aError
                }
        case ConvertId:
                {
-               ResponseMessage msg( response );
+               ResponseMessage msg( *response );
                QString id = msg.getNodeValue( QString( "Id" ),
                                                                           QDomNode::AttributeNode,
                                                                           QString( "AlternateId" ) );
@@ -148,7 +151,7 @@ void CommunicationManager::requestFinished( int aRequestId, QHttp::Error aError
                }
        case GetCalendarItem:
                {
-               ResMsgGetCalendarItem msg( response );
+               ResMsgGetCalendarItem msg( *response );
                int err = msg.getMeetingDetailsFromResponse( *(rd->meeting) );
                if( err )
                        emit error( ERROR_BASE+9, CommunicationManager::FetchingCommunication );
@@ -157,6 +160,7 @@ void CommunicationManager::requestFinished( int aRequestId, QHttp::Error aError
                break;
                }
        }
+       delete response;
        delete rd;
 }
 
@@ -188,5 +192,15 @@ CommunicationManager::RequestData* CommunicationManager::takeRequest( int aReque
        return NULL;
 }
 
-
-
+const CommunicationManager::RequestData* CommunicationManager::findRequest( RequestType aRequestType ) const
+{
+       for( int i = iRequestInfos.count() - 1; i >= 0 ; i-- )
+       {
+               struct RequestData* rd = iRequestInfos[i];
+               if( rd->type == aRequestType )
+               {
+                       return rd;
+               }
+       }
+       return NULL;
+}
index d1ded78..a8d8185 100644 (file)
@@ -85,7 +85,7 @@ signals:
 
 protected slots:
        //! Connected to Communication::requestFinished.
-       void requestFinished( int aRequestId, QHttp::Error aError );
+       void requestFinished( int aRequestId, int aError );
        //! Connected to Communication::readProgress.
        void readProgress( int aRequestId, int aDone, int aTotal );
        
@@ -146,6 +146,12 @@ protected:
         *  \param aRequestId Request ID number returned by the Communication::request
        */
        RequestData* takeRequest( int aRequestId );
+       //! Finds a RequestData from the iRequestInfos list.
+       /*! 
+        *  Returns a pointer to RequestData, NULL if not found.
+        *  \param aRequestType Request type
+       */
+       const RequestData* findRequest( RequestType aRequestType ) const;
        
 private:
        //! Instance of Connection settings of the fetching communication
index 9ed1c36..a7dc0e2 100644 (file)
@@ -1,41 +1,41 @@
-#include <iostream>\r
-#include <fstream> \r
-#include <QApplication>\r
-#include <QTime>\r
-#include "WindowManager.h"\r
-\r
-using namespace std;\r
-\r
-ofstream logfile;\r
-\r
-void DebugOutputHandler( QtMsgType type, const char *msg ) {\r
-    switch( type ) {\r
-        case QtDebugMsg:\r
-            logfile << QTime::currentTime().toString().toAscii().data() << " Debug: " << msg << "\n";\r
-            logfile.flush();\r
-            break;\r
-        case QtCriticalMsg:\r
-            logfile << QTime::currentTime().toString().toAscii().data() << " Critical: " << msg << "\n";\r
-            logfile.flush();\r
-            break;\r
-        case QtWarningMsg:\r
-            logfile << QTime::currentTime().toString().toAscii().data() << " Warning: " << msg << "\n";\r
-            logfile.flush();\r
-            break;\r
-        case QtFatalMsg:\r
-            logfile << QTime::currentTime().toString().toAscii().data() <<  " Fatal: " << msg << "\n";\r
-            logfile.flush();\r
-    }\r
-}\r
-\r
-int main( int argc, char *argv[] )\r
-{\r
-       #ifndef QT_NO_DEBUG_OUTPUT\r
-       logfile.open( "/tmp/qtmeetings.log", ios::app );\r
-       qInstallMsgHandler( DebugOutputHandler );\r
-       #endif\r
-       \r
-       QApplication app( argc, argv );\r
-       WindowManager *windowManager = new WindowManager;\r
-       return app.exec();\r
-}\r
+#include <iostream>
+#include <fstream> 
+#include <QApplication>
+#include <QTime>
+#include "WindowManager.h"
+
+using namespace std;
+
+ofstream logfile;
+
+void DebugOutputHandler( QtMsgType type, const char *msg ) {
+    switch( type ) {
+        case QtDebugMsg:
+            logfile << QTime::currentTime().toString().toAscii().data() << " Debug: " << msg << "\n";
+            logfile.flush();
+            break;
+        case QtCriticalMsg:
+            logfile << QTime::currentTime().toString().toAscii().data() << " Critical: " << msg << "\n";
+            logfile.flush();
+            break;
+        case QtWarningMsg:
+            logfile << QTime::currentTime().toString().toAscii().data() << " Warning: " << msg << "\n";
+            logfile.flush();
+            break;
+        case QtFatalMsg:
+            logfile << QTime::currentTime().toString().toAscii().data() <<  " Fatal: " << msg << "\n";
+            logfile.flush();
+    }
+}
+
+int main( int argc, char *argv[] )
+{
+       #ifdef DEBUG_OUTPUT_TO_FILE
+       logfile.open( "/tmp/qtmeetings.log", ios::app );
+       qInstallMsgHandler( DebugOutputHandler );
+       #endif
+       
+       QApplication app( argc, argv );
+       WindowManager *windowManager = new WindowManager;
+       return app.exec();
+}
index df94925..65fc2a7 100644 (file)
@@ -21,9 +21,9 @@ void TestCommunication::initTestCase()
     delete conn;
     conn = NULL;
        connect( iComm,
-                        SIGNAL( requestFinished( int, QHttp::Error ) ),
+                        SIGNAL( requestFinished( int, int ) ),
                         this,
-                        SLOT( requestFinished( int, QHttp::Error ) )
+                        SLOT( requestFinished( int, int ) )
                        );
 }
 
@@ -42,8 +42,7 @@ void TestCommunication::testPublicSignals()
        QCOMPARE( rp.isValid(), true );
        QSignalSpy rs( iComm, SIGNAL(requestStarted( int )) );
        QCOMPARE( rs.isValid(), true );
-       qRegisterMetaType<QHttp::Error>("QHttp::Error");
-       QSignalSpy rf( iComm, SIGNAL(requestFinished( int, QHttp::Error )) );
+       QSignalSpy rf( iComm, SIGNAL(requestFinished( int, int )) );
        QCOMPARE( rf.isValid(), true );
        
        iRequestId = iComm->request( "", QString("").toAscii() );
@@ -59,7 +58,7 @@ void TestCommunication::testPublicSignals()
        QCOMPARE( rf.count(), 1 );
        args = rf.takeFirst();
        QCOMPARE( args.at(0).type(), QVariant::Int );
-       QCOMPARE( args.at(1).typeName(), "QHttp::Error" );
+       QCOMPARE( args.at(1).type(), QVariant::Int );
        
        //verify readProgress
        QCOMPARE( rp.count(), 1 );
@@ -79,22 +78,36 @@ void TestCommunication::testRequestAndResponse()
     }
     QByteArray content = file.readAll();
 
-       iRequestId = iComm->request( command, content );
+       iError = 0;
+       int id_ok = iComm->request( command, content );
+       iRequestId = id_ok; 
        int id_fail = iComm->request( "", QString("").toAscii() );
        QVERIFY( iRequestId != 0 );
-       QCOMPARE( id_fail, 0 );
+       QVERIFY( id_fail != 0 );
+       qDebug() << "id_ok:" << id_ok;
+       qDebug() << "id_fail: " << id_fail;
        for(int i=0; iRequestId != 0 && i < 5000/250; i++ )
                QTest::qWait( 250 );
-       const QByteArray& response = iComm->response( 0 );
-       QVERIFY( response.contains("GetUserAvailabilityResponse") );
-       iRequestId = iComm->request( "", QString("").toAscii() );
-       QVERIFY( iRequestId != 0 );
-       QVERIFY( response.isEmpty() );
+       qDebug() << "iError:" << iError;
+       QVERIFY( iError == 0 );
+       QByteArray* response = iComm->response( id_ok );
+       QVERIFY( response != NULL );
+       QVERIFY( !response->isEmpty() );
+       if( response->contains("GetUserAvailabilityResponse") == FALSE )
+       {
+               QWARN( *response );
+       }
+       QVERIFY( response->contains("GetUserAvailabilityResponse") );
+       delete response;
+       response = iComm->response( 0 );
+       QVERIFY( response == NULL );
 }
 
-void TestCommunication::requestFinished( int aRequestId, QHttp::Error /*aError*/ )
+void TestCommunication::requestFinished( int aRequestId, int aError )
 {
-       QCOMPARE( aRequestId, iRequestId );
+       qDebug() << "TestCommunication::requestFinished:" << aRequestId << aError;
+       if( aRequestId == iRequestId )
+               iError = aError;
        iRequestId = 0;
 }
 
index 1fd9e4d..835234d 100644 (file)
@@ -13,9 +13,9 @@ private slots:
        void cleanupTestCase();
        void testPublicSignals();
     void testRequestAndResponse();
-       void requestFinished( int aRequestId, QHttp::Error aError );
+       void requestFinished( int aRequestId, int aError );
 
 private:
        Communication* iComm;
-       int iRequestId;
+       int iRequestId, iError;
 };