From 2fca0376ce9f97582dced3d820ebc176d05c012e Mon Sep 17 00:00:00 2001 From: konrad Date: Sun, 3 Oct 2010 14:07:37 +0000 Subject: [PATCH] make transaction async git-svn-id: https://silmor.de/svn/softmagic/pack/trunk@598 6e3c4bff-ac9f-4ac1-96c5-d2ea494d3e33 --- qtbase/include/transaction.h | 60 +++++++++++-- qtbase/include/transaction_p.h | 32 +++++++ qtbase/src/transaction.cpp | 126 +++++++++++++++++++++++++- woc/qt/qtctrans.cpp | 201 ++++++++++++++++++++++++++-------------- woc/qt/qtout.cpp | 16 +++ woc/qt/qtout.h | 15 +++- 6 files changed, 371 insertions(+), 79 deletions(-) diff --git a/qtbase/include/transaction.h b/qtbase/include/transaction.h index 103e277..25cc027 100644 --- a/qtbase/include/transaction.h +++ b/qtbase/include/transaction.h @@ -19,6 +19,7 @@ #include "nullable.h" #include "WHelper" +#include class WTransaction_Private; @@ -29,14 +30,20 @@ class WTransaction:public WHelper public: /**stage the transaction is in*/ enum Stage { - Uninitialized,/// > m_trn; + int m_tmout; + }; + + /**use this class to wait for any of a number of transactions */ + class WaitForAny:public WaitFor_Private{ + public: + WaitForAny(int tmout=0):WaitFor_Private(tmout){} + virtual ~WaitForAny(){loop();} + protected: + void loop(); + }; + /**use this class to wait for all of a number of transactions */ + class WaitForAll:public WaitFor_Private{ + public: + WaitForAll(int tmout=0):WaitFor_Private(tmout){} + virtual ~WaitForAll(){loop();} + protected: + void loop(); + }; protected: /**internal: construct the transaction*/ WTransaction(QString iface=QString()); @@ -65,14 +110,15 @@ class WTransaction:public WHelper /**internal: execute a query on the web asynchronously*/ virtual void startQuery(QString,QByteArray); + protected slots: /**internal: collect query data*/ - virtual void endQuery(); + virtual void endQuery()=0; signals: - /** this signal is raised when the transaction is finished, data is available*/ + /** this signal is raised when the transaction is finished successfully or with an error, data or an error information is available*/ void finished(); protected: - class Log; friend class WTransaction::Log; + friend class WTransaction::LogWrap; friend class WTransaction_Private; WTransaction_Private *d; }; diff --git a/qtbase/include/transaction_p.h b/qtbase/include/transaction_p.h index 6a7716b..cc58d2a 100644 --- a/qtbase/include/transaction_p.h +++ b/qtbase/include/transaction_p.h @@ -58,6 +58,7 @@ class WTransaction_Private:public QObject { Q_OBJECT public: WTransaction_Private(WTransaction*); + virtual ~WTransaction_Private(); /**internal: execute a query synchronously on the web, used by subclasses*/ virtual QByteArray executeQuery(QString,QByteArray); @@ -76,6 +77,8 @@ class WTransaction_Private:public QObject { signals: /** \internal this signal is raised when the transaction on the HTTP level finished*/ void webFinished(); + /** used by high level objects, emitted when data is available */ + void dataAvailable(); public: WTransaction::Stage m_stage; @@ -95,4 +98,33 @@ class WTransaction_Private:public QObject { int m_refctr; }; +/// \internal wrapper around the logger +class WTransaction::LogWrap{ + public: + /**instantiates a log wrapper using the logger of the parent or creates a new one in the parent*/ + LogWrap(WTransaction*parent,const QString&request,const QString&interface=QString()); + + ///logs/stores a request message + void setRequ(const QString&,const QString&); + ///logs/stores a response message + void setResp(const QString&,const QString&); + ///logs an error, if the interface is set up to log only on errors it also logs the stored request and response + void setError(const QString&); + ///logs some informational message + void setInfo(const QString&); + private: + WTransaction*parent; +}; + +class WTransaction_PrivateBase { + private: + int refctr; + protected: + WTransaction_PrivateBase(); + virtual ~WTransaction_PrivateBase(); + public: + virtual void attach(){refctr++;} + virtual void detach(){refctr--;if(refctr==0)delete this;} +}; + #endif diff --git a/qtbase/src/transaction.cpp b/qtbase/src/transaction.cpp index 56fc80a..0c9a10f 100644 --- a/qtbase/src/transaction.cpp +++ b/qtbase/src/transaction.cpp @@ -15,6 +15,7 @@ #include "WInterface" #include +#include #include #include #include @@ -137,7 +138,6 @@ void WTransaction_Private::startQuery(QString hreq,QByteArray data) } -void WTransaction::endQuery(){d->endQuery();} void WTransaction_Private::endQuery() { //sanity check @@ -181,8 +181,10 @@ void WTransaction_Private::endQuery() m_stage=WTransaction::Error; m_errtype="_internal"; m_errstr="Ooops: still in Request phase at end of response handling."; + return; } //remaining high level errors are handled by the generated code + emit dataAvailable(); } QByteArray WTransaction::executeQuery(QString hreq,QByteArray data){return d->executeQuery(hreq,data);} @@ -210,6 +212,21 @@ QByteArray WTransaction_Private::executeQuery(QString hreq,QByteArray data) return QByteArray(); } +bool WTransaction::waitForFinished(int tmout) +{ + //is it running? + if(d->m_stage==Success)return true; + if(d->m_stage!=Request)return false; + //wait for it + QEventLoop loop; + connect(this,SIGNAL(finished()),&loop,SLOT(quit())); + if(tmout>0) + QTimer::singleShot(tmout,&loop,SLOT(quit())); + loop.exec(); + //return + return d->m_stage==Success; +} + void WTransaction_Private::webTimeout() { if(m_stage!=WTransaction::Request)return; @@ -332,6 +349,35 @@ void WTransaction::Log::setError(const QString&s) outStr(QString("Transaction %1 Error: %2").arg(trn).arg(s)); } +WTransaction::LogWrap::LogWrap(WTransaction*p,const QString&r,const QString&i) +{ + Q_ASSERT_X(p!=0,"LogWrap constructor","expecting parent to be valid"); + parent=p; + if(p->d->m_log==0)new Log(p,r,i); +} + +void WTransaction::LogWrap::setRequ(const QString&s1,const QString&s2) +{ + if(parent->d->m_log) + parent->d->m_log->setRequ(s1,s2); +} +void WTransaction::LogWrap::setResp(const QString&s1,const QString&s2) +{ + if(parent->d->m_log) + parent->d->m_log->setResp(s1,s2); +} +void WTransaction::LogWrap::setError(const QString&s) +{ + if(parent->d->m_log) + parent->d->m_log->setError(s); +} +void WTransaction::LogWrap::setInfo(const QString&s) +{ + if(parent->d->m_log) + parent->d->m_log->setInfo(s); +} + + /*****************************************************************************/ WTransaction_Private::WTransaction_Private(WTransaction*trn) @@ -341,12 +387,19 @@ WTransaction_Private::WTransaction_Private(WTransaction*trn) m_refctr=0; attach(trn); connect(this,SIGNAL(webFinished()),this,SLOT(endQuery())); +// qDebug()<<"constructed a private object"<<(long long)this; +} + +WTransaction_Private::~WTransaction_Private() +{ +// qDebug()<<"destructing private object"<<(long long)this; } WTransaction_Private* WTransaction_Private::attach(WTransaction*trn) { m_refctr++; connect(trn,SIGNAL(destroyed(QObject*)),this,SLOT(detach(QObject*))); + connect(this,SIGNAL(dataAvailable()),trn,SLOT(endQuery())); return this; } @@ -360,3 +413,74 @@ void WTransaction_Private::detach(QObject*trn) trn->disconnect(this); } } + +WTransaction_PrivateBase::WTransaction_PrivateBase() +{ + refctr=1; +// qDebug()<<"created private base"<<(long long)this; +} + +WTransaction_PrivateBase::~WTransaction_PrivateBase() +{ +// qDebug()<<"destructed private base"<<(long long)this; +} + +/*****************************************************************************/ + +WTransaction::WaitFor_Private& WTransaction::WaitFor_Private::operator<<(WTransaction&t) +{ + m_trn<< &t; + return *this; +} +WTransaction::WaitFor_Private& WTransaction::WaitFor_Private::operator<<(WTransaction*t) +{ + m_trn<< t; + return *this; +} + +void WTransaction::WaitForAny::loop() +{ + QEventLoop loop; + //reduce list and connect to signals + for(int i=0;iisInProgress())continue; + connect(m_trn[i],SIGNAL(finished()),&loop,SLOT(quit())); + } + //set up timeout + if(m_tmout>0) + QTimer::singleShot(m_tmout,&loop,SLOT(quit())); + //start loop + loop.exec(); +} +void WTransaction::WaitForAll::loop() +{ + QDateTime start=QDateTime::currentDateTime(); + do{ + int rtm=0; + //check timeout + if(m_tmout>0){ + rtm=m_tmout-start.msecsTo(QDateTime::currentDateTime()); + if(rtm<=0) + return; + } + //reduce list + QList > tmp; + for(int i=0;iisInProgress())continue; + tmp<0) + QTimer::singleShot(rtm,&loop,SLOT(quit())); + //start loop + loop.exec(); + }while(true); +} diff --git a/woc/qt/qtctrans.cpp b/woc/qt/qtctrans.cpp index 515adae..6a7fefa 100644 --- a/woc/qt/qtctrans.cpp +++ b/woc/qt/qtctrans.cpp @@ -31,10 +31,14 @@ void WocQtClientTransaction::finalize() void WocQtClientTransaction::newTransaction(const WocTransaction&trn) { QString cn=m_prefix+"T"+trn.name(); + QString cnp=cn+"_Private"; addFile(cn); +// addFile(WocQtOut::Header,"prv"+cn+".h"); MFile hdr(m_basedir+"/"+m_subdir+"/src"+cn+".h"); +// MFile hdp(m_basedir+"/"+m_subdir+"/prv"+cn+".h"); MFile src(m_basedir+"/"+m_subdir+"/src"+cn+".cpp"); if(!hdr.open(QIODevice::WriteOnly|QIODevice::Truncate) || +// !hdp.open(QIODevice::WriteOnly|QIODevice::Truncate) || !src.open(QIODevice::WriteOnly|QIODevice::Truncate)){ qDebug("Error: cannot create class files for transaction %s.",cn.toAscii().data()); emit errorFound(); @@ -45,44 +49,59 @@ void WocQtClientTransaction::newTransaction(const WocTransaction&trn) QStringList out=trn.outputNames(); //lead in hdr.write(QByteArray(HDRSTART).replace("%",cn.toAscii())); +// hdp.write(QByteArray(HDRSTART).replace("%",cnp.toAscii())); src.write(QByteArray(SRCSTART).replace("%",cn.toAscii())); - QString hcd; - QString scd; - hcd="#include \""+m_transbase+"\"\n"; - hcd+="#include \"WTransaction_Private\"\n"; - scd+="#include \"WInterface\"\n#include \n"; + + //start constructing code + QString hcd;//header: class decl + QString hdi;//header includes + QString pcd;//private class decl + QString scd;//cpp source + QString sri;//cpp includes + hdi="#include \""+m_transbase+"\"\n"; +// pcd="#include \"src"+cn+".h\"\n"; + sri+="#include \"WTransaction_Private\"\n"; +// sri+="#include \"prv"+cn+".h\"\n"; + sri+="#include \"WInterface\"\n"; + sri+="#include \n\n"; for(int i=0;i\n"; + if(tp!="")hdi+="#include <"+tp+">\n"; } for(int i=0;i\n"; + if(tp!="")hdi+="#include <"+tp+">\n"; } + hcd+="\nclass "+cnp+";\n"; hcd+="\nclass "+cn+":public "+m_transbase+"\n{\n"; - hdr.write(hcd.toAscii()); + pcd+="\nclass "+cnp+":public WTransaction_PrivateBase\n{\n"; //create properties QString inlist,clist; - hcd=" private:\n"; + hcd+=" private:\n\t"+cnp+"*p;\n\tfriend class "+cnp+";\n"; + pcd+=" protected:\n\tfriend class "+cn+";\n"; + pcd+="\t"+cnp+"("+cn+"*parent){parent->p=this;}\n"; + pcd+="\tvoid attach("+cn+"*parent){parent->p=this;WTransaction_PrivateBase::attach();}\n"; + pcd+="\tvoid detach("+cn+"*parent){parent->p=0;WTransaction_PrivateBase::detach();}\n"; for(int i=0;iprojectName()+"\""; //define parametric constructor - hcd=" public:\n"; - hcd+="\t"+cn+"("+inlist+defparm+");\n"; + hcd+="\t"+cn+"("+inlist+");\n"; //parametric constructor implementation scd+=cn+"::"+cn+"("+inlist+")\n\t:"+m_transbase+"(iface)\n{\n"; + scd+="\tnew "+cnp+"(this);\n"; for(int i=0;idetach(this);\n}\n\n"; + //query method implemented scd+="void "+cn+"::netquery()\n{\n"; - scd+="\tLog log(this,\""+trn.name()+"\");\n"; - scd+="\tQDomDocument doc;QDomElement root=doc.createElement(\"WobRequest-"+trn.name()+"\");\n"; - scd+="\tQDomElement tmp;\n"; - scd+="\tWInterface *iface=WInterface::instance(d->m_iface);\n"; - scd+="\tif(iface==0){d->m_errtype=\"_iface\";d->m_errstr=\"interface not found\";d->m_stage=Error;log.setError(d->m_errstr);return;}\n"; + scd+="\tWTransaction::Log log(this,\""+trn.name()+"\");\n"; + scd+="\tQByteArray enc=encodeData();\n"; + scd+="\tif(enc.isEmpty()){\n\t\temit finished();\n\t\treturn;\n\t}\n"; + scd+="\texecuteQuery(\""+trn.name()+"\",enc);\n"; + scd+="}\n"; + //async query method implemented + scd+="void "+cn+"::asyncnetquery()\n{\n"; + scd+="\tnew WTransaction::Log(this,\""+trn.name()+"\");\n"; + scd+="\tQByteArray enc=encodeData();\n"; + scd+="\tif(enc.isEmpty()){\n\t\temit finished();\n\t\treturn;\n\t}\n"; + scd+="\tstartQuery(\""+trn.name()+"\",enc);\n"; + scd+="}\n"; //encode input + scd+="QByteArray "+cn+"::encodeData()\n{\n"; scd+=trnInput(trn); - scd+="\tdoc.appendChild(root);\n"; - //query - scd+="\tQByteArray rba=executeQuery(\""+trn.name()+"\",doc.toByteArray());\n"; + scd+="}\n"; //decode output + scd+="void "+cn+"::decodeData(QByteArray rba)\n{\n"; scd+=trnOutput(trn); scd+="}\n"; - hdr.write(hcd.toAscii()); - src.write(scd.toAscii()); + scd+="void "+cn+"::endQuery()\n{\n"; + scd+="\tdecodeData(d->m_rspdata.trimmed());\n"; + scd+="\temit finished();\n}\n"; //create getters - hcd=""; for(int i=0;iout_"+out[i]+";}\n"; } + //create queries - scd=""; hcd+="\tstatic "+cn+" query("+inlist+defparm+");\n"; scd+=cn+" "+cn+"::query("+inlist+")\n{\n"; scd+="\t"+cn; - if(clist!="")scd+=" r("+clist+");\n";else scd+=" r;"; + if(clist!="")scd+=" r("+clist+");\n";else scd+=" r;\n"; scd+="\tr.netquery();\n\treturn r;\n}\n"; + hcd+="\tstatic "+cn+" asyncQuery("+inlist+defparm+");\n"; + scd+=cn+" "+cn+"::asyncQuery("+inlist+")\n{\n"; + scd+="\t"+cn; + if(clist!="")scd+=" r("+clist+");\n";else scd+=" r;\n"; + scd+="\tr.asyncnetquery();\n\treturn r;\n}\n"; - //lead out + //button up + hcd+="};\n\n"; + pcd+="};\n\n"; + + //write code + hdr.write(hdi.toAscii()); hdr.write(hcd.toAscii()); +// hdp.write(pcd.toAscii()); + src.write(sri.toAscii()); + src.write(pcd.toAscii()); src.write(scd.toAscii()); - hdr.write(QByteArray("\n};\n")); + + //lead out hdr.write(QByteArray(HDREND).replace("%",cn.toAscii())); +// hdp.write(QByteArray(HDREND).replace("%",cnp.toAscii())); src.write(QByteArray(SRCEND).replace("%",cn.toAscii())); } QString WocQtClientTransaction::trnInput(const WocTransaction&trn) { - QString code="\t/*start of input encoding*/\n"; + QString code; + code+="\tWTransaction::LogWrap log(this,\""+trn.name()+"\");\n"; + code+="\tQDomDocument doc;QDomElement root=doc.createElement(\"WobRequest-"+trn.name()+"\");\n"; + code+="\tQDomElement tmp;\n"; + code+="\tWInterface *iface=WInterface::instance(d->m_iface);\n"; + code+="\tif(iface==0){\n"; + code+="\t\td->m_errtype=\"_iface\";d->m_errstr=\"interface not found\";\n"; + code+="\t\td->m_stage=Error;log.setError(d->m_errstr);\n\t\treturn QByteArray();\n\t}\n"; + code+="\t/*start of input encoding*/\n"; QStringList sl=trn.inputNames(); for(int i=0;iin_"+sl[i]; if(trn.isBoolType(t)) code+="?\"true\":\"false\""; code+=");\n"; }else{ if(trn.isListType(t)){ QString pt=trn.plainType(t); - code+="\tfor(int i=0;im_errstr=QString(QCoreApplication::translate(\"WobTransaction\",\"XML result parser error line %1 col %2: %3\")).arg(eln).arg(ecl).arg(emsg);\n\t\tlog.setError(d->m_errstr);\n\t\treturn;\n\t}\n"; - code+="\troot=doc.documentElement();\n"; + code+="\t\td->m_stage=Error;d->m_errtype=\"_iface\";\n"; + code+="\t\td->m_errstr=QString(QCoreApplication::translate(\"WobTransaction\",\"XML result parser error line %1 col %2: %3\")).arg(eln).arg(ecl).arg(emsg);\n"; + code+="\t\tlog.setError(d->m_errstr);\n\t\treturn;\n\t}\n"; + code+="\tQDomElement root=doc.documentElement();\n"; //decide where to go, error handling - code+="\tif(d->m_wobstatus!=\"ok\"){\n\t\td->m_stage=Error;d->m_errtype=\"_server\";d->m_errstr=\"unknown server error\";\n"; + code+="\tif(d->m_wobstatus!=\"ok\"){\n"; + code+="\t\td->m_stage=Error;d->m_errtype=\"_server\";d->m_errstr=\"unknown server error\";\n"; code+="\t\tQString terr=root.text().trimmed();\n"; code+="\t\tif(terr==\"\"){\n\t\t\tlog.setError(d->m_errstr);\n\t\t\treturn;\n\t\t}\n"; code+="\t\td->m_errtype=root.attribute(\"type\",\"_server\");\n"; @@ -238,7 +299,7 @@ QString WocQtClientTransaction::trnOutput(const WocTransaction&trn) for(int i=0;iout_"+sl[i]+"="; if(trn.isBoolType(t))code+="str2bool("; code+="root.attribute(\""+sl[i]+"\")"; if(trn.isIntType(t))code+=".toInt()";else @@ -249,25 +310,25 @@ QString WocQtClientTransaction::trnOutput(const WocTransaction&trn) if(trn.isListType(t)){ code+="\tfor(int i=0;im_stage=Error;d->m_errtype=e.component();d->m_errstr=e.error();log.setError(d->m_errstr);}\n"; + code+="\t\ttry{p->out_"+sl[i]+".append("+qtobjtype(trn,sl[i],WocQtOut::Out)+"(nl.at(i).toElement()));}catch(WException e){d->m_stage=Error;d->m_errtype=e.component();d->m_errstr=e.error();log.setError(d->m_errstr);}\n"; }else if(trn.isIntType(t)){ - code+="\t\tout_"+sl[i]+".append(nl.at(i).toElement().text().toInt());\n"; + code+="\t\tp->out_"+sl[i]+".append(nl.at(i).toElement().text().toInt());\n"; }else if(trn.isBoolType(t)){ - code+="\t\tout_"+sl[i]+".append(str2bool(nl.at(i).toElement().text()));\n"; + code+="\t\tp->out_"+sl[i]+".append(str2bool(nl.at(i).toElement().text()));\n"; }else if(trn.isBlobType(t)){ - code+="\t\tout_"+sl[i]+".append(QByteArray::fromBase64(nl.at(i).toElement().text().toAscii()));\n"; + code+="\t\tp->out_"+sl[i]+".append(QByteArray::fromBase64(nl.at(i).toElement().text().toAscii()));\n"; }else{//can only be string - code+="\t\tout_"+sl[i]+".append(nl.at(i).toElement().text());\n"; + code+="\t\tp->out_"+sl[i]+".append(nl.at(i).toElement().text());\n"; } code+="\t}\n"; }else{ code+="\tif(nl.size()>0){\n"; if(trn.isObjectType(t)){ - code+="\t\ttry{out_"+sl[i]+"="+qtobjtype(trn,sl[i],WocQtOut::Out)+"(nl.at(0).toElement());}catch(WException e){d->m_stage=Error;d->m_errtype=e.component();d->m_errstr=e.error();log.setError(d->m_errstr);}\n"; + code+="\t\ttry{p->out_"+sl[i]+"="+qtobjtype(trn,sl[i],WocQtOut::Out)+"(nl.at(0).toElement());}catch(WException e){d->m_stage=Error;d->m_errtype=e.component();d->m_errstr=e.error();log.setError(d->m_errstr);}\n"; }else if(trn.isBlobType(t)){ - code+="\t\tout_"+sl[i]+"=QByteArray::fromBase64(nl.at(0).toElement().text().toAscii());\n"; + code+="\t\tp->out_"+sl[i]+"=QByteArray::fromBase64(nl.at(0).toElement().text().toAscii());\n"; }else{//can only be string - code+="\t\tout_"+sl[i]+"=nl.at(0).toElement().text();\n"; + code+="\t\tp->out_"+sl[i]+"=nl.at(0).toElement().text();\n"; } code+="\t}\n"; } diff --git a/woc/qt/qtout.cpp b/woc/qt/qtout.cpp index ad3dcec..d95382b 100644 --- a/woc/qt/qtout.cpp +++ b/woc/qt/qtout.cpp @@ -140,6 +140,22 @@ void WocQtOut::addFile(QString bn) wrap.write(QString("#include \"src"+bn+".h\"\n").toAscii()); } +void WocQtOut::addFile(WocQtOut::FileType ft,QString fn) +{ + QString code; + switch(ft){ + case Header:code="HEADERS+="+m_subdir+"/"+fn+"\n";break; + case Source:code+="SOURCES+="+m_subdir+"/"+fn+"\n";break; + } + m_pri.write(code.toAscii()); +} + +void WocQtOut::addProjectLine(QString ln) +{ + m_pri.write(QString(ln+"\n").toAscii()); +} + + QString WocQtOut::qttype(const WocClass&cls,QString p,bool dolist) { QString r; diff --git a/woc/qt/qtout.h b/woc/qt/qtout.h index 77781c2..45deeae 100644 --- a/woc/qt/qtout.h +++ b/woc/qt/qtout.h @@ -58,6 +58,12 @@ class WocQtOut:public WocOutput MFile m_pri,m_iface,m_ifacecpp,m_hdr; bool m_clean; + ///types of files generated + enum FileType{ + Header, + Source + }; + /**helper: generate a proper Qt type for a property*/ QString qttype(const WocClass&,QString,bool dolist=true); enum InOut{In,Out}; @@ -65,8 +71,15 @@ class WocQtOut:public WocOutput QString qttype(const WocTransaction&,QString,InOut); /**helper: generate a proper QT type for a transaction variable, WO* only */ QString qtobjtype(const WocTransaction&,QString,InOut); - /**helper: adds a file to the project file*/ + /**helper: adds a file to the project file + \param basename the class name to add*/ void addFile(QString basename); + /**helper: adds a file to the project file + \param fname the file name to add + \param kind the type of file*/ + void addFile(FileType kind,QString fname); + /**helper: add a line to the project file*/ + void addProjectLine(QString); WocQtClass*qclass; WocQtTable*qtable; -- 1.7.2.5