LML has 3 "services": RequestHandler, RenameHandler and DirectoryWatcher.
Now they have a common base class and a manager.
#include <LocalMyList/MyList>
#include <LocalMyList/Settings>
+#include <LocalMyList/ServiceManager>
+#include <LocalMyList/Service>
#include <LocalMyList/DirectoryWatcher>
#ifndef LOCALMYLIST_NO_ANIDBUDPCLIENT
# include <LocalMyList/RequestHandler>
connect(AniDBUdpClient::Client::instance(), SIGNAL(connectionError()), this, SLOT(handleUdpClientError()));
LocalMyList::instance()->setupRequestHandler();
- LocalMyList::instance()->requestHandler()->handleRequests();
- LocalMyList::instance()->requestHandler()->handleMyListUpdates();
+ LocalMyList::instance()->serviceManager()->processRequests("RequestHandler");
#else
log(QString("AniOni was configured to run the UDP client, but LocalMyList is not compiled with the UDP client."
" Recompile LocalMyList with AniDBUdpClient"), QtServiceBase::Error);
if (watchDirectories)
{
LocalMyList::instance()->setupDirectoryWatcher();
- LocalMyList::instance()->directoryWatcher()->checkWatchedDirectories();
+ LocalMyList::instance()->serviceManager()->processRequests("DirectoryWatcher");
}
if (!(runUdpClient || runRenameHandler || watchDirectories))
{
- log(QString("AniOni was configured to do nothing. Check settings"), QtServiceBase::Error);
+ log(QString("AniOni was configured to do nothing. Check settings."), QtServiceBase::Error);
application()->exit(1);
return;
}
namespace LocalMyList {
DirectoryWatcher::DirectoryWatcher(Database *db, Settings *settings, QObject *parent) :
- QObject(parent)
+ Service("DirectoryWatcher", parent)
{
this->db = db;
this->settings = settings;
-
- watcher = new QFileSystemWatcher();
- connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watchedDirectoryChanged(QString)), Qt::QueuedConnection);
-
- setWatchedDirectories();
+ watcher = 0;
}
DirectoryWatcher::~DirectoryWatcher()
watchedDirectoryChanged(dir);
}
+void DirectoryWatcher::doInit()
+{
+ watcher = new QFileSystemWatcher();
+}
+
+void DirectoryWatcher::doDeInit()
+{
+ delete watcher;
+ watcher = 0;
+}
+
+bool DirectoryWatcher::doStart()
+{
+ connect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watchedDirectoryChanged(QString)), Qt::QueuedConnection);
+
+ setWatchedDirectories();
+ return true;
+}
+
+void LocalMyList::DirectoryWatcher::doStop()
+{
+ disconnect(watcher, SIGNAL(directoryChanged(QString)), this, SLOT(watchedDirectoryChanged(QString)));
+}
+
+void DirectoryWatcher::doProcessRequests()
+{
+ checkWatchedDirectories();
+}
+
} // namespace LocalMyList
#define DIRECTORYWATCHER_H
#include "localmylist_global.h"
-#include <QObject>
+#include "service.h"
#include <QMap>
#include <QSet>
class Database;
class Settings;
-class LOCALMYLISTSHARED_EXPORT DirectoryWatcher : public QObject
+class LOCALMYLISTSHARED_EXPORT DirectoryWatcher : public Service
{
Q_OBJECT
public:
void checkWatchedDirectories();
+protected:
+ void doInit();
+ void doDeInit();
+ bool doStart();
+ void doStop();
+ void doProcessRequests();
+
private:
Database *db;
Settings *settings;
--- /dev/null
+#include "../../service.h"
+
--- /dev/null
+#include "../../servicemanager.h"
+
filelocationchecktask.cpp \
messagehandler.cpp \
asyncquerytask.cpp \
+ service.cpp \
+ servicemanager.cpp \
dynamicmodel/data.cpp \
dynamicmodel/node.cpp \
dynamicmodel/model.cpp \
dynamicmodel/datatype.cpp \
dynamicmodel/types.cpp \
dynamicmodel/datamodel.cpp \
- dynamicmodel/typerelation.cpp
+ dynamicmodel/typerelation.cpp
HEADERS += \
localmylist_global.h \
asyncquerytask.h \
filelocationchecktask.h \
sqlresultiteratorinterface.h \
+ service.h \
+ servicemanager.h \
dynamicmodel/data.h \
dynamicmodel/node.h \
dynamicmodel/model.h \
dynamicmodel/dynamicmodel_global.h \
dynamicmodel/types.h \
dynamicmodel/datamodel.h \
- dynamicmodel/typerelation.h
+ dynamicmodel/typerelation.h
CONV_HEADERS += \
include/LocalMyList/AbstractTask \
include/LocalMyList/MyListExportParseTask \
include/LocalMyList/MyListModel \
include/LocalMyList/MyListNode \
+ include/LocalMyList/Service \
+ include/LocalMyList/ServiceManager \
include/LocalMyList/Settings \
include/LocalMyList/UnknownFileLookupTask \
include/LocalMyList/UnknownFileLookupTask \
#include "asyncquerytask.h"
#include "workthread.h"
#include "messagehandler.h"
+#include "servicemanager.h"
+#include "service.h"
#ifndef LOCALMYLIST_NO_ANIDBUDPCLIENT
# include "requesthandler.h"
# include "renamehandler.h"
{
init();
- m_requestHandler = 0;
- m_renameHandler = 0;
- m_directoryWatcher = 0;
workThread = 0;
m_defaultLocalQSettings = new QSettings(QSettings::IniFormat, QSettings::UserScope, organizationName, libraryName, this);
db = new Database();
connect(db, SIGNAL(connected()), this, SLOT(setupHostInfo()));
m_settings = new Settings(db, this);
+ m_serviceManager = new ServiceManager(this);
m_udpClientId = 0;
}
return m_settings;
}
-RequestHandler *MyList::requestHandler() const
+ServiceManager *MyList::serviceManager() const
{
- return m_requestHandler;
-}
-
-DirectoryWatcher *MyList::directoryWatcher() const
-{
- return m_directoryWatcher;
+ return m_serviceManager;
}
// -------
void MyList::setupRequestHandler()
{
#ifndef LOCALMYLIST_NO_ANIDBUDPCLIENT
- if (m_requestHandler || !db->isConnected() || !udpClientId())
+ if (m_serviceManager->hasService("RequestHandler") || !db->isConnected() || !udpClientId())
return;
- m_requestHandler = new RequestHandler(db, this);
- connect(db, SIGNAL(newPendingRequest()), m_requestHandler, SLOT(handleRequests()));
- connect(db, SIGNAL(newPendingMyListUpdate()), m_requestHandler, SLOT(handleMyListUpdates()));
+ Service *service = new RequestHandler(db, this);
+ m_serviceManager->addService(service);
+ service->start();
#endif
}
void MyList::setupRenameHandler()
{
#ifndef LOCALMYLIST_NO_ANIDBUDPCLIENT
- if (m_renameHandler || !db->isConnected())
+ if (m_serviceManager->hasService("RenameHandler") || !db->isConnected())
return;
setupWorkThread();
- m_renameHandler = new RenameHandler(workThread->database(), workThread->settings());
- m_renameHandler->moveToThread(workThread);
+ Service *service = new RenameHandler(workThread->database(), workThread->settings());
+ service->moveToThread(workThread);
+ m_serviceManager->addService(service);
+ service->start();
#endif
}
void MyList::setupDirectoryWatcher()
{
- if (m_directoryWatcher || !db->isConnected())
+ if (m_serviceManager->hasService("DirectoryWatcher") || !db->isConnected())
return;
- m_directoryWatcher = new DirectoryWatcher(db, m_settings);
+ Service *service = new DirectoryWatcher(db, m_settings);
+ m_serviceManager->addService(service);
+ service->start();
}
void MyList::setupWorkThread()
class Settings;
class AbstractTask;
class WorkThread;
+class ServiceManager;
class RequestHandler;
class RenameHandler;
class DirectoryWatcher;
Q_OBJECT
Q_PROPERTY(LocalMyList::Database *database READ database)
Q_PROPERTY(LocalMyList::Settings *settings READ settings)
- Q_PROPERTY(LocalMyList::RequestHandler *requestHandler READ requestHandler)
+ Q_PROPERTY(LocalMyList::ServiceManager *serviceManager READ serviceManager)
Q_PROPERTY(QString hostName READ hostName WRITE setHostName)
Q_PROPERTY(int hostId READ hostId)
Q_PROPERTY(bool isUdpHost READ isUdpHost)
LocalMyList::Database *database() const;
LocalMyList::Settings *settings() const;
- LocalMyList::RequestHandler *requestHandler() const;
- LocalMyList::DirectoryWatcher *directoryWatcher() const;
+ LocalMyList::ServiceManager *serviceManager() const;
QString hostName() const;
void setHostName(QString name);
DatabaseConnectionSettings dbs;
Database *db;
WorkThread *workThread;
- RequestHandler *m_requestHandler;
- RenameHandler *m_renameHandler;
- DirectoryWatcher *m_directoryWatcher;
+ ServiceManager *m_serviceManager;
Settings *m_settings;
HostInfo hostInfo;
namespace LocalMyList {
RenameHandler::RenameHandler(Database *db, Settings *settings, QObject *parent) :
- QObject(parent), renameEngine(0), validScript(false)
+ Service("RenameHandler", parent), renameEngine(0), validScript(false)
{
this->db = db;
this->settings = settings;
- connect(db, SIGNAL(renameDataChanged()), this, SLOT(handleRename()), Qt::QueuedConnection);
- connect(this, SIGNAL(renameBatchFinished()), this, SLOT(handleRename()), Qt::QueuedConnection);
- connect(settings, SIGNAL(settingsChanged()), this, SLOT(clearSetup()));
- connect(db, SIGNAL(reconnected()), this, SLOT(handleRename()), Qt::QueuedConnection);
m_setup = false;
}
{
RaiiTransaction t(db);
- QSqlQuery &q = db->prepare(
- "SELECT * "
- "FROM rename_data "
- "WHERE host_id = :host_id "
- "AND renamed IS NULL AND failed_rename = false "
- "LIMIT :limit");
+ QSqlQuery &q = db->prepare(R"(
+ SELECT *
+ FROM rename_data
+ WHERE host_id = :host_id
+ AND renamed IS NULL AND failed_rename = false
+ LIMIT :limit
+ )");
q.bindValue(":host_id", MyList::instance()->hostId());
q.bindValue(":limit", 1);
m_setup = true;
}
+bool RenameHandler::doStart()
+{
+ connect(db, SIGNAL(renameDataChanged()), this, SLOT(handleRename()), Qt::QueuedConnection);
+ connect(this, SIGNAL(renameBatchFinished()), this, SLOT(handleRename()), Qt::QueuedConnection);
+ connect(settings, SIGNAL(settingsChanged()), this, SLOT(clearSetup()));
+ connect(db, SIGNAL(reconnected()), this, SLOT(handleRename()), Qt::QueuedConnection);
+ return true;
+}
+
+void RenameHandler::doStop()
+{
+ disconnect(db, SIGNAL(renameDataChanged()), this, SLOT(handleRename()));
+ disconnect(this, SIGNAL(renameBatchFinished()), this, SLOT(handleRename()));
+ disconnect(settings, SIGNAL(settingsChanged()), this, SLOT(clearSetup()));
+ disconnect(db, SIGNAL(reconnected()), this, SLOT(handleRename()));
+}
+
+void RenameHandler::doProcessRequests()
+{
+ handleRename();
+}
+
} // namespace LocalMyList
#define RENAMEHANDLER_H
#include "localmylist_global.h"
-#include <QObject>
+#include "service.h"
namespace RenameParser {
class RenameEngine;
class Database;
class Settings;
-class RenameHandler : public QObject
+class RenameHandler : public Service
{
Q_OBJECT
public:
explicit RenameHandler(Database *db, Settings *settings, QObject *parent = 0);
-
+
bool isSetup();
signals:
void clearSetup();
void setupRenameEngine();
+protected:
+ bool doStart();
+ void doStop();
+ void doProcessRequests();
+
private:
Database *db;
Settings *settings;
namespace LocalMyList {
RequestHandler::RequestHandler(Database *db, QObject *parent) :
- QObject(parent)
+ Service("RequestHandler", parent)
{
this->db = db;
- connect(this, SIGNAL(batchFinished()), this, SLOT(handleRequests()), Qt::QueuedConnection);
- connect(this, SIGNAL(myListUpdateBatchFinished()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection);
- connect(db, SIGNAL(reconnected()), this, SLOT(handleRequests()), Qt::QueuedConnection);
- connect(db, SIGNAL(reconnected()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection);
-
- db->failPendingRequestsFromOldClients();
}
void RequestHandler::handleRequests()
emit myListUpdateBatchFinished();
}
+bool RequestHandler::doStart()
+{
+ connect(this, SIGNAL(batchFinished()), this, SLOT(handleRequests()), Qt::QueuedConnection);
+ connect(this, SIGNAL(myListUpdateBatchFinished()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection);
+ connect(db, SIGNAL(reconnected()), this, SLOT(handleRequests()), Qt::QueuedConnection);
+ connect(db, SIGNAL(reconnected()), this, SLOT(handleMyListUpdates()), Qt::QueuedConnection);
+
+
+ connect(db, SIGNAL(newPendingRequest()), this, SLOT(handleRequests()));
+ connect(db, SIGNAL(newPendingMyListUpdate()), this, SLOT(handleMyListUpdates()));
+
+ db->failPendingRequestsFromOldClients();
+ return true;
+}
+
+void RequestHandler::doStop()
+{
+ disconnect(this, SIGNAL(batchFinished()), this, SLOT(handleRequests()));
+ disconnect(this, SIGNAL(myListUpdateBatchFinished()), this, SLOT(handleMyListUpdates()));
+ disconnect(db, SIGNAL(reconnected()), this, SLOT(handleRequests()));
+ disconnect(db, SIGNAL(reconnected()), this, SLOT(handleMyListUpdates()));
+
+ disconnect(db, SIGNAL(newPendingRequest()), this, SLOT(handleRequests()));
+ disconnect(db, SIGNAL(newPendingMyListUpdate()), this, SLOT(handleMyListUpdates()));
+}
+
+void RequestHandler::doProcessRequests()
+{
+ handleRequests();
+ handleMyListUpdates();
+}
+
void RequestHandler::animeRequestComplete(bool success)
{
using namespace ::AniDBUdpClient;
#define REQUESTHANDLER_H
#include "localmylist_global.h"
+#include "service.h"
#include <QObject>
#include <QMetaType>
#include <QMap>
class Database;
-class LOCALMYLISTSHARED_EXPORT RequestHandler : public QObject
+class LOCALMYLISTSHARED_EXPORT RequestHandler : public Service
{
Q_OBJECT
public:
void handleRequests();
void handleMyListUpdates();
+protected:
+ bool doStart();
+ void doStop();
+ void doProcessRequests();
+
private slots:
void animeRequestComplete(bool success);
void episodeRequestComplete(bool success);
void myListAddReplyComplete(bool success);
void myListEditReplyComplete(bool success);
void myListUpdateVoteReplyComplete(bool success);
+
private:
Database *db;
--- /dev/null
+#include "service.h"
+
+#include <QDebug>
+#include "mylist.h"
+
+namespace LocalMyList {
+
+Service::Service(const QString &name, QObject *parent) :
+ QObject{parent}, m_state{Stopped}, m_name{name}
+{
+}
+
+Service::~Service()
+{
+ stop();
+ deinitialize();
+}
+
+Service::ServiceState Service::state() const
+{
+ return m_state;
+}
+
+
+QString Service::name() const
+{
+ return m_name;
+}
+
+void Service::start()
+{
+ if (m_state == Running)
+ return;
+ if (m_state == Uninitialized)
+ {
+ initAndStart();
+ return;
+ }
+ if (m_state != Stopped || m_state != Error)
+ return;
+ tryStart();
+}
+
+void Service::stop()
+{
+ if (m_state != Running)
+ return;
+
+ qDebug() << "Stopping service" << name();
+ doStop();
+ setState(Stopped);
+}
+
+void Service::deinitialize()
+{
+ if (m_state != Stopped || m_state != Error)
+ return;
+ doDeInit();
+ setState(Uninitialized);
+}
+
+void Service::processRequests()
+{
+ if (m_state != Running)
+ return;
+
+ doProcessRequests();
+}
+
+void Service::setState(Service::ServiceState state)
+{
+ if (m_state == state)
+ return;
+ m_state = state;
+ emit stateChanged(state);
+}
+
+void Service::initAndStart()
+{
+ doInit();
+ setState(Stopped);
+ tryStart();
+}
+
+void Service::tryStart()
+{
+ qDebug() << "Starting service" << name();
+ if (doStart())
+ {
+ setState(Running);
+ return;
+ }
+ MyList::instance()->database()->log(QString("Service %1 failed to start!").arg(name()));
+ setState(Error);
+}
+
+
+} // namespace LocalMyList
--- /dev/null
+#ifndef SERVICE_H
+#define SERVICE_H
+
+#include "localmylist_global.h"
+#include <QObject>
+
+namespace LocalMyList {
+
+class LOCALMYLISTSHARED_EXPORT Service : public QObject
+{
+ Q_OBJECT
+ Q_ENUMS(ServiceState)
+ Q_PROPERTY(ServiceState state READ state NOTIFY stateChanged)
+ Q_PROPERTY(QString name READ name)
+
+public:
+ ~Service();
+
+ enum ServiceState
+ {
+ Uninitialized, //<! Uninitialized
+ Stopped, //<! Initialized
+ Running, //<! Service is running
+ Error, //<! Service failed to start
+ Invalid = -1, //<! This value represents the state of an invalid/nonexistant service. A service can't be in this state.
+ };
+
+ ServiceState state() const;
+ QString name() const;
+
+public slots:
+ /*!
+ * \brief start Starts the service if it isn't started.
+ *
+ * Initializes if necessary.
+ * To check if the service started correctly connect to the stateChanged signal
+ */
+ void start();
+
+ //! Stops the service if it is running
+ void stop();
+
+ //! Stops the service if it is stopped
+ void deinitialize();
+
+ //! Forces the service to check for and process any requests.
+ void processRequests();
+
+signals:
+ void stateChanged(ServiceState state);
+
+protected:
+ Service(const QString &name, QObject *parent = 0);
+
+ /*!
+ * \brief initialize any resourcesneeded by the service
+ * Called only when needed. Do not call yourself.
+ */
+ virtual void doInit() {}
+ /*!
+ * \brief doDeInit cleanup any resources
+ * Called only when needed. Do not call yourself.
+ */
+ virtual void doDeInit() {}
+ virtual bool doStart() = 0;
+ virtual void doStop() = 0;
+
+ /*!
+ * \brief Check and process any pending requests
+ * Called only when service is running.
+ */
+ virtual void doProcessRequests() {}
+
+private slots:
+ void initAndStart();
+
+private:
+ void setState(ServiceState state);
+ void tryStart();
+
+ ServiceState m_state;
+ QString m_name;
+};
+
+} // namespace LocalMyList
+
+#endif // SERVICE_H
--- /dev/null
+#include "servicemanager.h"
+
+#include "service.h"
+
+namespace LocalMyList {
+
+ServiceManager::ServiceManager(QObject *parent) :
+ QObject(parent)
+{
+ // qRegisterMetaType
+}
+
+ServiceManager::~ServiceManager()
+{
+ for (Service *s : m_registeredServices)
+ callServiceSlot(s, SLOT(deleteLater()));
+}
+
+bool ServiceManager::addService(Service *service)
+{
+ if (m_registeredServices.contains(service->name()))
+ return false;
+
+ m_registeredServices.insert(service->name(), service);
+ connect(service, SIGNAL(stateChanged(Service::ServiceState)), this, SLOT(serviceStateChanged(Service::ServiceState)));
+ return true;
+}
+
+bool ServiceManager::hasService(const QString &serviceName) const
+{
+ return m_registeredServices.contains(serviceName);
+}
+
+void ServiceManager::start(const QString &serviceName)
+{
+ auto it = m_registeredServices.find(serviceName);
+ if (it == m_registeredServices.end())
+ return;
+
+ callServiceSlot(it.value(), "start");
+}
+
+void ServiceManager::startAll()
+{
+ for (Service *s : m_registeredServices)
+ callServiceSlot(s, "start");
+}
+
+void ServiceManager::stop(const QString &serviceName)
+{
+ auto it = m_registeredServices.find(serviceName);
+ if (it == m_registeredServices.end())
+ return;
+
+ callServiceSlot(it.value(), "stop");
+}
+
+void ServiceManager::stopAll()
+{
+ for (Service *s : m_registeredServices)
+ callServiceSlot(s, "stop");
+}
+
+void ServiceManager::processRequests(const QString &serviceName)
+{
+ auto it = m_registeredServices.find(serviceName);
+ if (it == m_registeredServices.end())
+ return;
+
+ callServiceSlot(it.value(), "processRequests");
+}
+
+void ServiceManager::processRequestsAll()
+{
+ for (Service *s : m_registeredServices)
+ callServiceSlot(s, "processRequests");
+}
+
+Service::ServiceState ServiceManager::state(const QString &serviceName)
+{
+ auto it = m_registeredServices.find(serviceName);
+ if (it == m_registeredServices.end())
+ return Service::Invalid;
+ return it.value()->state();
+}
+
+QList<QString> ServiceManager::registeredServices() const
+{
+ return m_registeredServices.keys();
+}
+
+void ServiceManager::serviceStateChanged(Service::ServiceState state)
+{
+ Q_ASSERT(qobject_cast<Service *>(sender()));
+ emit stateChanged(static_cast<Service *>(sender())->name(), state);
+}
+
+void ServiceManager::callServiceSlot(Service *s, const char *slot) const
+{
+ QMetaObject::invokeMethod(s, slot, Qt::AutoConnection);
+}
+
+} // namespace LocalMyList
--- /dev/null
+#ifndef SERVICEMANAGER_H
+#define SERVICEMANAGER_H
+
+#include "localmylist_global.h"
+#include <QObject>
+#include <QString>
+#include <QMap>
+
+#include "service.h"
+
+namespace LocalMyList {
+
+class LOCALMYLISTSHARED_EXPORT ServiceManager : public QObject
+{
+ Q_OBJECT
+
+public:
+ explicit ServiceManager(QObject *parent = 0);
+ ~ServiceManager();
+
+ /*!
+ * \brief addService Adds service to be managed by the ServiceMAnager.
+ * Takes ownershit of the passed service.
+ *
+ * \param service The service to add
+ * \return Returns true on success.
+ */
+ bool addService(Service *service);
+
+signals:
+ void stateChanged(const QString &serviceName, Service::ServiceState state);
+
+public slots:
+ void start(const QString &serviceName);
+ void startAll();
+ void stop(const QString &serviceName);
+ void stopAll();
+ void processRequests(const QString &serviceName);
+ void processRequestsAll();
+
+ Service::ServiceState state(const QString &serviceName);
+
+ bool hasService(const QString &serviceName) const;
+
+ /*!
+ * \brief registeredServices return the names of registered services.
+ * \return list of names of registered services.
+ */
+ QList<QString> registeredServices() const;
+
+private slots:
+ void serviceStateChanged(Service::ServiceState state);
+
+private:
+ void callServiceSlot(Service *s, const char *slot) const;
+ QMap<QString, Service*> m_registeredServices;
+};
+
+} // namespace LocalMyList
+
+#endif // SERVICEMANAGER_H