Exceptions with code and message for QxService

You find a bug using QxOrm library and you know how to fix it : submit a patch on this forum

Exceptions with code and message for QxService

Postby nickla » Tue Aug 14, 2012 1:51 pm

I noticed that there is no ability to send error code and error message from service server code to client. Yes, there is ability to use service method setMessageReturn. But what if i want to send message_return not from service class? I decided to do it using boost::exception. This is my patch:

Code: Select all
Index: include/QxOrm.h
===================================================================
--- include/QxOrm.h   (revision 282)
+++ include/QxOrm.h   (revision 283)
@@ -49,6 +49,8 @@
 
 #include <QxPrecompiled.h>
 
+#include <QxException.h>
+
 #include <QxCommon/QxConfig.h>
 #include <QxCommon/QxMacro.h>
 #include <QxCommon/QxHashValue.h>
Index: include/QxException.h
===================================================================
--- include/QxException.h   (revision 0)
+++ include/QxException.h   (revision 283)
@@ -0,0 +1,11 @@
+#ifndef QXEXCEPTION_H
+#define QXEXCEPTION_H
+
+#include <boost/exception/all.hpp>
+#include <iostream>
+
+typedef boost::error_info<struct qx_exception_tag_code,int> QxExceptionCode;
+typedef boost::error_info<struct qx_exception_tag_message,std::string> QxExceptionMessage;
+struct QxException: virtual boost::exception, virtual std::exception { };
+
+#endif // QXEXCEPTION_H

Property changes on: include\QxException.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: QxOrm.pro
===================================================================
--- QxOrm.pro   (revision 282)
+++ QxOrm.pro   (revision 283)
@@ -68,6 +68,8 @@
 
 HEADERS += ./include/QxPrecompiled.h
 
+HEADERS += ./include/QxException.h
+
 HEADERS += ./include/QxMemLeak/bool_array.h
 HEADERS += ./include/QxMemLeak/class_level_lock.h
 HEADERS += ./include/QxMemLeak/cont_ptr_utils.h
Index: src/QxService/QxTransaction.cpp
===================================================================
--- src/QxService/QxTransaction.cpp   (revision 282)
+++ src/QxService/QxTransaction.cpp   (revision 283)
@@ -31,6 +31,8 @@
 
 #include <QtNetwork/qhostaddress.h>
 
+#include <QxException.h>
+
 #include <QxService/QxTransaction.h>
 #include <QxService/QxConnect.h>
 #include <QxService/QxTools.h>
@@ -65,6 +67,16 @@
       m_pOutputParameter = m_pServiceInstance->getOutputParameter_BaseClass();
       m_bMessageReturn = m_pServiceInstance->getMessageReturn();
    }
+   catch (QxException & x ) {
+      qx_bool returnValue;
+      if ( int const * code=boost::get_error_info<QxExceptionCode>(x) ) {
+         returnValue.setCode(*code);
+      }
+      if ( std::string const * message=boost::get_error_info<QxExceptionMessage>(x) ) {
+         returnValue.setDesc(QString::fromUtf8((*message).c_str()));
+      }
+      this->setMessageReturn(returnValue);
+   }
    catch (const std::exception & e) { QString msg(e.what()); if (msg.isEmpty()) { msg = "[QxOrm] unexpected error occured executing service method"; }; m_bMessageReturn = qx_bool(0, msg); }
    catch (...) { m_bMessageReturn = qx_bool(0, "[QxOrm] unknown error occured executing service method"); }
    m_pServiceInstance.reset();
Index: src/QxService/QxThread.cpp
===================================================================
--- src/QxService/QxThread.cpp   (revision 282)
+++ src/QxService/QxThread.cpp   (revision 283)
@@ -32,6 +32,8 @@
 #include <QxService/QxTools.h>
 #include <QxService/QxConnect.h>
 
