Akonadi

transactionsequence.cpp
1 /*
2  Copyright (c) 2006-2008 Volker Krause <[email protected]>
3 
4  This library is free software; you can redistribute it and/or modify it
5  under the terms of the GNU Library General Public License as published by
6  the Free Software Foundation; either version 2 of the License, or (at your
7  option) any later version.
8 
9  This library is distributed in the hope that it will be useful, but WITHOUT
10  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
12  License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to the
16  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  02110-1301, USA.
18 */
19 
20 #include "transactionsequence.h"
21 #include "transactionjobs.h"
22 
23 #include "job_p.h"
24 
25 #include <QSet>
26 
27 using namespace Akonadi;
28 
29 class Akonadi::TransactionSequencePrivate : public JobPrivate
30 {
31 public:
32  TransactionSequencePrivate(TransactionSequence *parent)
33  : JobPrivate(parent)
34  , mState(Idle)
35  {
36  }
37 
38  enum TransactionState {
39  Idle,
40  Running,
41  WaitingForSubjobs,
42  RollingBack,
43  Committing
44  };
45 
46  Q_DECLARE_PUBLIC(TransactionSequence)
47 
48  TransactionState mState;
49  QSet<KJob *> mIgnoredErrorJobs;
50  bool mAutoCommit = true;
51 
52  void commitResult(KJob *job)
53  {
55 
56  if (job->error()) {
57  q->setError(job->error());
58  q->setErrorText(job->errorText());
59  }
60  q->emitResult();
61  }
62 
63  void rollbackResult(KJob *job)
64  {
66 
67  Q_UNUSED(job);
68  q->emitResult();
69  }
70 
71  QString jobDebuggingString() const override;
72 };
73 
74 QString Akonadi::TransactionSequencePrivate::jobDebuggingString() const
75 {
76  //TODO add state
77  return QStringLiteral("autocommit %1").arg(mAutoCommit);
78 }
79 
81  : Job(new TransactionSequencePrivate(this), parent)
82 {
83 }
84 
86 {
87 }
88 
90 {
92 
93  //Don't abort the rollback job, while keeping the state set.
94  if (d->mState == TransactionSequencePrivate::RollingBack) {
95  return Job::addSubjob(job);
96  }
97 
98  if (error()) {
99  //This can happen if a rollback is in progress, so make sure we don't set the state back to running.
100  job->kill(EmitResult);
101  return false;
102  }
103  // TODO KDE5: remove property hack once SpecialCollectionsRequestJob has been fixed
104  if (d->mState == TransactionSequencePrivate::Idle && !property("transactionsDisabled").toBool()) {
105  d->mState = TransactionSequencePrivate::Running; // needs to be set before creating the transaction job to avoid infinite recursion
106  new TransactionBeginJob(this);
107  } else {
108  d->mState = TransactionSequencePrivate::Running;
109  }
110  return Job::addSubjob(job);
111 }
112 
113 void TransactionSequence::slotResult(KJob *job)
114 {
115  Q_D(TransactionSequence);
116 
117  if (!job->error() || d->mIgnoredErrorJobs.contains(job)) {
118  // If we have an error but want to ignore it, we can't call Job::slotResult
119  // because it would confuse the subjob queue processing logic. Just removing
120  // the subjob instead is fine.
121  if (!job->error()) {
122  Job::slotResult(job);
123  } else {
124  Job::removeSubjob(job);
125  }
126 
127  if (!hasSubjobs()) {
128  if (d->mState == TransactionSequencePrivate::WaitingForSubjobs) {
129  if (property("transactionsDisabled").toBool()) {
130  emitResult();
131  return;
132  }
133  d->mState = TransactionSequencePrivate::Committing;
135  connect(job, &TransactionCommitJob::result, [d](KJob *job) { d->commitResult(job);});
136  }
137  }
138  } else if (job->error() == KJob::KilledJobError) {
139  Job::slotResult(job);
140  } else {
141  setError(job->error());
142  setErrorText(job->errorText());
143  removeSubjob(job);
144 
145  // cancel all subjobs in case someone else is listening (such as ItemSync)
146  const auto subjobs = this->subjobs();
147  for (KJob *job : subjobs) {
148  job->kill(KJob::EmitResult);
149  }
150  clearSubjobs();
151 
152  if (d->mState == TransactionSequencePrivate::Running || d->mState == TransactionSequencePrivate::WaitingForSubjobs) {
153  if (property("transactionsDisabled").toBool()) {
154  emitResult();
155  return;
156  }
157  d->mState = TransactionSequencePrivate::RollingBack;
159  connect(job, &TransactionRollbackJob::result, this, [d](KJob *job) { d->rollbackResult(job);});
160  }
161  }
162 }
163 
165 {
166  Q_D(TransactionSequence);
167 
168  if (d->mState == TransactionSequencePrivate::Running) {
169  d->mState = TransactionSequencePrivate::WaitingForSubjobs;
170  } else if (d->mState == TransactionSequencePrivate::RollingBack) {
171  return;
172  } else {
173  // we never got any subjobs, that means we never started a transaction
174  // so we can just quit here
175  if (d->mState == TransactionSequencePrivate::Idle) {
176  emitResult();
177  }
178  return;
179  }
180 
181  if (subjobs().isEmpty()) {
182  if (property("transactionsDisabled").toBool()) {
183  emitResult();
184  return;
185  }
186  if (!error()) {
187  d->mState = TransactionSequencePrivate::Committing;
189  connect(job, &TransactionCommitJob::result, this, [d](KJob *job) { d->commitResult(job);});
190  } else {
191  d->mState = TransactionSequencePrivate::RollingBack;
193  connect(job, &TransactionRollbackJob::result, this, [d](KJob *job) { d->rollbackResult(job);});
194  }
195  }
196 }
197 
199 {
200  Q_D(TransactionSequence);
201 
202  // make sure this is one of our sub jobs
203  Q_ASSERT(subjobs().contains(job));
204 
205  d->mIgnoredErrorJobs.insert(job);
206 }
207 
209 {
210  Q_D(TransactionSequence);
211 
212  if (d->mAutoCommit) {
213  if (d->mState == TransactionSequencePrivate::Idle) {
214  emitResult();
215  } else {
216  commit();
217  }
218  }
219 }
220 
222 {
223  Q_D(TransactionSequence);
224  d->mAutoCommit = enable;
225 }
226 
228 {
229  Q_D(TransactionSequence);
230 
232  // we never really started
233  if (d->mState == TransactionSequencePrivate::Idle) {
234  emitResult();
235  return;
236  }
237 
238  const auto jobList = subjobs();
239  for (KJob *job : jobList) {
240  // Killing the current subjob means forcibly closing the akonadiserver socket
241  // (with a bit of delay since it happens in a secondary thread)
242  // which means the next job gets disconnected
243  // and the itemsync finishes with error "Cannot connect to the Akonadi service.", not ideal
244  if (job != d->mCurrentSubJob) {
245  job->kill(KJob::EmitResult);
246  }
247  }
248 
249  d->mState = TransactionSequencePrivate::RollingBack;
251  connect(job, &TransactionRollbackJob::result, this, [d](KJob *job) { d->rollbackResult(job);});
252 }
253 
254 #include "moc_transactionsequence.cpp"
bool kill(KillVerbosity verbosity=Quietly)
void emitResult()
void rollback()
Rolls back the current transaction as soon as possible.
Job that commits a session-global transaction.
void setError(int errorCode)
Base class for all actions in the Akonadi storage.
Definition: job.h:93
bool hasSubjobs() const
void setAutomaticCommittingEnabled(bool enable)
Disable automatic committing.
bool removeSubjob(KJob *job) override
Removes the given subjob of this job.
Definition: job.cpp:375
bool addSubjob(KJob *job) override
Adds the given job as a subjob to this job.
void setErrorText(const QString &errorText)
QVariant property(const char *name) const const
Job that aborts a session-global transaction.
void setIgnoreJobFailure(KJob *job)
Sets which job of the sequence might fail without rolling back the complete transaction.
~TransactionSequence() override
Destroys the transaction sequence.
void commit()
Commits the transaction as soon as all pending sub-jobs finished successfully.
void doStart() override
This method must be reimplemented in the concrete jobs.
void clearSubjobs()
Base class for jobs that need to run a sequence of sub-jobs in a transaction.
Helper integration between Akonadi and Qt.
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
TransactionSequence(QObject *parent=nullptr)
Creates a new transaction sequence.
const QList< KJob * > & subjobs() const
void result(KJob *job)
The user canceled this job.
Definition: job.h:114
Job that begins a session-global transaction.
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool addSubjob(KJob *job) override
Adds the given job as a subjob to this job.
Definition: job.cpp:365
QString errorText() const
int error() const
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Fri Jun 5 2020 23:08:56 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.