summaryrefslogtreecommitdiffstats
path: root/common/resourceaccess.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'common/resourceaccess.cpp')
-rw-r--r--common/resourceaccess.cpp181
1 files changed, 181 insertions, 0 deletions
diff --git a/common/resourceaccess.cpp b/common/resourceaccess.cpp
new file mode 100644
index 0000000..2b58545
--- /dev/null
+++ b/common/resourceaccess.cpp
@@ -0,0 +1,181 @@
1/*
2 * Copyright (C) 2014 Aaron Seigo <aseigo@kde.org>
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) version 3, or any
8 * later version accepted by the membership of KDE e.V. (or its
9 * successor approved by the membership of KDE e.V.), which shall
10 * act as a proxy defined in Section 6 of version 3 of the license.
11 *
12 * This library 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 GNU
15 * Lesser General Public License for more details.
16 *
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21#include "resourceaccess.h"
22
23#include "common/console.h"
24#include "common/commands.h"
25#include "common/handshake_generated.h"
26#include "common/revisionupdate_generated.h"
27
28#include <QDebug>
29#include <QProcess>
30
31namespace Akonadi2
32{
33
34ResourceAccess::ResourceAccess(const QString &resourceName, QObject *parent)
35 : QObject(parent),
36 m_resourceName(resourceName),
37 m_socket(new QLocalSocket(this)),
38 m_tryOpenTimer(new QTimer(this)),
39 m_startingProcess(false)
40{
41 m_tryOpenTimer->setInterval(50);
42 m_tryOpenTimer->setSingleShot(true);
43 connect(m_tryOpenTimer, &QTimer::timeout,
44 this, &ResourceAccess::open);
45
46 log("Starting access");
47 connect(m_socket, &QLocalSocket::connected,
48 this, &ResourceAccess::connected);
49 connect(m_socket, &QLocalSocket::disconnected,
50 this, &ResourceAccess::disconnected);
51 connect(m_socket, SIGNAL(error(QLocalSocket::LocalSocketError)),
52 this, SLOT(connectionError(QLocalSocket::LocalSocketError)));
53 connect(m_socket, &QIODevice::readyRead,
54 this, &ResourceAccess::readResourceMessage);
55
56}
57
58ResourceAccess::~ResourceAccess()
59{
60
61}
62
63QString ResourceAccess::resourceName() const
64{
65 return m_resourceName;
66}
67
68bool ResourceAccess::isReady() const
69{
70 return m_socket->isValid();
71}
72
73void ResourceAccess::open()
74{
75 if (m_socket->isValid()) {
76 log("Socket valid, so aborting the open");
77 return;
78 }
79
80 m_socket->setServerName(m_resourceName);
81 log(QString("Opening %1").arg(m_socket->serverName()));
82 //FIXME: race between starting the exec and opening the socket?
83 m_socket->open();
84}
85
86void ResourceAccess::close()
87{
88 log(QString("Closing %1").arg(m_socket->fullServerName()));
89 m_socket->close();
90}
91
92void ResourceAccess::connected()
93{
94 m_startingProcess = false;
95 log(QString("Connected: ").arg(m_socket->fullServerName()));
96
97 {
98 auto name = m_fbb.CreateString(QString::number((long long)this).toLatin1());
99 auto command = Akonadi2::CreateHandshake(m_fbb, name);
100 Akonadi2::FinishHandshakeBuffer(m_fbb, command);
101 Commands::write(m_socket, Commands::HandshakeCommand, m_fbb);
102 m_fbb.Clear();
103 }
104
105 emit ready(true);
106}
107
108void ResourceAccess::disconnected()
109{
110 m_socket->close();
111 log(QString("Disconnected from %1").arg(m_socket->fullServerName()));
112 emit ready(false);
113 open();
114}
115
116void ResourceAccess::connectionError(QLocalSocket::LocalSocketError error)
117{
118 log(QString("Connection error: %2").arg(error));
119 if (m_startingProcess) {
120 if (!m_tryOpenTimer->isActive()) {
121 m_tryOpenTimer->start();
122 }
123 return;
124 }
125
126 m_startingProcess = true;
127 log(QString("Attempting to start resource ") + m_resourceName);
128 QStringList args;
129 args << m_resourceName;
130 if (QProcess::startDetached("akonadi2_synchronizer", args)) {
131 m_socket->open();
132 }
133}
134
135void ResourceAccess::readResourceMessage()
136{
137 if (!m_socket->isValid()) {
138 return;
139 }
140
141 m_partialMessageBuffer += m_socket->readAll();
142
143 // should be scheduled rather than processed all at once
144 while (processMessageBuffer()) {}
145}
146
147bool ResourceAccess::processMessageBuffer()
148{
149 static const int headerSize = (sizeof(int) * 2);
150 if (m_partialMessageBuffer.size() < headerSize) {
151 return false;
152 }
153
154 const int commandId = *(int*)m_partialMessageBuffer.constData();
155 const int size = *(int*)(m_partialMessageBuffer.constData() + sizeof(int));
156
157 if (size > m_partialMessageBuffer.size() - headerSize) {
158 return false;
159 }
160
161 switch (commandId) {
162 case Commands::RevisionUpdateCommand: {
163 auto buffer = Akonadi2::GetRevisionUpdate(m_partialMessageBuffer.constData() + headerSize);
164 log(QString("Revision updated to: %1").arg(buffer->revision()));
165 emit revisionChanged(buffer->revision());
166 break;
167 }
168 default:
169 break;
170 }
171
172 m_partialMessageBuffer.remove(0, headerSize + size);
173 return m_partialMessageBuffer.size() >= headerSize;
174}
175
176void ResourceAccess::log(const QString &message)
177{
178 Console::main()->log(m_resourceName + ": " + message);
179}
180
181}