+#include <QxException.h>
+
 #include <QxMemLeak/mem_leak.h>
 
 namespace qx {
@@ -90,6 +92,16 @@
 
    Q_EMIT transactionStarted(m_pTransaction);
    try { m_pTransaction->executeServer(); }
+   catch (QxException & x ) {
+      qx_bool returnValue;
+      if ( int const * code=boost::get_error_info<QxExceptionCode>(x) ) {
+          returnValue.setCode(*code);
+      }
+      if ( std::string const * message=boost::get_error_info<QxExceptionMessage>(x) ) {
+          returnValue.setDesc(QString::fromUtf8((*message).c_str()));
+      }
+      m_pTransaction->setMessageReturn(returnValue);
+   }
    catch (const std::exception & e) { m_pTransaction->setMessageReturn(qx_bool(0, e.what())); }
    catch (...) { m_pTransaction->setMessageReturn(qx_bool(0, "unknown error")); }
    if (! m_bIsRunning) { return; }


Using this patch i can send error code and error message like this:
Code: Select all
bool UserManagerDao::authenticate(const QString username, const QString password, const QString serviceName, const QString serviceMethodName)
{
    qx::QxSqlQuery query("WHERE user_system_manager.username = :username AND user_system_manager.passwd = MD5(:password)");
    query.bind(":username", username);
    query.bind(":password", password);

    qx::dao::fetch_by_query_with_all_relation(query, *UserManagerCurrent::getSingleton());

    if (UserManagerCurrent::getSingleton()->id() == 0) {
        throw QxException() << QxExceptionCode(1) << QxExceptionMessage("There is no such user with this password");
    }
    if (UserManagerCurrent::getSingleton()->status()->isEnabled() == false) {
        throw QxException() << QxExceptionCode(2) << QxExceptionMessage(UserManagerCurrent::getSingleton()->status()->caption().toStdString());
    }
    return true;
}


Return xml will look like this:
Code: Select all
<qx.service.QxTransaction class_id="0" tracking_level="1" version="0" object_id="_0">
    // ....
    <service_name>UserManagerService</service_name>
    <service_method>currentUser</service_method>
    <message_return class_id="3" tracking_level="0" version="0">
        <value>0</value>
        <code>2</code>
        <desc>This user is blocked</desc>
    </message_return>
    <input_parameter class_id="4" tracking_level="0" version="1">
        <px class_id="6" class_name="UserManagerServiceInput" tracking_level="1" version="0" object_id="_1">
            <AbstractServiceInput class_id="7" tracking_level="1" version="0" object_id="_2">
                <qx.service.IxParameter class_id="5" tracking_level="1" version="0" object_id="_3"></qx.service.IxParameter>
                <user>kna</user>
                <passwd>123</passwd>
            </AbstractServiceInput>
            <id>0</id>
            <UserManager class_id="8" tracking_level="0" version="1">
                <px class_id="-1"></px>
            </UserManager>
        </px>
    </input_parameter>
    <output_parameter>
        <px class_id="-1"></px>
    </output_parameter>
</qx.service.QxTransaction>


I can throw this QxException anywhere in project and client will see this message and error code.

What does value mean in message_return parameter?
nickla
 
Posts: 52
Joined: Wed Jul 11, 2012 4:19 pm
Location: Russia

Re: Exceptions with code and message for QxService

Postby nickla » Tue Aug 14, 2012 4:47 pm

I added some error constants to QxOrm. Here they are:

Code: Select all
Index: include/QxPrecompiled.h
===================================================================
--- include/QxPrecompiled.h   (revision 286)
+++ include/QxPrecompiled.h   (revision 288)
@@ -99,6 +99,8 @@
 #pragma warning(pop)
 #endif // _MSC_VER
 
+#include <QxConsts.h>
+
 #include <QxCommon/QxConfig.h>
 #include <QxCommon/QxMacro.h>
 
Index: include/QxConsts.h
===================================================================
--- include/QxConsts.h   (revision 0)
+++ include/QxConsts.h   (revision 288)
@@ -0,0 +1,18 @@
+#ifndef _QX_QXCONSTS_H
+#define _QX_QXCONSTS_H
+
+struct QxErrorCode {
+    enum {
+        ERROR_OK = 0,
+
+        ERROR_UNKNOWN,
+
+        SERVER_NOT_FOUND,
+        SERVER_READ_ERROR,
+        SERVER_WRITE_ERROR,
+
+        SERVICE_NOT_SPECIFIED
+    };
+};
+
+#endif // _QX_QXCONSTS_H

Property changes on: include\QxConsts.h
___________________________________________________________________
Added: svn:eol-style
## -0,0 +1 ##
+native
\ No newline at end of property
Index: QxOrm.pro
===================================================================
--- QxOrm.pro   (revision 286)
+++ QxOrm.pro   (revision 288)
@@ -68,6 +68,7 @@
 
 HEADERS += ./include/QxPrecompiled.h
 
+HEADERS += ./include/QxConsts.h
 HEADERS += ./include/QxException.h
 
 HEADERS += ./include/QxMemLeak/bool_array.h
Index: src/QxService/QxTransaction.cpp
===================================================================
--- src/QxService/QxTransaction.cpp   (revision 286)
+++ src/QxService/QxTransaction.cpp   (revision 288)
@@ -85,7 +85,7 @@
 void QxTransaction::executeClient(IxService * pService, const QString & sMethod)
 {
    if ((pService == NULL) || sMethod.isEmpty()) { qAssert(false); return; }
-   if (pService->getServiceName().isEmpty()) { pService->setMessageReturn(qx_bool(0, "[QxOrm] empty service name")); return; }
+   if (pService->getServiceName().isEmpty()) { pService->setMessageReturn(qx_bool(QxErrorCode::SERVICE_NOT_SPECIFIED, "[QxOrm] empty service name")); return; }
    pService->registerClass();
 
    QTcpSocket socket;
@@ -93,7 +93,7 @@
    long serverPort = QxConnect::getSingleton()->getPort();
    socket.connectToHost(serverName, serverPort);
    if (! socket.waitForConnected(QxConnect::getSingleton()->getMaxWait()))
-   { pService->setMessageReturn(qx_bool(0, "[QxOrm] unable to connect to server")); return; }
+   { pService->setMessageReturn(qx_bool(QxErrorCode::SERVER_NOT_FOUND, "[QxOrm] unable to connect to server")); return; }
 
    if (m_sTransactionId.isEmpty())
    { setTransactionId(QUuid::createUuid().toString()); }
@@ -108,9 +108,9 @@
    setInputParameter(pService->getInputParameter_BaseClass());
 
    qx_bool bWriteOk = writeSocket(socket);
-   if (! bWriteOk) { pService->setMessageReturn(qx_bool(0, QString("[QxOrm] unable to write request to socket : '") + bWriteOk.getDesc() + QString("'"))); return; }
+   if (! bWriteOk) { pService->setMessageReturn(qx_bool(QxErrorCode::SERVER_WRITE_ERROR, QString("[QxOrm] unable to write request to socket : '") + bWriteOk.getDesc() + QString("'"))); return; }
    qx_bool bReadOk = readSocket(socket);
-   if (! bReadOk) { pService->setMessageReturn(qx_bool(0, QString("[QxOrm] unable to read reply from socket : '") + bReadOk.getDesc() + QString("'"))); return; }
+   if (! bReadOk) { pService->setMessageReturn(qx_bool(QxErrorCode::SERVER_READ_ERROR, QString("[QxOrm] unable to read reply from socket : '") + bReadOk.getDesc() + QString("'"))); return; }
 
    pService->setOutputParameter(getOutputParameter());
    pService->setMessageReturn(getMessageReturn());
Index: src/QxService/QxThread.cpp
===================================================================
--- src/QxService/QxThread.cpp   (revision 286)
+++ src/QxService/QxThread.cpp   (revision 288)
@@ -102,8 +102,8 @@
       }
       m_pTransaction->setMessageReturn(returnValue);
    }
