akonadi
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; 00091 new TransactionBeginJob( this ); 00092 } 00093 return Job::addSubjob( job ); 00094 } 00095 00096 void TransactionSequence::slotResult(KJob * job) 00097 { 00098 Q_D( TransactionSequence ); 00099 00100 if ( !job->error() || d->mIgnoredErrorJobs.contains( job ) ) { 00101 // If we have an error but want to ignore it, we can't call Job::slotResult 00102 // because it would confuse the subjob queue processing logic. Just removing 00103 // the subjob instead is fine. 00104 if ( !job->error() ) 00105 Job::slotResult( job ); 00106 else 00107 Job::removeSubjob( job ); 00108 00109 if ( !hasSubjobs() && d->mState == TransactionSequencePrivate::WaitingForSubjobs ) { 00110 d->mState = TransactionSequencePrivate::Committing; 00111 TransactionCommitJob *job = new TransactionCommitJob( this ); 00112 connect( job, SIGNAL( result( KJob* ) ), SLOT( commitResult( KJob* ) ) ); 00113 } 00114 } else { 00115 setError( job->error() ); 00116 setErrorText( job->errorText() ); 00117 removeSubjob( job ); 00118 00119 // cancel all subjobs in case someone else is listening (such as ItemSync), but without notifying ourselves again 00120 foreach ( KJob* job, subjobs() ) { 00121 disconnect( job, SIGNAL(result(KJob*)), this, SLOT(slotResult(KJob*)) ); 00122 job->kill( EmitResult ); 00123 } 00124 clearSubjobs(); 00125 00126 if ( d->mState == TransactionSequencePrivate::Running || d->mState == TransactionSequencePrivate::WaitingForSubjobs ) { 00127 d->mState = TransactionSequencePrivate::RollingBack; 00128 TransactionRollbackJob *job = new TransactionRollbackJob( this ); 00129 connect( job, SIGNAL( result( KJob* ) ), SLOT( rollbackResult( KJob* ) ) ); 00130 } else if ( d->mState == TransactionSequencePrivate::Idle ) { 00131 // we can get here if transactions are disabled 00132 emitResult(); 00133 } 00134 } 00135 } 00136 00137 void TransactionSequence::commit() 00138 { 00139 Q_D( TransactionSequence ); 00140 00141 if ( d->mState == TransactionSequencePrivate::Running ) { 00142 d->mState = TransactionSequencePrivate::WaitingForSubjobs; 00143 } else { 00144 // we never got any subjobs, that means we never started a transaction 00145 // so we can just quit here 00146 if ( d->mState == TransactionSequencePrivate::Idle ) 00147 emitResult(); 00148 return; 00149 } 00150 00151 if ( subjobs().isEmpty() ) { 00152 if ( !error() ) { 00153 d->mState = TransactionSequencePrivate::Committing; 00154 TransactionCommitJob *job = new TransactionCommitJob( this ); 00155 connect( job, SIGNAL( result( KJob* ) ), SLOT( commitResult( KJob* ) ) ); 00156 } else { 00157 d->mState = TransactionSequencePrivate::RollingBack; 00158 TransactionRollbackJob *job = new TransactionRollbackJob( this ); 00159 connect( job, SIGNAL( result( KJob* ) ), SLOT( rollbackResult( KJob* ) ) ); 00160 } 00161 } 00162 } 00163 00164 void TransactionSequence::setIgnoreJobFailure( KJob *job ) 00165 { 00166 Q_D( TransactionSequence ); 00167 00168 // make sure this is one of our sub jobs 00169 Q_ASSERT( subjobs().contains( job ) ); 00170 00171 d->mIgnoredErrorJobs.insert( job ); 00172 } 00173 00174 void TransactionSequence::doStart() 00175 { 00176 Q_D( TransactionSequence ); 00177 00178 if ( d->mAutoCommit ) { 00179 if ( d->mState == TransactionSequencePrivate::Idle ) 00180 emitResult(); 00181 else 00182 commit(); 00183 } 00184 } 00185 00186 void TransactionSequence::setAutomaticCommittingEnabled(bool enable) 00187 { 00188 Q_D( TransactionSequence ); 00189 d->mAutoCommit = enable; 00190 } 00191 00192 void TransactionSequence::rollback() 00193 { 00194 Q_D( TransactionSequence ); 00195 00196 setError( UserCanceled ); 00197 // we never really started 00198 if ( d->mState == TransactionSequencePrivate::Idle ) { 00199 emitResult(); 00200 return; 00201 } 00202 00203 // TODO cancel not yet executed sub-jobs here 00204 00205 d->mState = TransactionSequencePrivate::RollingBack; 00206 TransactionRollbackJob *job = new TransactionRollbackJob( this ); 00207 connect( job, SIGNAL( result( KJob* ) ), SLOT( rollbackResult( KJob* ) ) ); 00208 } 00209 00210 00211 #include "transactionsequence.moc"