1 | /* |
2 | Copyright (c) 2006-2008 Volker Krause <vkrause@kde.org> |
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 <QtCore/QSet> |
26 | #include <QtCore/QVariant> |
27 | |
28 | using namespace Akonadi; |
29 | |
30 | class Akonadi::TransactionSequencePrivate : public JobPrivate |
31 | { |
32 | public: |
33 | TransactionSequencePrivate(TransactionSequence *parent) |
34 | : JobPrivate(parent) |
35 | , mState(Idle) |
36 | , mAutoCommit(true) |
37 | { |
38 | } |
39 | |
40 | enum TransactionState { |
41 | Idle, |
42 | Running, |
43 | WaitingForSubjobs, |
44 | RollingBack, |
45 | Committing |
46 | }; |
47 | |
48 | Q_DECLARE_PUBLIC(TransactionSequence) |
49 | |
50 | TransactionState mState; |
51 | QSet<KJob *> mIgnoredErrorJobs; |
52 | bool mAutoCommit; |
53 | |
54 | void commitResult(KJob *job) |
55 | { |
56 | Q_Q(TransactionSequence); |
57 | |
58 | if (job->error()) { |
59 | q->setError(job->error()); |
60 | q->setErrorText(job->errorText()); |
61 | } |
62 | q->emitResult(); |
63 | } |
64 | |
65 | void rollbackResult(KJob *job) |
66 | { |
67 | Q_Q(TransactionSequence); |
68 | |
69 | Q_UNUSED(job); |
70 | q->emitResult(); |
71 | } |
72 | }; |
73 | |
74 | TransactionSequence::TransactionSequence(QObject *parent) |
75 | : Job(new TransactionSequencePrivate(this), parent) |
76 | { |
77 | } |
78 | |
79 | TransactionSequence::~TransactionSequence() |
80 | { |
81 | } |
82 | |
83 | bool TransactionSequence::addSubjob(KJob *job) |
84 | { |
85 | Q_D(TransactionSequence); |
86 | |
87 | // TODO KDE5: remove property hack once SpecialCollectionsRequestJob has been fixed |
88 | if (d->mState == TransactionSequencePrivate::Idle && !property("transactionsDisabled" ).toBool()) { |
89 | d->mState = TransactionSequencePrivate::Running; // needs to be set before creating the transaction job to avoid infinite recursion |
90 | new TransactionBeginJob(this); |
91 | } else { |
92 | d->mState = TransactionSequencePrivate::Running; |
93 | } |
94 | return Job::addSubjob(job); |
95 | } |
96 | |
97 | void TransactionSequence::slotResult(KJob *job) |
98 | { |
99 | Q_D(TransactionSequence); |
100 | |
101 | if (!job->error() || d->mIgnoredErrorJobs.contains(job)) { |
102 | // If we have an error but want to ignore it, we can't call Job::slotResult |
103 | // because it would confuse the subjob queue processing logic. Just removing |
104 | // the subjob instead is fine. |
105 | if (!job->error()) { |
106 | Job::slotResult(job); |
107 | } else { |
108 | Job::removeSubjob(job); |
109 | } |
110 | |
111 | if (!hasSubjobs() && d->mState == TransactionSequencePrivate::WaitingForSubjobs) { |
112 | if (property("transactionsDisabled" ).toBool()) { |
113 | emitResult(); |
114 | return; |
115 | } |
116 | d->mState = TransactionSequencePrivate::Committing; |
117 | TransactionCommitJob *job = new TransactionCommitJob(this); |
118 | connect(job, SIGNAL(result(KJob*)), SLOT(commitResult(KJob*))); |
119 | } |
120 | } else { |
121 | setError(job->error()); |
122 | setErrorText(job->errorText()); |
123 | removeSubjob(job); |
124 | |
125 | // cancel all subjobs in case someone else is listening (such as ItemSync), but without notifying ourselves again |
126 | foreach (KJob *job, subjobs()) { |
127 | disconnect(job, SIGNAL(result(KJob*)), this, SLOT(slotResult(KJob*))); |
128 | job->kill(EmitResult); |
129 | } |
130 | clearSubjobs(); |
131 | |
132 | if (d->mState == TransactionSequencePrivate::Running || d->mState == TransactionSequencePrivate::WaitingForSubjobs) { |
133 | if (property("transactionsDisabled" ).toBool()) { |
134 | emitResult(); |
135 | return; |
136 | } |
137 | d->mState = TransactionSequencePrivate::RollingBack; |
138 | TransactionRollbackJob *job = new TransactionRollbackJob(this); |
139 | connect(job, SIGNAL(result(KJob*)), SLOT(rollbackResult(KJob*))); |
140 | } |
141 | } |
142 | } |
143 | |
144 | void TransactionSequence::commit() |
145 | { |
146 | Q_D(TransactionSequence); |
147 | |
148 | if (d->mState == TransactionSequencePrivate::Running) { |
149 | d->mState = TransactionSequencePrivate::WaitingForSubjobs; |
150 | } else { |
151 | // we never got any subjobs, that means we never started a transaction |
152 | // so we can just quit here |
153 | if (d->mState == TransactionSequencePrivate::Idle) { |
154 | emitResult(); |
155 | } |
156 | return; |
157 | } |
158 | |
159 | if (subjobs().isEmpty()) { |
160 | if (property("transactionsDisabled" ).toBool()) { |
161 | emitResult(); |
162 | return; |
163 | } |
164 | if (!error()) { |
165 | d->mState = TransactionSequencePrivate::Committing; |
166 | TransactionCommitJob *job = new TransactionCommitJob(this); |
167 | connect(job, SIGNAL(result(KJob*)), SLOT(commitResult(KJob*))); |
168 | } else { |
169 | d->mState = TransactionSequencePrivate::RollingBack; |
170 | TransactionRollbackJob *job = new TransactionRollbackJob(this); |
171 | connect(job, SIGNAL(result(KJob*)), SLOT(rollbackResult(KJob*))); |
172 | } |
173 | } |
174 | } |
175 | |
176 | void TransactionSequence::setIgnoreJobFailure(KJob *job) |
177 | { |
178 | Q_D(TransactionSequence); |
179 | |
180 | // make sure this is one of our sub jobs |
181 | Q_ASSERT(subjobs().contains(job)); |
182 | |
183 | d->mIgnoredErrorJobs.insert(job); |
184 | } |
185 | |
186 | void TransactionSequence::doStart() |
187 | { |
188 | Q_D(TransactionSequence); |
189 | |
190 | if (d->mAutoCommit) { |
191 | if (d->mState == TransactionSequencePrivate::Idle) { |
192 | emitResult(); |
193 | } else { |
194 | commit(); |
195 | } |
196 | } |
197 | } |
198 | |
199 | void TransactionSequence::setAutomaticCommittingEnabled(bool enable) |
200 | { |
201 | Q_D(TransactionSequence); |
202 | d->mAutoCommit = enable; |
203 | } |
204 | |
205 | void TransactionSequence::rollback() |
206 | { |
207 | Q_D(TransactionSequence); |
208 | |
209 | setError(UserCanceled); |
210 | // we never really started |
211 | if (d->mState == TransactionSequencePrivate::Idle) { |
212 | emitResult(); |
213 | return; |
214 | } |
215 | |
216 | // TODO cancel not yet executed sub-jobs here |
217 | |
218 | d->mState = TransactionSequencePrivate::RollingBack; |
219 | TransactionRollbackJob *job = new TransactionRollbackJob(this); |
220 | connect(job, SIGNAL(result(KJob*)), SLOT(rollbackResult(KJob*))); |
221 | } |
222 | |
223 | #include "moc_transactionsequence.cpp" |
224 | |