-   catch (const std::exception & e) { m_pTransaction->setMessageReturn(qx_bool(0, e.what())); }
-   catch (...) { m_pTransaction->setMessageReturn(qx_bool(0, "unknown error")); }
+   catch (const std::exception & e) { m_pTransaction->setMessageReturn(qx_bool(QxErrorCode::ERROR_UNKNOWN, e.what())); }
+   catch (...) { m_pTransaction->setMessageReturn(qx_bool(QxErrorCode::ERROR_UNKNOWN, "unknown error")); }
    if (! m_bIsRunning) { return; }
 
    qx_bool bWriteOk = writeSocket(socket);
@@ -124,7 +124,7 @@
 
 qx_bool QxThread::writeSocket(QTcpSocket & socket)
 {
-   if (! m_pTransaction) { return qx_bool(0, "empty service transaction"); }
+   if (! m_pTransaction) { return qx_bool(QxErrorCode::SERVICE_NOT_SPECIFIED, "empty service transaction"); }
 
    quint32 uiTransactionSize = 0;
    IxParameter_ptr pInputBackup = m_pTransaction->getInputParameter();
Index: src/QxService/QxTools.cpp
===================================================================
--- src/QxService/QxTools.cpp   (revision 286)
+++ src/QxService/QxTools.cpp   (revision 288)
@@ -43,7 +43,7 @@
 qx_bool QxTools::readSocket(QTcpSocket & socket, QxTransaction & transaction, quint32 & size)
 {
    while (socket.bytesAvailable() < (qint64)(QX_SERVICE_TOOLS_HEADER_SIZE))
-   { if (! socket.waitForReadyRead(QxConnect::getSingleton()->getMaxWait())) { return qx_bool(0, "invalid bytes count available to retrieve transaction header"); } }
+   { if (! socket.waitForReadyRead(QxConnect::getSingleton()->getMaxWait())) { return qx_bool(QxErrorCode::ERROR_UNKNOWN, "invalid bytes count available to retrieve transaction header"); } }
 
    quint32 uiSerializedSize = 0;
    quint16 uiSerializationType(0), uiCompressData(0), uiEncryptData(0);
@@ -57,7 +57,7 @@
    in >> uiEncryptData;
 
    while (socket.bytesAvailable() < (qint64)(uiSerializedSize))
-   { if (! socket.waitForReadyRead(QxConnect::getSingleton()->getMaxWait())) { return qx_bool(0, "invalid bytes count available to retrieve transaction serialized data"); } }
+   { if (! socket.waitForReadyRead(QxConnect::getSingleton()->getMaxWait())) { return qx_bool(QxErrorCode::ERROR_UNKNOWN, "invalid bytes count available to retrieve transaction serialized data"); } }
 
    QByteArray dataSerialized = socket.read((qint64)(uiSerializedSize));
    qAssert(dataSerialized.size() == (int)(uiSerializedSize));
@@ -67,7 +67,7 @@
    {
       QxSimpleCrypt crypto(QxConnect::getSingleton()->getEncryptKey());
       QByteArray decrypted = crypto.decryptToByteArray(dataSerialized);
-      if ((crypto.lastError() != QxSimpleCrypt::ErrorNoError) || decrypted.isEmpty()) { return qx_bool(0, "an error occured during decryption of data"); }
+      if ((crypto.lastError() != QxSimpleCrypt::ErrorNoError) || decrypted.isEmpty()) { return qx_bool(QxErrorCode::ERROR_UNKNOWN, "an error occured during decryption of data"); }
       dataSerialized = decrypted;
    }
 
@@ -103,7 +103,7 @@
       case QxConnect::serialization_polymorphic_xml:        bDeserializeOk = qx::serialization::polymorphic_xml::from_byte_array(transaction, dataSerialized); break;
       case QxConnect::serialization_polymorphic_text:       bDeserializeOk = qx::serialization::polymorphic_text::from_byte_array(transaction, dataSerialized); break;
 #endif // _QX_SERIALIZE_POLYMORPHIC
-      default:                                              return qx_bool(0, "unknown serialization type to read data from socket");
+      default:                                              return qx_bool(QxErrorCode::ERROR_UNKNOWN, "unknown serialization type to read data from socket");
    }
 
    return bDeserializeOk;
@@ -142,7 +142,7 @@
       case QxConnect::serialization_polymorphic_xml:        dataSerialized = qx::serialization::polymorphic_xml::to_byte_array(transaction, (& owner)); break;
       case QxConnect::serialization_polymorphic_text:       dataSerialized = qx::serialization::polymorphic_text::to_byte_array(transaction, (& owner)); break;
 #endif // _QX_SERIALIZE_POLYMORPHIC
-      default:                                              return qx_bool(0, "unknown serialization type to write data to socket");
+      default:                                              return qx_bool(QxErrorCode::ERROR_UNKNOWN, "unknown serialization type to write data to socket");
    }
 
    if (dataSerialized.isEmpty())
