transactionsequence.cpp
00001 /* 00002 Copyright (c) 2006-2008 Volker Krause <vkrause@kde.org> 00003 00004 This library is free software; you can redistribute it and/or modify it 00005 under the terms of the GNU Library General Public License as published by 00006 the Free Software Foundation; either version 2 of the License, or (at your 00007 option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, but WITHOUT 00010 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 00011 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public 00012 License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to the 00016 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 00017 02110-1301, USA. 00018 */ 00019 00020 #include "transactionsequence.h" 00021 #include "transactionjobs.h" 00022 00023 #include "job_p.h" 00024 00025 #include <QtCore/QSet> 00026 #include <QtCore/QVariant> 00027 00028 using namespace Akonadi; 00029 00030 class Akonadi::TransactionSequencePrivate : public JobPrivate 00031 { 00032 public: 00033 TransactionSequencePrivate( TransactionSequence *parent ) 00034 : JobPrivate( parent ), 00035 mState( Idle ), 00036 mAutoCommit( true ) 00037 { 00038 } 00039 00040 enum TransactionState 00041 { 00042 Idle, 00043 Running, 00044 WaitingForSubjobs, 00045 RollingBack, 00046 Committing 00047 }; 00048 00049 Q_DECLARE_PUBLIC( TransactionSequence ) 00050 00051 TransactionState mState; 00052 QSet<KJob*> mIgnoredErrorJobs; 00053 bool mAutoCommit; 00054 00055 void commitResult( KJob *job ) 00056 { 00057 Q_Q( TransactionSequence ); 00058 00059 if ( job->error() ) { 00060 q->setError( job->error() ); 00061 q->setErrorText( job->errorText() ); 00062 } 00063 q->emitResult(); 00064 } 00065 00066 void rollbackResult( KJob *job ) 00067 { 00068 Q_Q( TransactionSequence ); 00069 00070 Q_UNUSED( job ); 00071 q->emitResult(); 00072 } 00073 }; 00074 00075 TransactionSequence::TransactionSequence( QObject * parent ) 00076 : Job( new TransactionSequencePrivate( this ), parent ) 00077 { 00078 } 00079 00080 TransactionSequence::~TransactionSequence() 00081 { 00082 } 00083 00084 bool TransactionSequence::addSubjob(KJob * job) 00085 { 00086 Q_D( TransactionSequence ); 00087 00088 // TODO KDE5: remove property hack once SpecialCollectionsRequestJob has been fixed 00089 if ( d->mState == TransactionSequencePrivate::Idle && !property( "transactionsDisabled" ).toBool() ) { 00090 d->mState = TransactionSequencePrivate::Running; // needs to be set before creating the transaction job to avoid infinite recursion 00091 new TransactionBeginJob( this ); 00092 } else { 00093 d->mState = TransactionSequencePrivate::Running; 00094 } 00095 return Job::addSubjob( job ); 00096 } 00097 00098 void TransactionSequence::slotResult(KJob * job) 00099 { 00100 Q_D( TransactionSequence ); 00101 00102 if ( !job->error() || d->mIgnoredErrorJobs.contains( job ) ) { 00103 // If we have an error but want to ignore it, we can't call Job::slotResult 00104 // because it would confuse the subjob queue processing logic. Just removing 00105 // the subjob instead is fine. 00106 if ( !job->error() ) 00107 Job::slotResult( job ); 00108 else 00109 Job::removeSubjob( job ); 00110 00111 if ( !hasSubjobs() && d->mState == TransactionSequencePrivate::WaitingForSubjobs ) { 00112 if ( property( "transactionsDisabled" ).toBool() ) { 00113 emitResult(); 00114 return; 00115 } 00116 d->mState = TransactionSequencePrivate::Committing; 00117 TransactionCommitJob *job = new TransactionCommitJob( this ); 00118 connect( job, SIGNAL(result(KJob*)), SLOT(commitResult(KJob*)) ); 00119 } 00120 } else { 00121 setError( job->error() ); 00122 setErrorText( job->errorText() ); 00123 removeSubjob( job ); 00124 00125 // cancel all subjobs in case someone else is listening (such as ItemSync), but without notifying ourselves again 00126 foreach ( KJob* job, subjobs() ) { 00127 disconnect( job, SIGNAL(result(KJob*)), this, SLOT(slotResult(KJob*)) ); 00128 job->kill( EmitResult ); 00129 } 00130 clearSubjobs(); 00131 00132 if ( d->mState == TransactionSequencePrivate::Running || d->mState == TransactionSequencePrivate::WaitingForSubjobs ) { 00133 if ( property( "transactionsDisabled" ).toBool() ) { 00134 emitResult(); 00135 return; 00136 } 00137 d->mState = TransactionSequencePrivate::RollingBack; 00138 TransactionRollbackJob *job = new TransactionRollbackJob( this ); 00139 connect( job, SIGNAL(result(KJob*)), SLOT(rollbackResult(KJob*)) ); 00140 } 00141 } 00142 } 00143 00144 void TransactionSequence::commit() 00145 { 00146 Q_D( TransactionSequence ); 00147 00148 if ( d->mState == TransactionSequencePrivate::Running ) { 00149 d->mState = TransactionSequencePrivate::WaitingForSubjobs; 00150 } else { 00151 // we never got any subjobs, that means we never started a transaction 00152 // so we can just quit here 00153 if ( d->mState == TransactionSequencePrivate::Idle ) 00154 emitResult(); 00155 return; 00156 } 00157 00158 if ( subjobs().isEmpty() ) { 00159 if ( property( "transactionsDisabled" ).toBool() ) { 00160 emitResult(); 00161 return; 00162 } 00163 if ( !error() ) { 00164 d->mState = TransactionSequencePrivate::Committing; 00165 TransactionCommitJob *job = new TransactionCommitJob( this ); 00166 connect( job, SIGNAL(result(KJob*)), SLOT(commitResult(KJob*)) ); 00167 } else { 00168 d->mState = TransactionSequencePrivate::RollingBack; 00169 TransactionRollbackJob *job = new TransactionRollbackJob( this ); 00170 connect( job, SIGNAL(result(KJob*)), SLOT(rollbackResult(KJob*)) ); 00171 } 00172 } 00173 } 00174 00175 void TransactionSequence::setIgnoreJobFailure( KJob *job ) 00176 { 00177 Q_D( TransactionSequence ); 00178 00179 // make sure this is one of our sub jobs 00180 Q_ASSERT( subjobs().contains( job ) ); 00181 00182 d->mIgnoredErrorJobs.insert( job ); 00183 } 00184 00185 void TransactionSequence::doStart() 00186 { 00187 Q_D( TransactionSequence ); 00188 00189 if ( d->mAutoCommit ) { 00190 if ( d->mState == TransactionSequencePrivate::Idle ) 00191 emitResult(); 00192 else 00193 commit(); 00194 } 00195 } 00196 00197 void TransactionSequence::setAutomaticCommittingEnabled(bool enable) 00198 { 00199 Q_D( TransactionSequence ); 00200 d->mAutoCommit = enable; 00201 } 00202 00203 void TransactionSequence::rollback() 00204 { 00205 Q_D( TransactionSequence ); 00206 00207 setError( UserCanceled ); 00208 // we never really started 00209 if ( d->mState == TransactionSequencePrivate::Idle ) { 00210 emitResult(); 00211 return; 00212 } 00213 00214 // TODO cancel not yet executed sub-jobs here 00215 00216 d->mState = TransactionSequencePrivate::RollingBack; 00217 TransactionRollbackJob *job = new TransactionRollbackJob( this ); 00218 connect( job, SIGNAL(result(KJob*)), SLOT(rollbackResult(KJob*)) ); 00219 } 00220 00221 00222 #include "transactionsequence.moc"