1 | /* |
2 | * Copyright 2013 Christian Mollekopf <mollekopf@kolabsys.com> |
3 | * |
4 | * This program is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU General Public License as |
6 | * published by the Free Software Foundation; either version 2 of |
7 | * the License or (at your option) version 3 or any later version |
8 | * accepted by the membership of KDE e.V. (or its successor approved |
9 | * by the membership of KDE e.V.), which shall act as a proxy |
10 | * defined in Section 14 of version 3 of the license. |
11 | * |
12 | * This program is distributed in the hope that it will be useful, |
13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | * GNU General Public License for more details. |
16 | * |
17 | * You should have received a copy of the GNU General Public License |
18 | * along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | * |
20 | */ |
21 | |
22 | #include "migratorbase.h" |
23 | #include <KStandardDirs> |
24 | #include <KDebug> |
25 | #include <KComponentData> |
26 | #include <KLocalizedString> |
27 | #include <akonadi/servermanager.h> |
28 | #include <QFile> |
29 | #include <QDateTime> |
30 | |
31 | static QString messageTypeToString(MigratorBase::MessageType type) |
32 | { |
33 | switch (type) { |
34 | case MigratorBase::Success: return QLatin1String("Success" ); |
35 | case MigratorBase::Skip: return QLatin1String("Skipped" ); |
36 | case MigratorBase::Info: return QLatin1String("Info " ); |
37 | case MigratorBase::Warning: return QLatin1String("WARNING" ); |
38 | case MigratorBase::Error: return QLatin1String("ERROR " ); |
39 | } |
40 | Q_ASSERT(false); |
41 | return QString(); |
42 | } |
43 | |
44 | |
45 | static QMap<QString, MigratorBase::MigrationState> fillMigrationStateMapping() |
46 | { |
47 | QMap<QString, MigratorBase::MigrationState> map; |
48 | map.insert(QLatin1String("Complete" ), MigratorBase::Complete); |
49 | map.insert(QLatin1String("Aborted" ), MigratorBase::Aborted); |
50 | map.insert(QLatin1String("InProgress" ), MigratorBase::InProgress); |
51 | map.insert(QLatin1String("Failed" ), MigratorBase::Failed); |
52 | return map; |
53 | } |
54 | |
55 | static QMap<QString, MigratorBase::MigrationState> migrationStateMapping = fillMigrationStateMapping(); |
56 | |
57 | static QString stateToIdentifier(MigratorBase::MigrationState state) |
58 | { |
59 | Q_ASSERT(migrationStateMapping.values().contains(state)); |
60 | return migrationStateMapping.key(state); |
61 | } |
62 | |
63 | static MigratorBase::MigrationState identifierToState(const QString &identifier) |
64 | { |
65 | Q_ASSERT(migrationStateMapping.contains(identifier)); |
66 | return migrationStateMapping.value(identifier); |
67 | } |
68 | |
69 | MigratorBase::MigratorBase(const QString &identifier, QObject *parent) |
70 | : QObject(parent), |
71 | mIdentifier(identifier), |
72 | mMigrationState(None), |
73 | mConfig(new KConfig(Akonadi::ServerManager::addNamespace(QLatin1String("akonadi-migrationrc" )))) |
74 | { |
75 | const QString logFileName = KStandardDirs::locateLocal("data" , KGlobal::mainComponent().componentName() + QLatin1String("/" ) + identifier + QLatin1String("migration.log" )); |
76 | setLogfile(logFileName); |
77 | connect(this, SIGNAL(message(MigratorBase::MessageType,QString)), SLOT(logMessage(MigratorBase::MessageType,QString))); |
78 | loadState(); |
79 | } |
80 | |
81 | MigratorBase::MigratorBase(const QString &identifier, const QString &configFile, const QString &logFile, QObject *parent) |
82 | : QObject(parent), |
83 | mIdentifier(identifier), |
84 | mMigrationState(None) |
85 | { |
86 | if (!configFile.isEmpty()) { |
87 | mConfig.reset(new KConfig(configFile)); |
88 | } |
89 | setLogfile(logFile); |
90 | connect(this, SIGNAL(message(MigratorBase::MessageType,QString)), SLOT(logMessage(MigratorBase::MessageType,QString))); |
91 | loadState(); |
92 | } |
93 | |
94 | MigratorBase::~MigratorBase() |
95 | { |
96 | |
97 | } |
98 | |
99 | void MigratorBase::setLogfile(const QString &logfile) |
100 | { |
101 | if (!logfile.isEmpty()) { |
102 | mLogFile.reset(new QFile(logfile)); |
103 | if (!mLogFile->open(QFile::Append)) { |
104 | mLogFile.reset(); |
105 | kWarning() << "Unable to open log file: " << logfile; |
106 | } |
107 | } else { |
108 | mLogFile.reset(); |
109 | } |
110 | } |
111 | |
112 | QString MigratorBase::identifier() const |
113 | { |
114 | return mIdentifier; |
115 | } |
116 | |
117 | QString MigratorBase::displayName() const |
118 | { |
119 | return QString(); |
120 | } |
121 | |
122 | QString MigratorBase::description() const |
123 | { |
124 | return QString(); |
125 | } |
126 | |
127 | QString MigratorBase::logfile() const |
128 | { |
129 | if (mLogFile) { |
130 | return mLogFile->fileName(); |
131 | } |
132 | return QString(); |
133 | } |
134 | |
135 | bool MigratorBase::canStart() |
136 | { |
137 | if (mIdentifier.isEmpty()) { |
138 | emit message(Error, i18n("Missing Identifier" )); |
139 | return false; |
140 | } |
141 | return true; |
142 | } |
143 | |
144 | void MigratorBase::start() |
145 | { |
146 | if (mMigrationState == InProgress) { |
147 | kWarning() << "already running" ; |
148 | return; |
149 | } |
150 | if (!canStart()) { |
151 | emit message(Error, i18n("Failed to start migration because migrator is not ready" )); |
152 | emit stoppedProcessing(); |
153 | return; |
154 | } |
155 | //TODO acquire dbus lock |
156 | logMessage(Info, displayName()); |
157 | emit message(Info, i18n("Starting migration..." )); |
158 | setMigrationState(InProgress); |
159 | setProgress(0); |
160 | startWork(); |
161 | } |
162 | |
163 | void MigratorBase::pause() |
164 | { |
165 | kWarning() << "pause is not implemented" ; |
166 | } |
167 | |
168 | void MigratorBase::resume() |
169 | { |
170 | kWarning() << "resume is not implemented" ; |
171 | } |
172 | |
173 | void MigratorBase::abort() |
174 | { |
175 | kWarning() << "abort is not implemented" ; |
176 | } |
177 | |
178 | void MigratorBase::logMessage(MigratorBase::MessageType type, const QString &msg) |
179 | { |
180 | if (mLogFile) { |
181 | mLogFile->write(QString(QLatin1Char('[') + QDateTime::currentDateTime().toString() + QLatin1String("] " ) |
182 | + messageTypeToString(type) + QLatin1String(": " ) + msg + QLatin1Char('\n')).toUtf8()); |
183 | mLogFile->flush(); |
184 | } |
185 | } |
186 | |
187 | bool MigratorBase::shouldAutostart() const |
188 | { |
189 | return false; |
190 | } |
191 | |
192 | void MigratorBase::setMigrationState(MigratorBase::MigrationState state) |
193 | { |
194 | mMigrationState = state; |
195 | switch (state) { |
196 | case Complete: |
197 | setProgress(100); |
198 | emit message(Success, i18n("Migration complete" )); |
199 | emit stoppedProcessing(); |
200 | break; |
201 | case Aborted: |
202 | emit message(Skip, i18n("Migration aborted" )); |
203 | emit stoppedProcessing(); |
204 | break; |
205 | case InProgress: |
206 | break; |
207 | case Failed: |
208 | emit message(Error, i18n("Migration failed" )); |
209 | emit stoppedProcessing(); |
210 | break; |
211 | case Paused: |
212 | emit message(Info, i18n("Migration paused" )); |
213 | emit stateChanged(mMigrationState); |
214 | return; |
215 | default: |
216 | kWarning() << "invalid state " << state; |
217 | Q_ASSERT(false); |
218 | return; |
219 | } |
220 | saveState(); |
221 | emit stateChanged(mMigrationState); |
222 | } |
223 | |
224 | MigratorBase::MigrationState MigratorBase::migrationState() const |
225 | { |
226 | return mMigrationState; |
227 | } |
228 | |
229 | void MigratorBase::saveState() |
230 | { |
231 | config().writeEntry(QLatin1String("MigrationState" ), stateToIdentifier(mMigrationState)); |
232 | } |
233 | |
234 | void MigratorBase::loadState() |
235 | { |
236 | const QString state = config().readEntry(QLatin1String("MigrationState" ), QString()); |
237 | if (!state.isEmpty()) { |
238 | mMigrationState = identifierToState(state); |
239 | } |
240 | |
241 | if (mMigrationState == InProgress) { |
242 | emit message(Warning, i18n("This migration has already been started once but was aborted" )); |
243 | mMigrationState = NeedsUpdate; |
244 | } |
245 | switch (mMigrationState) { |
246 | case Complete: |
247 | mProgress = 100; |
248 | break; |
249 | default: |
250 | mProgress = 0; |
251 | } |
252 | } |
253 | |
254 | NullableConfigGroup MigratorBase::config() |
255 | { |
256 | if (mConfig) { |
257 | return NullableConfigGroup(mConfig->group(mIdentifier)); |
258 | } |
259 | return NullableConfigGroup(); |
260 | } |
261 | |
262 | int MigratorBase::progress() const |
263 | { |
264 | return mProgress; |
265 | } |
266 | |
267 | void MigratorBase::setProgress(int prog) |
268 | { |
269 | if (mProgress != prog) { |
270 | mProgress = prog; |
271 | emit progress(prog); |
272 | } |
273 | } |
274 | |
275 | QString MigratorBase::status() const |
276 | { |
277 | switch (mMigrationState) { |
278 | case None: return i18nc("@info:status" , "Not started" ); |
279 | case InProgress: return i18nc("@info:status" , "Running..." ); |
280 | case Complete: return i18nc("@info:status" , "Complete" ); |
281 | case Aborted: return i18nc("@info:status" , "Aborted" ); |
282 | case Paused: return i18nc("@info:status" , "Paused" ); |
283 | case NeedsUpdate: return i18nc("@info:status" , "Needs Update" ); |
284 | case Failed: return i18nc("@info:status" , "Failed" ); |
285 | } |
286 | return QString(); |
287 | } |
288 | |
289 | |