///\internal private logger class
class WTransaction::Log{
public:
- Log(WTransaction*,const QString&);
+ /**instantiates the logger
+
+ A logger object is used by transactions to output information about the message exchange that is going on. Depending on the log level of the interface the logger will log very tersely or verbosely. It can log messages immediately or store them to be logged in case of errors.
+
+ \param parent the transaction this is about
+ \param request the name of the request this transaction issues
+ \param interface the name of the interface that is used */
+ Log(WTransaction*parent,const QString&request,const QString&interface=QString());
+ ///deletes the logger
~Log();
+
+ ///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:
QString req,rsp;
- QString trn;
+ QString trn,ifc;
WTransaction*parent;
int lvl,cnt;
};
///\internal private iface
-class WTransaction::Private {
+class WTransaction_Private:public QObject {
+ Q_OBJECT
public:
- Stage m_stage;
+ WTransaction_Private(WTransaction*);
+ /**internal: execute a query synchronously on the web, used by subclasses*/
+ virtual QByteArray executeQuery(QString,QByteArray);
+
+ /**internal: execute a query on the web asynchronously*/
+ virtual void startQuery(QString,QByteArray);
+
+ public slots:
+ /**internal: triggers when the transaction times out*/
+ virtual void webTimeout();
+ /**internal: triggers when the response data is available*/
+ virtual void webReady();
+ /**internal: triggers when the response errors out*/
+ virtual void webError();
+ /**internal: collect query data*/
+ virtual void endQuery();
+ signals:
+ /** \internal this signal is raised when the transaction on the HTTP level finished*/
+ void webFinished();
+
+ public:
+ WTransaction::Stage m_stage;
QString m_errtype,m_errstr,m_iface,m_wobstatus;
- Log*m_log;
+ WTransaction::Log*m_log;
QPointer<QNetworkReply>m_rsp;
QByteArray m_rspdata;
QPointer<QTimer>m_qtimeout;
+
+ public slots:
+ ///attach this private object to a master
+ WTransaction_Private* attach(WTransaction*);
+ ///detach this object from a master, delete it if there are no more masters
+ void detach(QObject*);
+ private:
+ ///internal reference counter
+ int m_refctr;
};
#endif
WTransaction::WTransaction(QString ifc)
{
- d=new Private;
+ d=new WTransaction_Private(this);
d->m_stage=Uninitialized;
d->m_iface=ifc;
d->m_log=0;
- connect(this,SIGNAL(webFinished()),this,SLOT(endQuery()));
}
WTransaction::WTransaction(const WTransaction&t)
:WHelper()
{
- d=new Private;
- d->m_stage=t.d->m_stage;
- d->m_errstr=t.d->m_errstr;
- d->m_errtype=t.d->m_errtype;
- d->m_iface=t.d->m_iface;
- d->m_log=0;
+ d=t.d->attach(this);
connect(this,SIGNAL(webFinished()),this,SLOT(endQuery()));
}
WTransaction::~WTransaction()
{
- if(d)delete d;
d=0;
}
WTransaction& WTransaction::operator=(const WTransaction&t)
{
- d->m_stage=t.d->m_stage;
- d->m_errstr=t.d->m_errstr;
- d->m_errtype=t.d->m_errtype;
- d->m_iface=t.d->m_iface;
+ d->detach(this);
+ d=t.d->attach(this);
return *this;
}
return ret;
}
-void WTransaction::startQuery(QString hreq,QByteArray data)
+void WTransaction::startQuery(QString hreq,QByteArray data){d->startQuery(hreq,data);}
+void WTransaction_Private::startQuery(QString hreq,QByteArray data)
{
QNetworkRequest req;
//find interface
- WInterface *iface=WInterface::instance(d->m_iface);
+ WInterface *iface=WInterface::instance(m_iface);
if(!iface){
- if(d->m_log)d->m_log->setError("cannot find interface.");
+ if(m_log)m_log->setError("cannot find interface.");
else qDebug("Error: transaction cannot find interface.");
- d->m_stage=Error;
- d->m_errtype="_iface";
- d->m_errstr=tr("interface not found");
+ m_stage=WTransaction::Error;
+ m_errtype="_iface";
+ m_errstr=tr("interface not found");
return;
}
//set target
if(url.scheme().toLower()=="http")port=80;
else port=443;
}
- if(d->m_log)d->m_log->setInfo(QString("New HTTP Request %1 URL %2\n\tto host=%3 port=%4 scheme=%5")
+ if(m_log)m_log->setInfo(QString("New HTTP Request %1 URL %2\n\tto host=%3 port=%4 scheme=%5")
.arg(hreq)
.arg(url.toString())
.arg(url.host())
req.setHeader(QNetworkRequest::ContentLengthHeader,QByteArray::number(data.size()));
req.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-webobject; charset=UTF-8");
//send it (with body)
- d->m_rsp=iface->post(req,data);
+ m_rsp=iface->post(req,data);
//latest point of deletion: end of this transaction
- d->m_rsp->setParent(this);
+ m_rsp->setParent(this);
//log request
- if(d->m_log){
- d->m_log->setRequ(hd2str(rqpairs(req)),esc(data));
+ if(m_log){
+ m_log->setRequ(hd2str(rqpairs(req)),esc(data));
}
//we are requesting now...
- d->m_stage=Request;
+ m_stage=WTransaction::Request;
//schedule receiving
- if(d->m_qtimeout.isNull()){
- d->m_qtimeout=new QTimer(this);
- connect(d->m_qtimeout,SIGNAL(timeout()),this,SLOT(webTimeout()));
+ if(m_qtimeout.isNull()){
+ m_qtimeout=new QTimer(this);
+ connect(m_qtimeout,SIGNAL(timeout()),this,SLOT(webTimeout()));
}
- d->m_qtimeout->setSingleShot(true);
- d->m_qtimeout->start(iface->webTimeout()*1000);
- connect(d->m_rsp,SIGNAL(finished()),this,SLOT(webReady()));
- connect(d->m_rsp,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(webError()));
+ m_qtimeout->setSingleShot(true);
+ m_qtimeout->start(iface->webTimeout()*1000);
+ connect(m_rsp,SIGNAL(finished()),this,SLOT(webReady()));
+ connect(m_rsp,SIGNAL(error(QNetworkReply::NetworkError)),this,SLOT(webError()));
}
-void WTransaction::endQuery()
+void WTransaction::endQuery(){d->endQuery();}
+void WTransaction_Private::endQuery()
{
//sanity check
- if(d->m_rsp.isNull())return;
- d->m_rspdata=QByteArray();
+ if(m_rsp.isNull())return;
+ m_rspdata=QByteArray();
//unschedule timeout
- if(!d->m_qtimeout.isNull())
- d->m_qtimeout->stop();
+ if(!m_qtimeout.isNull())
+ m_qtimeout->stop();
//delete response object when done here
QObject rspown;
- connect(&rspown,SIGNAL(destroyed(QObject*)),d->m_rsp,SLOT(deleteLater()));
+ connect(&rspown,SIGNAL(destroyed(QObject*)),m_rsp,SLOT(deleteLater()));
//process result
- if(d->m_stage==Error && d->m_errtype=="_timeout"){
+ if(m_stage==WTransaction::Error && m_errtype=="_timeout"){
//it did not finish yet, caught a timeout.
- d->m_rsp->abort();
- d->m_errstr=tr("Web Request timed out.");
- if(d->m_log)d->m_log->setError("Request timed out.");
+ m_rsp->abort();
+ m_errstr=tr("Web Request timed out.");
+ if(m_log)m_log->setError("Request timed out.");
return;
}else
//finished with low-level error?
- if(d->m_stage==Error){
- d->m_errstr="HTTP Error: "+d->m_rsp->errorString();
- if(d->m_log)d->m_log->setError(QString("Request finished with HTTP level error: %1").arg(d->m_errstr));
+ if(m_stage==WTransaction::Error){
+ m_errstr="HTTP Error: "+m_rsp->errorString();
+ if(m_log)m_log->setError(QString("Request finished with HTTP level error: %1").arg(m_errstr));
return;
}
//get data
- d->m_wobstatus=d->m_rsp->rawHeader("X-WobResponse-Status");
- d->m_wobstatus=d->m_wobstatus.replace("\"","").trimmed().toLower();
- d->m_rspdata=d->m_rsp->readAll();
- if(d->m_log)d->m_log->setResp(hd2str(d->m_rsp->rawHeaderPairs()),esc(d->m_rspdata));
+ m_wobstatus=m_rsp->rawHeader("X-WobResponse-Status");
+ m_wobstatus=m_wobstatus.replace("\"","").trimmed().toLower();
+ m_rspdata=m_rsp->readAll();
+ if(m_log)m_log->setResp(hd2str(m_rsp->rawHeaderPairs()),esc(m_rspdata));
//check for high level error
- int statcode=d->m_rsp->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
+ int statcode=m_rsp->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
if(statcode!=200){
- d->m_errstr=tr("HTTP Error, return code %1 %2") .arg(statcode).arg(d->m_rsp->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
- d->m_errtype="_HTTP";
- d->m_stage=Error;
- if(d->m_log)d->m_log->setError(QString("Request finished with HTTP level error: %1").arg(d->m_errstr));
+ m_errstr=tr("HTTP Error, return code %1 %2") .arg(statcode).arg(m_rsp->attribute(QNetworkRequest::HttpReasonPhraseAttribute).toString());
+ m_errtype="_HTTP";
+ m_stage=WTransaction::Error;
+ if(m_log)m_log->setError(QString("Request finished with HTTP level error: %1").arg(m_errstr));
return;
}
- if(d->m_stage==Request){
- d->m_stage=Error;
- d->m_errtype="_internal";
- d->m_errstr="Ooops: still in Request phase at end of response handling.";
+ if(m_stage==WTransaction::Request){
+ m_stage=WTransaction::Error;
+ m_errtype="_internal";
+ m_errstr="Ooops: still in Request phase at end of response handling.";
}
//remaining high level errors are handled by the generated code
}
-QByteArray WTransaction::executeQuery(QString hreq,QByteArray data)
+QByteArray WTransaction::executeQuery(QString hreq,QByteArray data){return d->executeQuery(hreq,data);}
+QByteArray WTransaction_Private::executeQuery(QString hreq,QByteArray data)
{
+ //sanity check
+ if(!m_rsp.isNull() || m_stage==WTransaction::Request)
+ return QByteArray();
+
//set up request
startQuery(hreq,data);
- if(d->m_rsp.isNull() || d->m_stage!=Request)
+ if(m_rsp.isNull() || m_stage!=WTransaction::Request)
return QByteArray();
/////////////////////////////////////////////////////////////////////
loop.exec();
/////////////////////////////////////////////////////////////////////
- if(d->m_stage==Success)
- return d->m_rspdata;
+ if(m_stage==WTransaction::Success)
+ return m_rspdata;
else
return QByteArray();
}
-void WTransaction::webTimeout()
+void WTransaction_Private::webTimeout()
{
- if(d->m_stage!=Request)return;
+ if(m_stage!=WTransaction::Request)return;
// qDebug("web timeout");
- if(d->m_log)d->m_log->setInfo(QString("timeout in request"));
- d->m_stage=Error;
- d->m_errtype="_timeout";
+ if(m_log)m_log->setInfo(QString("timeout in request"));
+ m_stage=WTransaction::Error;
+ m_errtype="_timeout";
emit webFinished();
}
-void WTransaction::webReady()
+void WTransaction_Private::webReady()
{
- if(d->m_stage!=Request)return;
- if(d->m_log)d->m_log->setInfo(QString("finished request"));
- d->m_stage=Success;
- d->m_errtype="";
+ if(m_stage!=WTransaction::Request)return;
+ if(m_log)m_log->setInfo(QString("finished request"));
+ m_stage=WTransaction::Success;
+ m_errtype="";
emit webFinished();
}
-void WTransaction::webError()
+void WTransaction_Private::webError()
{
- if(d->m_stage!=Request)return;
- if(d->m_log)d->m_log->setInfo(QString("error in request"));
- d->m_stage=Error;
- d->m_errtype="_web";
+ if(m_stage!=WTransaction::Request)return;
+ if(m_log)m_log->setInfo(QString("error in request"));
+ m_stage=WTransaction::Error;
+ m_errtype="_web";
emit webFinished();
}
QString WTransaction::interface()const{return d->m_iface;}
+/*****************************************************************************/
+
static int logid=0;
-WTransaction::Log::Log(WTransaction*p,const QString&s)
+WTransaction::Log::Log(WTransaction*p,const QString&s,const QString&i)
{
+ Q_ASSERT_X(p!=0,"WTransaction::Log constructor","Logger Parent must be a valid transaction object.");
parent=p;
+ if(parent->d->m_log!=0)delete parent->d->m_log;
parent->d->m_log=this;
trn=s;
+ if(i.isNull())
+ ifc=p->interface();
+ else
+ ifc=i;
cnt=logid++;
WInterface*in=WInterface::instance(p->d->m_iface);
if(in)lvl=in->logLevel();
else lvl=WInterface::LogOnError;
if(lvl>WInterface::LogNone)
- outStr(QString("Running transaction %1").arg(s));
+ outStr(QString("Running transaction %1 on interface %2").arg(s).arg(ifc));
}
WTransaction::Log::~Log()
{
- parent->d->m_log=0;
+ if(parent->d->m_log==this)
+ parent->d->m_log=0;
}
void WTransaction::Log::outStr(const QString&s)
}
//show actual error
outStr(QString("Transaction %1 Error: %2").arg(trn).arg(s));
-}
\ No newline at end of file
+}
+
+/*****************************************************************************/
+
+WTransaction_Private::WTransaction_Private(WTransaction*trn)
+{
+ m_stage=WTransaction::Uninitialized;
+ m_log=0;
+ m_refctr=0;
+ attach(trn);
+ connect(this,SIGNAL(webFinished()),this,SLOT(endQuery()));
+}
+
+WTransaction_Private* WTransaction_Private::attach(WTransaction*trn)
+{
+ m_refctr++;
+ connect(trn,SIGNAL(destroyed(QObject*)),this,SLOT(detach(QObject*)));
+ return this;
+}
+
+void WTransaction_Private::detach(QObject*trn)
+{
+ m_refctr--;
+ if(m_refctr==0)
+ deleteLater();
+ else if(trn){
+ disconnect(trn);
+ trn->disconnect(this);
+ }
+}