@@ -159,7 +159,7 @@
       crypto.setCompressionMode(QxSimpleCrypt::CompressionNever);
       crypto.setIntegrityProtectionMode(QxSimpleCrypt::ProtectionChecksum);
       QByteArray encrypted = crypto.encryptToByteArray(dataSerialized);
-      if ((crypto.lastError() != QxSimpleCrypt::ErrorNoError) || encrypted.isEmpty()) { return qx_bool(0, "an error occured during encryption of data"); }
+      if ((crypto.lastError() != QxSimpleCrypt::ErrorNoError) || encrypted.isEmpty()) { return qx_bool(QxErrorCode::ERROR_UNKNOWN, "an error occured during encryption of data"); }
       dataSerialized = encrypted;
       uiEncryptData = 1;
    }
@@ -184,7 +184,7 @@
    }
 
    if (iTotalWritten != iTotalToWrite)
-   { return qx_bool(0, "unable to write all data bytes (header) to socket"); }
+   { return qx_bool(QxErrorCode::ERROR_UNKNOWN, "unable to write all data bytes (header) to socket"); }
 
    iTotalWritten = 0;
    iTotalToWrite = (qint64)(dataSerialized.size());
@@ -197,7 +197,7 @@
    }
 
    size = (quint32)(dataHeader.size() + dataSerialized.size());
