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