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
31static 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
45static 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
55static QMap<QString, MigratorBase::MigrationState> migrationStateMapping = fillMigrationStateMapping();
56
57static QString stateToIdentifier(MigratorBase::MigrationState state)
58{
59 Q_ASSERT(migrationStateMapping.values().contains(state));
60 return migrationStateMapping.key(state);
61}
62
63static MigratorBase::MigrationState identifierToState(const QString &identifier)
64{
65 Q_ASSERT(migrationStateMapping.contains(identifier));
66 return migrationStateMapping.value(identifier);
67}
68
69MigratorBase::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
81MigratorBase::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
94MigratorBase::~MigratorBase()
95{
96
97}
98
99void 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
112QString MigratorBase::identifier() const
113{
114 return mIdentifier;
115}
116
117QString MigratorBase::displayName() const
118{
119 return QString();
120}
121
122QString MigratorBase::description() const
123{
124 return QString();
125}
126
127QString MigratorBase::logfile() const
128{
129 if (mLogFile) {
130 return mLogFile->fileName();
131 }
132 return QString();
133}
134
135bool MigratorBase::canStart()
136{
137 if (mIdentifier.isEmpty()) {
138 emit message(Error, i18n("Missing Identifier"));
139 return false;
140 }
141 return true;
142}
143
144void 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
163void MigratorBase::pause()
164{
165 kWarning() << "pause is not implemented";
166}
167
168void MigratorBase::resume()
169{
170 kWarning() << "resume is not implemented";
171}
172
173void MigratorBase::abort()
174{
175 kWarning() << "abort is not implemented";
176}
177
178void 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
187bool MigratorBase::shouldAutostart() const
188{
189 return false;
190}
191
192void 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
224MigratorBase::MigrationState MigratorBase::migrationState() const
225{
226 return mMigrationState;
227}
228
229void MigratorBase::saveState()
230{
231 config().writeEntry(QLatin1String("MigrationState"), stateToIdentifier(mMigrationState));
232}
233
234void 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
254NullableConfigGroup MigratorBase::config()
255{
256 if (mConfig) {
257 return NullableConfigGroup(mConfig->group(mIdentifier));
258 }
259 return NullableConfigGroup();
260}
261
262int MigratorBase::progress() const
263{
264 return mProgress;
265}
266
267void MigratorBase::setProgress(int prog)
268{
269 if (mProgress != prog) {
270 mProgress = prog;
271 emit progress(prog);
272 }
273}
274
275QString 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