-   return ((iTotalWritten == iTotalToWrite) ? qx_bool(true) : qx_bool(0, "unable to write all data bytes (serialized data) to socket"));
+   return ((iTotalWritten == iTotalToWrite) ? qx_bool(true) : qx_bool(QxErrorCode::ERROR_UNKNOWN, "unable to write all data bytes (serialized data) to socket"));
 }
 
 } // namespace service


This helps me to do following in client:
Code: Select all
#ifndef _QX_SERVICE_CONSTS_H
#define _QX_SERVICE_CONSTS_H

struct QxServiceError {
    enum {
        // everything is ok
        ERROR_OK = 0,

        // authentication errors
        AUTH_ACCESS_DENY = 1000,
        AUTH_USER_BLOCKED
    };
};

#endif // _QX_SERVICE_CONSTS_H

//... somewhere in login dialog
void DialogLogin::onLoginClick()
{
    QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));

    UserManagerServiceInputPtr input = UserManagerServiceInputPtr(new UserManagerServiceInput());
    input->setUsername(ui->lineUsername->text());
    input->setPassword(ui->linePassword->text());

    UserManagerService service;
    service.setInputParameter(input);
    service.currentUser();

    if (service.getMessageReturn().getCode() != QxServiceError::ERROR_OK) {
        qCritical() << service.getMessageReturn().getDesc();
        QMessageBox::critical(this, QString::fromUtf8("Authorization error"), service.getMessageReturn().getDesc());
    }

    // If transaction is ok => display user fetched on GUI
    UserManagerPtr output = (service.isValidWithOutput() ? service.getOutputParameter()->user() : UserManagerPtr());

    QApplication::restoreOverrideCursor();
}
nickla
 
Posts: 52
Joined: Wed Jul 11, 2012 4:19 pm
Location: Russia

Re: Exceptions with code and message for QxService

Postby qxorm » Wed Aug 15, 2012 12:17 pm

I noticed that there is no ability to send error code and error message from service server code to client. Yes, there is ability to use service method setMessageReturn. But what if i want to send message_return not from service class? I decided to do it using boost::exception

Yes, you're right, with current version of QxOrm library, you have the setMessageReturn() method to inform the client if everything is ok or if there is an error (code + description).
And if you want to throw an exception on server side, you can only send a description of the error (and not a code) using exc.what() method of std::exception class.
So to send an error code :
1- you could manage your own try/catch() in your service method ;
2- or I could add your patch in the next release... :)
Ok it's a good idea, but why boost::exception ?
I think I will just create a new exception class, without using boost::exception...

What does value mean in message_return parameter ?

message_return parameter is a qx_bool variable, so you have 3 properties associated to qx_bool class :
* value : like bool type, 0 means false, and 1 means true ;
* code : an error code (generally, value = 0 if you have an error code) ;
* desc : an error description (generally, value = 0 if you have an error description).

I added some error constants to QxOrm...

Ok, instead of just return a description, I could also send an error code !
Thx for your patch ;)
I will add something like this for the next version...
qxorm
Site Admin
 
Posts: 481
Joined: Mon Apr 12, 2010 7:45 am

Re: Exceptions with code and message for QxService

Postby nickla » Wed Aug 15, 2012 12:42 pm

It will be great. Thanks.

I choose boost::exception b\c you use boost and boost has realization of exception with code and message already.
nickla
 
Posts: 52
Joined: Wed Jul 11, 2012 4:19 pm
Location: Russia

Re: Exceptions with code and message for QxService

Postby qxorm » Tue Dec 11, 2012 1:20 pm

Done, you can test it with the following BETA version :
http://www.qxorm.com/version/QxOrm_1.2.5_BETA_04.zip

I created a class exception named : qx::exception.
Now, you can throw an exception on server side with code + description.
qxorm
Site Admin
 
Posts: 481
Joined: Mon Apr 12, 2010 7:45 am


Return to QxOrm - Submit a patch

Who is online

Users browsing this forum: No registered users and 1 guest

cron