void Listener::checkConnections() { if (m_connections.isEmpty()) { m_server->close(); emit noClients(); } }
Listener::Listener(const QString &resourceName, QObject *parent) : QObject(parent), m_server(new QLocalServer(this)), m_resourceName(resourceName), m_resource(0), m_pipeline(new Akonadi2::Pipeline(resourceName, parent)), m_clientBufferProcessesTimer(new QTimer(this)), m_messageId(0) { connect(m_pipeline, &Akonadi2::Pipeline::revisionUpdated, this, &Listener::refreshRevision); connect(m_server, &QLocalServer::newConnection, this, &Listener::acceptConnection); log(QString("Trying to open %1").arg(resourceName)); if (!m_server->listen(resourceName)) { // FIXME: multiple starts need to be handled here m_server->removeServer(resourceName); if (!m_server->listen(resourceName)) { log("Utter failure to start server"); exit(-1); } } if (m_server->isListening()) { log(QString("Listening on %1").arg(m_server->serverName())); } m_checkConnectionsTimer = new QTimer; m_checkConnectionsTimer->setSingleShot(true); m_checkConnectionsTimer->setInterval(1000); connect(m_checkConnectionsTimer, &QTimer::timeout, [this]() { if (m_connections.isEmpty()) { log(QString("No connections, shutting down.")); m_server->close(); emit noClients(); } }); //TODO: experiment with different timeouts // or even just drop down to invoking the method queued? => invoke queued unless we need throttling m_clientBufferProcessesTimer->setInterval(0); m_clientBufferProcessesTimer->setSingleShot(true); connect(m_clientBufferProcessesTimer, &QTimer::timeout, this, &Listener::processClientBuffers); }
void Listener::processCommand(int commandId, uint messageId, Client &client, uint size, const std::function<void()> &callback) { switch (commandId) { case Akonadi2::Commands::HandshakeCommand: { flatbuffers::Verifier verifier((const uint8_t *)client.commandBuffer.constData(), size); if (Akonadi2::VerifyHandshakeBuffer(verifier)) { auto buffer = Akonadi2::GetHandshake(client.commandBuffer.constData()); client.name = buffer->name()->c_str(); sendCurrentRevision(client); } else { qWarning() << "received invalid command"; } break; } case Akonadi2::Commands::SynchronizeCommand: { flatbuffers::Verifier verifier((const uint8_t *)client.commandBuffer.constData(), size); if (Akonadi2::VerifySynchronizeBuffer(verifier)) { auto buffer = Akonadi2::GetSynchronize(client.commandBuffer.constData()); log(QString("\tSynchronize request (id %1) from %2").arg(messageId).arg(client.name)); loadResource(); if (!m_resource) { qWarning() << "No resource loaded"; break; } //TODO a more elegant composition of jobs should be possible if (buffer->sourceSync()) { bool localSync = buffer->localSync(); m_resource->synchronizeWithSource(m_pipeline).then<void>([callback, localSync, this](Async::Future<void> &f){ if (localSync) { m_resource->processAllMessages().then<void>([callback](Async::Future<void> &f){ callback(); f.setFinished(); }).exec(); } else { callback(); f.setFinished(); } }).exec(); } else if (buffer->localSync()) { m_resource->processAllMessages().then<void>([callback](Async::Future<void> &f){ callback(); f.setFinished(); }).exec(); } return; } else { qWarning() << "received invalid command"; } break; } case Akonadi2::Commands::FetchEntityCommand: case Akonadi2::Commands::DeleteEntityCommand: case Akonadi2::Commands::ModifyEntityCommand: case Akonadi2::Commands::CreateEntityCommand: log(QString("\tCommand id %1 of type %2 from %3").arg(messageId).arg(commandId).arg(client.name)); loadResource(); if (m_resource) { m_resource->processCommand(commandId, client.commandBuffer, size, m_pipeline); } break; case Akonadi2::Commands::ShutdownCommand: log(QString("\tReceived shutdown command from %1").arg(client.name)); callback(); m_server->close(); emit noClients(); return; default: if (commandId > Akonadi2::Commands::CustomCommand) { loadResource(); if (m_resource) { m_resource->processCommand(commandId, client.commandBuffer, size, m_pipeline); } } else { //TODO: handle error: we don't know wtf this command is } break; } callback(); }