1 /*
2     SPDX-FileCopyrightText: 2006 Tobias Koenig <tokoe@kde.org>
3                   2006 Marc Mutz <mutz@kde.org>
4                   2006 - 2007 Volker Krause <vkrause@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #pragma once
10 
11 #include "akonadicore_export.h"
12 
13 #include <KCompositeJob>
14 
15 #include <memory>
16 
17 class QString;
18 
19 namespace Akonadi
20 {
21 namespace Protocol
22 {
23 class Command;
24 using CommandPtr = QSharedPointer<Command>;
25 }
26 
27 class JobPrivate;
28 class Session;
29 class SessionPrivate;
30 
31 /**
32  * @short Base class for all actions in the Akonadi storage.
33  *
34  * This class encapsulates a request to the pim storage service,
35  * the code looks like
36  *
37  * @code
38  *
39  *  Akonadi::Job *job = new Akonadi::SomeJob( some parameter );
40  *  connect( job, SIGNAL(result(KJob*)),
41  *           this, SLOT(slotResult(KJob*)) );
42  *
43  * @endcode
44  *
45  * The job is queued for execution as soon as the event loop is entered
46  * again.
47  *
48  * And the slotResult is usually at least:
49  *
50  * @code
51  *
52  *  if ( job->error() ) {
53  *    // handle error...
54  *  }
55  *
56  * @endcode
57  *
58  * With the synchronous interface the code looks like
59  *
60  * @code
61  *   Akonadi::SomeJob *job = new Akonadi::SomeJob( some parameter );
62  *   if ( !job->exec() ) {
63  *     qDebug() << "Error:" << job->errorString();
64  *   } else {
65  *     // do something
66  *   }
67  * @endcode
68  *
69  * @warning Using the synchronous method is error prone, use this only
70  *          if the asynchronous access is not possible. See the documentation of
71  *          KJob::exec() for more details.
72  *
73  * Subclasses must reimplement doStart().
74  *
75  * @note KJob-derived objects delete itself, it is thus not possible
76  * to create job objects on the stack!
77  *
78  * @author Volker Krause <vkrause@kde.org>, Tobias Koenig <tokoe@kde.org>, Marc Mutz <mutz@kde.org>
79  */
80 class AKONADICORE_EXPORT Job : public KCompositeJob
81 {
82     Q_OBJECT
83 
84     friend class Session;
85     friend class SessionPrivate;
86 
87 public:
88     /**
89      * Describes a list of jobs.
90      */
91     using List = QList<Job *>;
92 
93     /**
94      * Describes the error codes that can be emitted by this class.
95      * Subclasses can provide additional codes, starting from UserError
96      * onwards
97      */
98     enum Error {
99         ConnectionFailed = UserDefinedError, ///< The connection to the Akonadi server failed.
100         ProtocolVersionMismatch, ///< The server protocol version is too old or too new.
101         UserCanceled, ///< The user canceled this job.
102         Unknown, ///< Unknown error.
103         UserError = UserDefinedError + 42 ///< Starting point for error codes defined by sub-classes.
104     };
105 
106     /**
107      * Creates a new job.
108      *
109      * If the parent object is a Job object, the new job will be a subjob of @p parent.
110      * If the parent object is a Session object, it will be used for server communication
111      * instead of the default session.
112      *
113      * @param parent The parent object, job or session.
114      */
115     explicit Job(QObject *parent = nullptr);
116 
117     /**
118      * Destroys the job.
119      */
120     ~Job() override;
121 
122     /**
123      * Jobs are started automatically once entering the event loop again, no need
124      * to explicitly call this.
125      */
126     void start() override;
127 
128     /**
129      * Returns the error string, if there has been an error, an empty
130      * string otherwise.
131      */
132     Q_REQUIRED_RESULT QString errorString() const final;
133 
134 Q_SIGNALS:
135     /**
136      * This signal is emitted directly before the job will be started.
137      *
138      * @param job The started job.
139      */
140     void aboutToStart(Akonadi::Job *job);
141 
142     /**
143      * This signal is emitted if the job has finished all write operations, ie.
144      * if this signal is emitted, the job guarantees to not call writeData() again.
145      * Do not emit this signal directly, call emitWriteFinished() instead.
146      *
147      * @param job This job.
148      * @see emitWriteFinished()
149      */
150     void writeFinished(Akonadi::Job *job);
151 
152 protected:
153     /**
154      * This method must be reimplemented in the concrete jobs. It will be called
155      * after the job has been started and a connection to the Akonadi backend has
156      * been established.
157      */
158     virtual void doStart() = 0;
159 
160     /**
161      * This method should be reimplemented in the concrete jobs in case you want
162      * to handle incoming data. It will be called on received data from the backend.
163      * The default implementation does nothing.
164      *
165      * @param tag The tag of the corresponding command, empty if this is an untagged response.
166      * @param response The received response
167      *
168      * @return Implementations should return true if the last response was processed and
169      * the job can emit result. Return false if more responses from server are expected.
170      */
171     virtual bool doHandleResponse(qint64 tag, const Protocol::CommandPtr &response);
172 
173     /**
174      * Adds the given job as a subjob to this job. This method is automatically called
175      * if you construct a job using another job as parent object.
176      * The base implementation does the necessary setup to share the network connection
177      * with the backend.
178      *
179      * @param job The new subjob.
180      */
181     bool addSubjob(KJob *job) override;
182 
183     /**
184      * Removes the given subjob of this job.
185      *
186      * @param job The subjob to remove.
187      */
188     bool removeSubjob(KJob *job) override;
189 
190     /**
191      * Kills the execution of the job.
192      */
193     bool doKill() override;
194 
195     /**
196      * Call this method to indicate that this job will not call writeData() again.
197      * @see writeFinished()
198      */
199     void emitWriteFinished();
200 
201 protected Q_SLOTS:
202     void slotResult(KJob *job) override;
203 
204 protected:
205     /// @cond PRIVATE
206     Job(JobPrivate *dd, QObject *parent);
207     std::unique_ptr<JobPrivate> const d_ptr;
208     /// @endcond
209 
210 private:
211     Q_DECLARE_PRIVATE(Job)
212 
213     /// @cond PRIVATE
214     Q_PRIVATE_SLOT(d_func(), void startNext())
215     Q_PRIVATE_SLOT(d_func(), void signalCreationToJobTracker())
216     Q_PRIVATE_SLOT(d_func(), void signalStartedToJobTracker())
217     Q_PRIVATE_SLOT(d_func(), void delayedEmitResult())
218     /// @endcond
219 };
220 
221 }
222 
223