summaryrefslogtreecommitdiffstats
path: root/src/serviceframework/servicedatabase.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/serviceframework/servicedatabase.cpp')
-rw-r--r--src/serviceframework/servicedatabase.cpp2328
1 files changed, 2328 insertions, 0 deletions
diff --git a/src/serviceframework/servicedatabase.cpp b/src/serviceframework/servicedatabase.cpp
new file mode 100644
index 00000000..d564701f
--- /dev/null
+++ b/src/serviceframework/servicedatabase.cpp
@@ -0,0 +1,2328 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the Qt Mobility Components.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+// #define QT_SFW_SERVICEDATABASE_DEBUG
+
+#include "servicedatabase_p.h"
+#include <QDir>
+#include <QSet>
+#include "qserviceinterfacedescriptor.h"
+#include "qserviceinterfacedescriptor_p.h"
+#include <QUuid>
+#include "dberror_p.h"
+
+//database name
+#define RESOLVERDATABASE "services.db"
+
+//database table names
+#define SERVICE_TABLE "Service"
+#define INTERFACE_TABLE "Interface"
+#define DEFAULTS_TABLE "Defaults"
+#define SERVICE_PROPERTY_TABLE "ServiceProperty"
+#define INTERFACE_PROPERTY_TABLE "InterfaceProperty"
+
+//separator
+#define RESOLVERDATABASE_PATH_SEPARATOR "//"
+
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+#include <QDebug>
+#endif
+
+#define SERVICE_DESCRIPTION_KEY "DESCRIPTION"
+#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+#define SECURITY_TOKEN_KEY "SECURITYTOKEN"
+#endif
+#define INTERFACE_DESCRIPTION_KEY "DESCRIPTION"
+#define SERVICE_INITIALIZED_KEY SERVICE_INITIALIZED_ATTR
+#define INTERFACE_CAPABILITY_KEY "CAPABILITIES"
+#define INTERFACE_SERVICETYPE_KEY "SERVICETYPE"
+
+//service prefixes
+#define SERVICE_IPC_PREFIX "_q_ipc_addr:"
+
+QTM_BEGIN_NAMESPACE
+
+enum TBindIndexes
+ {
+ EBindIndex=0,
+ EBindIndex1,
+ EBindIndex2,
+ EBindIndex3,
+ EBindIndex4,
+ EBindIndex5,
+ EBindIndex6,
+ EBindIndex7
+ };
+
+
+/*
+ \class ServiceDatabase
+ The ServiceDatabase is responsible for the management of a single
+ service database. It provides operations for:
+ - opening and closing a connection with the database,
+ - registering and unregistering services
+ - querying for services and interfaces
+ - setting and getting default interface implementations.
+*/
+
+/*
+ Constructor
+*/
+ServiceDatabase::ServiceDatabase(void)
+:m_isDatabaseOpen(false),m_inTransaction(false)
+{
+}
+
+/*
+ Destructor
+*/
+ServiceDatabase::~ServiceDatabase()
+{
+ close();
+}
+
+/*
+ Opens the service database
+ The method creates or opens database and creates tables if they are not present
+ Returns true if the operation was successful, false if not.
+*/
+bool ServiceDatabase::open()
+{
+ if (m_isDatabaseOpen)
+ return true;
+
+ QString path;
+
+ //Create full path to database
+ if (m_databasePath.isEmpty ())
+ m_databasePath = databasePath();
+
+ path = m_databasePath;
+ QFileInfo dbFileInfo(path);
+ if (!dbFileInfo.dir().exists()) {
+ // Create the path with QFile, avoids problems with S60 3.2/3.1
+ // Avoid security violation with PlatSec
+#ifndef Q_OS_SYMBIAN
+ QDir::root().mkpath(dbFileInfo.path());
+#endif
+ QFile file(path);
+ if (!file.open(QIODevice::ReadWrite)) {
+ QString errorText(QLatin1String("Could not create database directory: %1"));
+ m_lastError.setError(DBError::CannotCreateDbDir, errorText.arg(dbFileInfo.path()));
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::open():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ close();
+ return false;
+ }
+ file.close();
+ }
+
+ m_connectionName = dbFileInfo.completeBaseName();
+ QSqlDatabase database;
+ if (QSqlDatabase::contains(m_connectionName)) {
+ database = QSqlDatabase::database(m_connectionName);
+ } else {
+ database = QSqlDatabase::addDatabase(QLatin1String("QSQLITE"), m_connectionName);
+ database.setDatabaseName(path);
+ }
+
+ if (!database.isValid()){
+ m_lastError.setError(DBError::InvalidDatabaseConnection);
+ close();
+ return false;
+ }
+
+ //Create or open database
+ if (!database.isOpen()) {
+ if (!database.open()) {
+ m_lastError.setError(DBError::SqlError, database.lastError().text());
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::open():-"
+ << "Problem:" << "Could not open database. "
+ << "Reason:" << m_lastError.text();
+#endif
+ close();
+ return false;
+ }
+ }
+ m_isDatabaseOpen = true;
+
+ //Check database structure (tables) and recreate tables if neccessary
+ //If one of tables is missing remove all tables and recreate them
+ //This operation is required in order to avoid data coruption
+ if (!checkTables()) {
+ if (dropTables()) {
+ if (createTables()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qDebug() << "ServiceDatabase::open():-"
+ << "Database tables recreated";
+#endif
+ } else {
+ //createTable() should've handled error message
+ //and warning
+ close();
+ return false;
+ }
+ }
+ else {
+ //dropTables() should've handled error message
+ //and warning
+ close();
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ Adds a \a service into the database.
+
+ May set the following error codes
+ DBError::NoError
+ DBError::LocationAlreadyRegistered
+ DBError::IfaceImplAlreadyRegistered
+ DBError::SqlError
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+//bool ServiceDatabase::registerService(ServiceMetaData &service)
+bool ServiceDatabase::registerService(const ServiceMetaDataResults &service, const QString &securityToken)
+{
+ // Derive the location name with the service type prefix to be stored
+ QString locationPrefix = service.location;
+ int type = service.interfaces[0].d->attributes[QServiceInterfaceDescriptor::ServiceType].toInt();
+ if (type == QService::InterProcess)
+ locationPrefix = SERVICE_IPC_PREFIX + service.location;
+
+#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+ Q_UNUSED(securityToken);
+#else
+ if (securityToken.isEmpty()) {
+ QString errorText("Access denied, no security token provided (for registering service: \"%1\")");
+ m_lastError.setError(DBError::NoWritePermissions, errorText.arg(service.name));
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << "Problem: Unable to register service,"
+ << "reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+#endif
+
+ if (!checkConnection()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ if (!beginTransaction(&query, Write)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << "Unable to begin transaction,"
+ << "reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ //See if the service's location has already been previously registered
+ QString statement(QLatin1String("SELECT Name from Service WHERE Location=? COLLATE NOCASE"));
+ QList<QVariant> bindValues;
+ bindValues.append(locationPrefix);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ if (query.next()) {
+ QString alreadyRegisteredService = query.value(EBindIndex).toString();
+ const QString errorText = QLatin1String("Cannot register service \"%1\". Service location \"%2\" is already "
+ "registered to service \"%3\". \"%3\" must first be deregistered "
+ "for new registration to take place.");
+
+ m_lastError.setError(DBError::LocationAlreadyRegistered,
+ errorText.arg(service.name)
+ .arg(service.location)
+ .arg(alreadyRegisteredService));
+
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+ // If service(s) have already been registered with same name, they must all come from
+ // same application. Fetch a service with given name and if such exists, check that its
+ // security ID equals to the security ID of the current registrar.
+ // One application may register multiple services with same name (different location),
+ // hence the keyword DISTINCT.
+ statement = "SELECT DISTINCT ServiceProperty.Value FROM Service, ServiceProperty "
+ "WHERE Service.ID = ServiceProperty.ServiceID "
+ "AND ServiceProperty.Key = ? "
+ "AND Service.Name = ?";
+ bindValues.clear();
+ bindValues.append(SECURITY_TOKEN_KEY);
+ bindValues.append(service.name);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ QString existingSecurityToken;
+ if (query.next()) {
+ existingSecurityToken = query.value(EBindIndex).toString();
+ }
+ if (!existingSecurityToken.isEmpty() && (existingSecurityToken != securityToken)) {
+ QString errorText("Access denied: \"%1\"");
+ m_lastError.setError(DBError::NoWritePermissions, errorText.arg(service.name));
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << "Problem: Unable to register service,"
+ << "reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+#endif // QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+
+ // Checks done, create new rows into tables.
+ statement = QLatin1String("INSERT INTO Service(ID,Name,Location) VALUES(?,?,?)");
+
+ qsrand(QTime::currentTime().msec());
+ QString serviceID = QUuid::createUuid().toString();
+
+ bindValues.clear();
+ bindValues.append(serviceID);
+ bindValues.append(service.name);
+ bindValues.append(locationPrefix);
+
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ statement = QLatin1String("INSERT INTO ServiceProperty(ServiceID,Key,Value) VALUES(?,?,?)");
+ bindValues.clear();
+ bindValues.append(serviceID);
+ bindValues.append(QLatin1String(SERVICE_DESCRIPTION_KEY));
+ if (service.description.isNull())
+ bindValues.append(QLatin1String(""));
+ else
+ bindValues.append(service.description);
+
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+#ifdef QT_SFW_SERVICEDATABASE_GENERATE
+ statement = "INSERT INTO ServiceProperty(ServiceId,Key,Value) VALUES(?,?,?)";
+ bindValues.clear();
+ bindValues.append(serviceID);
+ bindValues.append(SERVICE_INITIALIZED_KEY);
+ bindValues.append(QString("NO"));
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+#endif
+
+#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+ // Insert a security token for the particular service
+ statement = "INSERT INTO ServiceProperty(ServiceID,Key,Value) VALUES(?,?,?)";
+ bindValues.clear();
+ bindValues.append(serviceID);
+ bindValues.append(SECURITY_TOKEN_KEY);
+ bindValues.append(securityToken);
+
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+#endif // QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+
+ QList <QServiceInterfaceDescriptor> interfaces = service.interfaces;
+ QString interfaceID;;
+ foreach (const QServiceInterfaceDescriptor &interface, interfaces) {
+ interfaceID = getInterfaceID(&query, interface);
+ if (m_lastError.code() == DBError::NoError) {
+ QString errorText;
+ errorText = QLatin1String("Cannot register service \"%1\". \"%1\" is already registered "
+ "and implements interface \"%2\", Version \"%3.%4.\" \"%1\" must "
+ "first be deregistered for new registration to take place.");
+ m_lastError.setError(DBError::IfaceImplAlreadyRegistered,
+ errorText.arg(interface.serviceName())
+ .arg(interface.interfaceName())
+ .arg(interface.majorVersion())
+ .arg(interface.minorVersion()));
+
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ } else if (m_lastError.code() == DBError::NotFound){
+ //No interface implementation already exists for the service
+ //so add it
+ if (!insertInterfaceData(&query, interface, serviceID)) {
+ rollbackTransaction(&query);
+ return false;
+ } else {
+ continue;
+ }
+ } else {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << "Unable to confirm if implementation version"
+ << (QString::number(interface.majorVersion()) + "."
+ + QString::number(interface.minorVersion())).toAscii()
+ << "for interface" << interface.interfaceName()
+ << "is already registered for service "
+ << interface.serviceName()
+ << qPrintable(QString("\n") + m_lastError.text());
+#endif
+ return false;
+ }
+ }
+
+ interfaces = service.latestInterfaces;
+ QServiceInterfaceDescriptor defaultInterface;
+ foreach (const QServiceInterfaceDescriptor &interface, interfaces) {
+ defaultInterface = interfaceDefault(interface.interfaceName(), NULL, true);
+ if (m_lastError.code() == DBError::NoError
+ || m_lastError.code() == DBError::ExternalIfaceIDFound) {
+ continue; //default already exists so don't do anything
+ } else if (m_lastError.code() == DBError::NotFound) {
+ //default does not already exist so create one
+ interfaceID = getInterfaceID(&query, interface);
+ if (m_lastError.code() != DBError::NoError) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << "Unable to retrieve interfaceID for "
+ "interface" << interface.interfaceName()
+ << qPrintable(QString("\n") + m_lastError.text());
+#endif
+ return false;
+ }
+
+ statement = QLatin1String("INSERT INTO Defaults(InterfaceName, InterfaceID) VALUES(?,?)");
+ bindValues.clear();
+ bindValues.append(interface.interfaceName());
+ bindValues.append(interfaceID);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ } else {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::registerService()"
+ << "Problem: Unable to confirm if interface"
+ << interface.interfaceName()
+ << "already has a default implementation";
+#endif
+ return false;
+ }
+ }
+
+ if (!commitTransaction(&query)) {
+ rollbackTransaction(&query);
+ return false;
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Obtains an interface ID corresponding to a given interface \a descriptor
+
+ May set the following error codes:
+ DBError::NoError
+ DBError::NotFound
+ DBError::SqlError
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+*/
+QString ServiceDatabase::getInterfaceID(const QServiceInterfaceDescriptor &descriptor) {
+ QString interfaceID;
+ if (!checkConnection()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::getInterfaceID():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return interfaceID;
+ }
+
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ return getInterfaceID(&query, descriptor);
+}
+
+/*
+ This function should only ever be called on a user scope database.
+ It returns a list of Interface Name and Interface ID pairs, where
+ the Interface ID refers to an external interface implementation
+ in the system scope database.
+
+ May set the last error to:
+ DBError::NoError
+ DBError::SqlError
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+
+ Aside: There is only one query which implicitly gets
+ wrapped in it's own transaction.
+*/
+QList<QPair<QString,QString> > ServiceDatabase::externalDefaultsInfo()
+{
+ QList<QPair<QString,QString> > ret;
+ if (!checkConnection()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::externalDefaultsInfo():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return ret;
+ }
+
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ //Prepare search query, bind criteria values and execute search
+ QString selectComponent = QLatin1String("SELECT InterfaceName, InterfaceID ");
+ QString fromComponent = QLatin1String("FROM Defaults ");
+ QString whereComponent = QLatin1String("WHERE InterfaceID NOT IN (SELECT Interface.ID FROM Interface) ");
+
+ //Aside: this individual query is implicitly wrapped in a transaction
+ if (!executeQuery(&query, selectComponent + fromComponent + whereComponent)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::externalDefaultsInfo():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return ret;
+ }
+
+ while (query.next()) {
+ ret.append(qMakePair(query.value(EBindIndex).toString(),
+ query.value(EBindIndex1).toString()));
+ }
+
+ m_lastError.setError(DBError::NoError);
+ return ret;
+}
+
+/*
+ Helper function that obtains an interfaceID for a given \a descriptor.
+
+ May set last error to one of the following error codes:
+ DBError::NoError
+ DBError::NotFound
+ DBError::SqlError
+
+ Aside: This function may be safely called standalone or within an explicit
+ transaction. If called standalone, it's single query is implicitly
+ wrapped in it's own transaction.
+*/
+QString ServiceDatabase::getInterfaceID(QSqlQuery *query, const QServiceInterfaceDescriptor &interface)
+{
+ QString statement = QLatin1String("SELECT Interface.ID "
+ "FROM Interface, Service "
+ "WHERE Service.ID = Interface.ServiceID "
+ "AND Service.Name = ? COLLATE NOCASE "
+ "AND Interface.Name = ? COLLATE NOCASE "
+ "AND Interface.VerMaj = ? AND Interface.VerMin = ?");
+ QList<QVariant> bindValues;
+ bindValues.append(interface.serviceName());
+ bindValues.append(interface.interfaceName());
+ bindValues.append(interface.majorVersion());
+ bindValues.append(interface.minorVersion());
+
+ if (!executeQuery(query, statement, bindValues)) {
+ return QString();
+ }
+
+ if (!query->next()) {
+ QString errorText(QLatin1String("No Interface Descriptor found with "
+ "Service name: %1 "
+ "Interface name: %2 "
+ "Version: %3.%4"));
+ m_lastError.setError(DBError::NotFound, errorText.arg(interface.serviceName())
+ .arg(interface.interfaceName())
+ .arg(interface.majorVersion())
+ .arg(interface.minorVersion()));
+ return QString();
+ }
+
+ m_lastError.setError(DBError::NoError);
+ return query->value(EBindIndex).toString();
+}
+
+/*
+ Helper functions that saves \a interface related data in the Interface table
+ The \a interface data is recorded as belonging to the service assocciated
+ with \a serviceID.
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+
+ Aside: It is already assumed that a write transaction has been started by the
+ time this function is called; and this function will not rollback/commit
+ the transaction.
+*/
+bool ServiceDatabase::insertInterfaceData(QSqlQuery *query,const QServiceInterfaceDescriptor &interface, const QString &serviceID)
+{
+ QString statement = QLatin1String("INSERT INTO Interface(ID, ServiceID,Name,VerMaj, VerMin) "
+ "VALUES(?,?,?,?,?)");
+ QString interfaceID = QUuid::createUuid();
+
+ QList<QVariant> bindValues;
+ bindValues.append(interfaceID);
+ bindValues.append(serviceID);
+ bindValues.append(interface.interfaceName());
+ bindValues.append(interface.majorVersion());
+ bindValues.append(interface.minorVersion());
+
+ if (!executeQuery(query, statement, bindValues)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::insertInterfaceData():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ statement = QLatin1String("INSERT INTO InterfaceProperty(InterfaceID, Key, Value) VALUES(?,?,?)");
+ QHash<QServiceInterfaceDescriptor::Attribute, QVariant>::const_iterator iter = interface.d->attributes.constBegin();
+ bool isValidInterfaceProperty;
+ QString capabilities;
+ QString interfaceDescription;
+ while (iter != interface.d->attributes.constEnd()) {
+ isValidInterfaceProperty = true;
+
+ bindValues.clear();
+ bindValues.append(interfaceID);
+ switch (iter.key()) {
+ case (QServiceInterfaceDescriptor::Capabilities):
+ bindValues.append(QLatin1String(INTERFACE_CAPABILITY_KEY));
+ capabilities = interface.attribute(QServiceInterfaceDescriptor::Capabilities).toStringList().join(QLatin1String(","));
+ if (capabilities.isNull())
+ capabilities = QLatin1String("");
+ bindValues.append(capabilities);
+ break;
+ case(QServiceInterfaceDescriptor::Location):
+ isValidInterfaceProperty = false;
+ break;
+ case(QServiceInterfaceDescriptor::ServiceDescription):
+ isValidInterfaceProperty = false;
+ break;
+ case(QServiceInterfaceDescriptor::InterfaceDescription):
+ bindValues.append(QLatin1String(INTERFACE_DESCRIPTION_KEY));
+ interfaceDescription = interface.attribute(QServiceInterfaceDescriptor::InterfaceDescription).toString();
+ if (interfaceDescription.isNull())
+ interfaceDescription = QLatin1String("");
+ bindValues.append(interfaceDescription);
+ break;
+ default:
+ isValidInterfaceProperty = false;
+ break;
+ }
+
+ if (isValidInterfaceProperty) {
+ if (!executeQuery(query, statement, bindValues)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::insertInterfaceData():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+ ++iter;
+ }
+
+ //add custom attributes
+ QHash<QString, QString>::const_iterator customIter = interface.d->customAttributes.constBegin();
+ while (customIter!=interface.d->customAttributes.constEnd()) {
+ bindValues.clear();
+ bindValues.append(interfaceID);
+ //to avoid key clashes use separate c_ namespace ->is this sufficient?
+ bindValues.append(QVariant("c_"+customIter.key()));
+ bindValues.append(customIter.value());
+ if (!executeQuery(query, statement, bindValues)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::insertInterfaceData(customProps):-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ ++customIter;
+ }
+ m_lastError.setError(DBError::NoError);
+
+ return true;
+}
+
+/*
+ Helper function that executes the sql query specified in \a statement.
+ It is assumed that the \a statement uses positional placeholders and
+ corresponding parameters are placed in the list of \a bindValues.
+
+ Aside: This function may be safely called standalone or within an explicit
+ transaction. If called standalone, it's single query is implicitly
+ wrapped in it's own transaction.
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+bool ServiceDatabase::executeQuery(QSqlQuery *query, const QString &statement, const QList<QVariant> &bindValues)
+{
+ Q_ASSERT(query != NULL);
+
+ bool success = false;
+ enum {Prepare =0 , Execute=1};
+ for (int stage=Prepare; stage <= Execute; ++stage) {
+ if ( stage == Prepare)
+ success = query->prepare(statement);
+ else // stage == Execute
+ success = query->exec();
+
+ if (!success) {
+ QString errorText;
+ errorText = QLatin1String("Problem: Could not %1 statement: %2"
+ "Reason: %3"
+ "Parameters: %4\n");
+ QString parameters;
+ if (bindValues.count() > 0) {
+ for(int i = 0; i < bindValues.count(); ++i) {
+ parameters.append(QLatin1String("\n\t[") + QString::number(i) + "]: " + bindValues.at(i).toString());
+ }
+ } else {
+ parameters = QLatin1String("None");
+ }
+
+ DBError::ErrorCode errorType;
+ int result = query->lastError().number();
+ if (result == 26 || result == 11) {//SQLILTE_NOTADB || SQLITE_CORRUPT
+ qWarning() << "Service Framework:- Database file is corrupt or invalid:" << databasePath();
+ errorType = DBError::InvalidDatabaseFile;
+ }
+ else if ( result == 8) //SQLITE_READONLY
+ errorType = DBError::NoWritePermissions;
+ else
+ errorType = DBError::SqlError;
+
+ m_lastError.setError(errorType,
+ errorText
+ .arg(stage == Prepare ?QLatin1String("prepare"):QLatin1String("execute"))
+ .arg(statement)
+ .arg(query->lastError().text())
+ .arg(parameters));
+
+ query->finish();
+ query->clear();
+ return false;
+ }
+
+ if (stage == Prepare) {
+ foreach (const QVariant &bindValue, bindValues)
+ query->addBindValue(bindValue);
+ }
+ }
+
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Obtains a list of QServiceInterfaceDescriptors that match the constraints supplied
+ by \a filter.
+
+ May set last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+QList<QServiceInterfaceDescriptor> ServiceDatabase::getInterfaces(const QServiceFilter &filter)
+{
+ QList<QServiceInterfaceDescriptor> interfaces;
+ if (!checkConnection()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::getInterfaces():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return interfaces;
+ }
+
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ //multiple read queries are performed so wrap them
+ //in a read only transaction
+ if (!beginTransaction(&query, Read)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::getInterfaces():-"
+ << "Unable to begin transaction. "
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return interfaces;
+ }
+
+ //Prepare search query, bind criteria values
+ QString selectComponent = QLatin1String("SELECT Interface.Name, "
+ "Service.Name, Interface.VerMaj, "
+ "Interface.VerMin, "
+ "Service.Location, "
+ "Service.ID, "
+ "Interface.ID ");
+ QString fromComponent = QLatin1String("FROM Interface, Service ");
+ QString whereComponent = QLatin1String("WHERE Service.ID = Interface.ServiceID ");
+ QList<QVariant> bindValues;
+
+ if (filter.serviceName().isEmpty() && filter.interfaceName().isEmpty()) {
+ //do nothing, (don't add any extra constraints to the query
+ } else {
+
+ if (!filter.serviceName().isEmpty()) {
+ whereComponent.append(QLatin1String("AND Service.Name = ?")).append(QLatin1String(" COLLATE NOCASE "));
+ bindValues.append(filter.serviceName());
+ }
+ if (!filter.interfaceName().isEmpty()) {
+ whereComponent.append(QLatin1String("AND Interface.Name = ?")).append(QLatin1String(" COLLATE NOCASE "));
+ bindValues.append(filter.interfaceName());
+ if (filter.majorVersion() >=0 && filter.minorVersion() >=0) {
+ if (filter.versionMatchRule() == QServiceFilter::ExactVersionMatch) {
+ whereComponent.append(QLatin1String("AND Interface.VerMaj = ?")).append(QLatin1String(" AND Interface.VerMin = ? "));
+ bindValues.append(QString::number(filter.majorVersion()));
+ bindValues.append(QString::number(filter.minorVersion()));
+ }
+ else if (filter.versionMatchRule() == QServiceFilter::MinimumVersionMatch) {
+ whereComponent.append(QLatin1String("AND ((Interface.VerMaj > ?"))
+ .append(QLatin1String(") OR Interface.VerMaj = ?")).append(QLatin1String(" AND Interface.VerMin >= ?")).append(QLatin1String(") "));
+ bindValues.append(QString::number(filter.majorVersion()));
+ bindValues.append(QString::number(filter.majorVersion()));
+ bindValues.append(QString::number(filter.minorVersion()));
+ }
+ }
+ }
+ }
+
+ if (!executeQuery(&query, selectComponent + fromComponent + whereComponent, bindValues)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::getInterfaces():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ rollbackTransaction(&query);
+ return interfaces;
+ }
+
+ QServiceInterfaceDescriptor interface;
+ interface.d = new QServiceInterfaceDescriptorPrivate;
+ QStringList capabilities;
+ QString serviceID;
+ QString interfaceID;
+ const QSet<QString> filterCaps = filter.capabilities().toSet();
+ QSet<QString> difference;
+
+ while (query.next()){
+ difference.clear();
+ interface.d->customAttributes.clear();
+ interface.d->attributes.clear();
+ interface.d->interfaceName = query.value(EBindIndex).toString();
+ interface.d->serviceName = query.value(EBindIndex1).toString();
+ interface.d->major = query.value(EBindIndex2).toInt();
+ interface.d->minor = query.value(EBindIndex3).toInt();
+
+ QString location = query.value(EBindIndex4).toString();
+ if (location.startsWith(QLatin1String(SERVICE_IPC_PREFIX))) {
+ interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::InterProcess;
+ interface.d->attributes[QServiceInterfaceDescriptor::Location]
+ = location.remove(0,QString(QLatin1String(SERVICE_IPC_PREFIX)).size());
+ } else {
+ interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::Plugin;
+ interface.d->attributes[QServiceInterfaceDescriptor::Location] = location;
+ }
+
+ serviceID = query.value(EBindIndex5).toString();
+ if (!populateServiceProperties(&interface, serviceID)) {
+ //populateServiceProperties should already give a warning message
+ //and set the last error
+ interfaces.clear();
+ rollbackTransaction(&query);
+ return interfaces;
+ }
+
+ interfaceID = query.value(EBindIndex6).toString();
+ if (!populateInterfaceProperties(&interface, interfaceID)) {
+ //populateInterfaceProperties should already give a warning message
+ //and set the last error
+ interfaces.clear();
+ rollbackTransaction(&query);
+ return interfaces;
+ }
+
+ const QSet<QString> ifaceCaps = interface.d->attributes.value(QServiceInterfaceDescriptor::Capabilities).toStringList().toSet();
+ difference = ((filter.capabilityMatchRule() == QServiceFilter::MatchMinimum) ? (filterCaps-ifaceCaps) : (ifaceCaps-filterCaps));
+ if (!difference.isEmpty())
+ continue;
+
+ //only return those interfaces that comply with set custom filters
+ if (filter.customAttributes().size() > 0) {
+ QSet<QString> keyDiff = filter.customAttributes().toSet();
+ keyDiff.subtract(interface.d->customAttributes.uniqueKeys().toSet());
+ if (keyDiff.isEmpty()) { //target descriptor has same custom keys as filter
+ bool isMatch = true;
+ const QStringList keys = filter.customAttributes();
+ for (int i = 0; i<keys.count(); i++) {
+ if (interface.d->customAttributes.value(keys[i]) !=
+ filter.customAttribute(keys[i])) {
+ isMatch = false;
+ break;
+ }
+ }
+ if (isMatch)
+ interfaces.append(interface);
+ }
+ } else { //no custom keys -> SQL statement ensures proper selection already
+ interfaces.append(interface);
+ }
+ }
+
+ rollbackTransaction(&query);//read-only operation so just rollback
+ m_lastError.setError(DBError::NoError);
+ return interfaces;
+}
+
+/*
+ Obtains a QServiceInterfaceDescriptor that
+ corresponds to a given \a interfaceID
+
+ May set last error to one of the following error codes:
+ DBError::NoError
+ DBError::NotFound
+ DBError::SqlError
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+QServiceInterfaceDescriptor ServiceDatabase::getInterface(const QString &interfaceID)
+{
+ QServiceInterfaceDescriptor interface;
+ if (!checkConnection()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::getInterface():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return interface;
+ }
+
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ if (!beginTransaction(&query, Read)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::getInterface():-"
+ << "Unable to begin transaction. "
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return interface;
+ }
+
+ QString selectComponent = QLatin1String("SELECT Interface.Name, "
+ "Service.Name, Interface.VerMaj, "
+ "Interface.VerMin, "
+ "Service.Location, "
+ "Service.ID ");
+ QString fromComponent = QLatin1String("FROM Interface, Service ");
+ QString whereComponent = QLatin1String("WHERE Service.ID = Interface.ServiceID "
+ "AND Interface.ID = ? ");
+ QList<QVariant> bindValues;
+ bindValues.append(interfaceID);
+
+ if (!executeQuery(&query, selectComponent + fromComponent + whereComponent, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::getInterfaces():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return interface;
+ }
+
+ if (!query.next()) {
+ rollbackTransaction(&query);
+ QString errorText(QLatin1String("Interface implementation not found for Interface ID: %1"));
+ m_lastError.setError(DBError::NotFound, errorText.arg(interfaceID));
+ return interface;
+ }
+
+ interface.d = new QServiceInterfaceDescriptorPrivate;
+ interface.d->interfaceName =query.value(EBindIndex).toString();
+ interface.d->serviceName = query.value(EBindIndex1).toString();
+ interface.d->major = query.value(EBindIndex2).toInt();
+ interface.d->minor = query.value(EBindIndex3).toInt();
+
+ QString location = query.value(EBindIndex4).toString();
+ if (location.startsWith(QLatin1String(SERVICE_IPC_PREFIX))) {
+ interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::InterProcess;
+ interface.d->attributes[QServiceInterfaceDescriptor::Location]
+ = location.remove(0,QString(QLatin1String(SERVICE_IPC_PREFIX)).size());
+ } else {
+ interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::Plugin;
+ interface.d->attributes[QServiceInterfaceDescriptor::Location] = location;
+ }
+
+ QString serviceID = query.value(EBindIndex5).toString();
+ if (!populateServiceProperties(&interface, serviceID)) {
+ //populateServiceProperties should already give a warning message
+ //and set the last error
+ rollbackTransaction(&query);
+ return QServiceInterfaceDescriptor();
+ }
+
+ if (!populateInterfaceProperties(&interface, interfaceID)) {
+ //populateInterfaceProperties should already give a warning message
+ //and set the last error
+ rollbackTransaction(&query);
+ return QServiceInterfaceDescriptor();
+ }
+
+ rollbackTransaction(&query);//read only operation so just rollback
+ m_lastError.setError(DBError::NoError);
+ return interface;
+}
+
+/*
+ Obtains a list of services names. If \a interfaceName is empty,
+ then all service names are returned. If \a interfaceName specifies
+ an interface then the names of all services implementing that interface
+ are returned
+
+ May set last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+
+ Aside: There is only one query which implicitly gets
+ wrapped in it's own transaction.
+*/
+QStringList ServiceDatabase::getServiceNames(const QString &interfaceName)
+{
+ QStringList services;
+ if (!checkConnection()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::getServiceNames():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return services;
+ }
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+ QString selectComponent(QLatin1String("SELECT DISTINCT Service.Name COLLATE NOCASE "));
+ QString fromComponent;
+ QString whereComponent;
+ QList<QVariant> bindValues;
+ if (interfaceName.isEmpty()) {
+ fromComponent = QLatin1String("FROM Service ");
+ } else {
+ fromComponent = QLatin1String("FROM Interface,Service ");
+ whereComponent = QLatin1String("WHERE Service.ID = Interface.ServiceID AND Interface.Name = ? COLLATE NOCASE ");
+ bindValues.append(interfaceName);
+ }
+
+ if (!executeQuery(&query, selectComponent + fromComponent + whereComponent, bindValues)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::getServiceNames():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return services;
+ }
+
+ while ( query.next()) {
+ services.append(query.value(EBindIndex).toString());
+ }
+ query.finish();
+ query.clear();
+ m_lastError.setError(DBError::NoError);
+ return services;
+}
+
+/*
+ Returns a descriptor for the default interface implementation of
+ \a interfaceName.
+
+ For user scope databases only, \a defaultInterfaceID is set if the default
+ in the user scope database refers to a interface implementation in the
+ system scope database. In this case the descriptor will be invalid and
+ the \a defaultInterfaceID must be used to query the system scope database,
+ The last error set to DBError::ExternalIfaceIDFound
+
+ If this function is called within a transaction, \a inTransaction
+ must be set to true. If \a inTransaction is false, this fuction
+ will begin and end its own transaction.
+
+ The last error may be set to one of the following error codes:
+ DBError::NoError
+ DBError::ExternalIfaceIDFound
+ DBError::SqlError
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+QServiceInterfaceDescriptor ServiceDatabase::interfaceDefault(const QString &interfaceName, QString *defaultInterfaceID,
+ bool inTransaction)
+{
+ QServiceInterfaceDescriptor interface;
+ if (!checkConnection())
+ {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::interfaceDefault():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return interface;
+ }
+
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ if (!inTransaction && !beginTransaction(&query, Read)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::interfaceDefault(QString, QString):-"
+ << "Unable to begin transaction. "
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return interface;
+ }
+
+ QString statement(QLatin1String("SELECT InterfaceID FROM Defaults WHERE InterfaceName = ? COLLATE NOCASE"));
+ QList<QVariant> bindValues;
+ bindValues.append(interfaceName);
+ if (!executeQuery(&query, statement, bindValues)) {
+ if (!inTransaction)
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::interfaceDefault():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return interface;
+ }
+
+ QString interfaceID;
+ if (!query.next())
+ {
+ if (!inTransaction)
+ rollbackTransaction(&query);
+ QString errorText(QLatin1String("No default service found for interface: \"%1\""));
+ m_lastError.setError(DBError::NotFound, errorText.arg(interfaceName));
+ return interface;
+ }
+ else
+ interfaceID = query.value(EBindIndex).toString();
+ Q_ASSERT(!interfaceID.isEmpty());
+
+ statement = QLatin1String("SELECT Interface.Name, "
+ "Service.Name, Interface.VerMaj, "
+ "Interface.VerMin, "
+ "Service.Location, "
+ "Service.ID "
+ "FROM Service, Interface "
+ "WHERE Service.ID = Interface.ServiceID AND Interface.ID = ?");
+ bindValues.clear();
+ bindValues.append(interfaceID);
+ if (!executeQuery(&query, statement, bindValues))
+ {
+ if (!inTransaction)
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::interfaceDefault():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return interface;
+ }
+
+ if (!query.next()) {
+ if (!inTransaction)
+ rollbackTransaction(&query);
+ if (defaultInterfaceID != NULL )
+ *defaultInterfaceID = interfaceID;
+ m_lastError.setError(DBError::ExternalIfaceIDFound);
+ return interface;
+ }
+
+ interface.d = new QServiceInterfaceDescriptorPrivate;
+ interface.d->interfaceName =query.value(EBindIndex).toString();
+ interface.d->serviceName = query.value(EBindIndex1).toString();
+ interface.d->major = query.value(EBindIndex2).toInt();
+ interface.d->minor = query.value(EBindIndex3).toInt();
+
+ QString location = query.value(EBindIndex4).toString();
+ if (location.startsWith(QLatin1String(SERVICE_IPC_PREFIX))) {
+ interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::InterProcess;
+ interface.d->attributes[QServiceInterfaceDescriptor::Location]
+ = location.remove(0,QString(QLatin1String(SERVICE_IPC_PREFIX)).size());
+ } else {
+ interface.d->attributes[QServiceInterfaceDescriptor::ServiceType] = QService::Plugin;
+ interface.d->attributes[QServiceInterfaceDescriptor::Location] = location;
+ }
+
+ QString serviceID = query.value(EBindIndex5).toString();
+ if (!populateServiceProperties(&interface, serviceID)) {
+ //populateServiceProperties should already give a warning
+ //and set the last error
+ if (!inTransaction)
+ rollbackTransaction(&query);
+ return QServiceInterfaceDescriptor();
+ }
+
+ if (!populateInterfaceProperties(&interface, interfaceID)) {
+ //populateInterfaceProperties should already give a warning
+ //and set the last error
+ if (!inTransaction)
+ rollbackTransaction(&query);
+ return QServiceInterfaceDescriptor();
+ }
+
+ if (!inTransaction)
+ rollbackTransaction(&query); //Read only operation so just rollback
+ m_lastError.setError(DBError::NoError);
+ return interface;
+}
+
+/*
+ Sets a particular service's \a interface implementation as a the default
+ implementation to look up when using the interface's name in
+ interfaceDefault().
+
+ For a user scope database an \a externalInterfaceID can be provided
+ so that the Defaults table will contain a "link" to an interface
+ implmentation provided in the system scope database.
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBerror::NotFound
+ DBError::SqlError
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+bool ServiceDatabase::setInterfaceDefault(const QServiceInterfaceDescriptor &interface, const QString &externalInterfaceID)
+{
+ if (!checkConnection()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ //Begin Transaction
+ if (!beginTransaction(&query, Write)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
+ << "Problem: Unable to begin transaction."
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QString statement;
+ QList<QVariant> bindValues;
+ QString interfaceID = externalInterfaceID;
+ if (interfaceID.isEmpty()) {
+ statement = QLatin1String("SELECT Interface.ID from Interface, Service "
+ "WHERE Service.ID = Interface.ServiceID "
+ "AND Service.Name = ? COLLATE NOCASE "
+ "AND Interface.Name = ? COLLATE NOCASE "
+ "AND Interface.VerMaj = ? "
+ "AND Interface.VerMin = ? ");
+ bindValues.append(interface.serviceName());
+ bindValues.append(interface.interfaceName());
+ bindValues.append(interface.majorVersion());
+ bindValues.append(interface.minorVersion());
+
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ if (!query.next()) {
+ QString errorText;
+ errorText = QLatin1String("No implementation for interface: %1, Version: %2.%3 found "
+ "for service: %4");
+ m_lastError.setNotFoundError(errorText.arg(interface.interfaceName())
+ .arg(interface.majorVersion())
+ .arg(interface.minorVersion())
+ .arg(interface.serviceName()));
+
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatbase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
+ << "Problem: Unable to set default service. "
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ interfaceID = query.value(EBindIndex).toString();
+ Q_ASSERT(!interfaceID.isEmpty());
+ }
+
+ statement = QLatin1String("SELECT InterfaceName FROM Defaults WHERE InterfaceName = ? COLLATE NOCASE");
+ bindValues.clear();
+ bindValues.append(interface.interfaceName());
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ if (query.next()) {
+ statement = QLatin1String("UPDATE Defaults "
+ "SET InterfaceID = ? "
+ "WHERE InterfaceName = ? COLLATE NOCASE");
+ bindValues.clear();
+ bindValues.append(interfaceID);
+ bindValues.append(interface.interfaceName());
+
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ } else {
+ statement = QLatin1String("INSERT INTO Defaults(InterfaceName,InterfaceID) VALUES(?,?)");
+ bindValues.clear();
+ bindValues.append(interface.interfaceName());
+ bindValues.append(interfaceID);
+
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::setInterfaceDefault(QServiceInterfaceDescriptor):-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+
+ //End Transaction
+ if (!commitTransaction(&query)) {
+ rollbackTransaction(&query);
+ return false;
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Removes the service with name \a serviceName.
+ If the service provides a default interface implementation, then
+ another service implementing the highest interface implementation
+ version becomes the new default(if any). If more than one service
+ provides same the highest version number, an arbitrary choice is made
+ between them.
+
+ May set the last error to the folowing error codes:
+ DBError::NoError
+ DBError::NotFound
+ DBError::SqlError
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+bool ServiceDatabase::unregisterService(const QString &serviceName, const QString &securityToken)
+{
+#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+ Q_UNUSED(securityToken);
+#else
+ if (securityToken.isEmpty()) {
+ QString errorText(QLatin1String("Access denied, no security token provided (for unregistering service: \"%1\")"));
+ m_lastError.setError(DBError::NoWritePermissions, errorText.arg(serviceName));
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << "Problem: Unable to unregister service. "
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+#endif
+
+
+ if (!checkConnection()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ if (!beginTransaction(&query, Write)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << "Problem: Unable to begin transaction. "
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QString statement(QLatin1String("SELECT Service.ID from Service WHERE Service.Name = ? COLLATE NOCASE"));
+ QList<QVariant> bindValues;
+ bindValues.append(serviceName);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QStringList serviceIDs;
+ while (query.next()) {
+ serviceIDs << query.value(EBindIndex).toString();
+ }
+
+#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+ // Only the application that registered the service is allowed to unregister that
+ // service. Fetch a security ID of a service (with given name) and verify that it matches
+ // with current apps security id. Only one application is allowed to register services with
+ // same name, hence a distinct (just any of the) security token will do because they are identical.
+ if (!serviceIDs.isEmpty()) {
+ statement = QLatin1String("SELECT DISTINCT Value FROM ServiceProperty WHERE ServiceID = ? AND Key = ?");
+ bindValues.clear();
+ bindValues.append(serviceIDs.first());
+ bindValues.append(SECURITY_TOKEN_KEY);
+
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ QString existingSecurityToken;
+ if (query.next()) {
+ existingSecurityToken = query.value(EBindIndex).toString();
+ }
+ if (existingSecurityToken != securityToken) {
+ QString errorText(QLatin1String("Access denied: \"%1\""));
+ m_lastError.setError(DBError::NoWritePermissions, errorText.arg(serviceName));
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << "Problem: Unable to unregister service"
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+#endif // QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+
+ statement = QLatin1String("SELECT Interface.ID from Interface, Service "
+ "WHERE Interface.ServiceID = Service.ID "
+ "AND Service.Name =? COLLATE NOCASE");
+ bindValues.clear();
+ bindValues.append(serviceName);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QStringList interfaceIDs;
+ while (query.next()) {
+ interfaceIDs << query.value(EBindIndex).toString();
+ }
+
+ if (serviceIDs.count() == 0) {
+ QString errorText(QLatin1String("Service not found: \"%1\""));
+ m_lastError.setError(DBError::NotFound, errorText.arg(serviceName));
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << "Problem: Unable to unregister service"
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ statement = QLatin1String("SELECT Defaults.InterfaceName "
+ "FROM Defaults, Interface, Service "
+ "WHERE Defaults.InterfaceID = Interface.ID "
+ "AND Interface.ServiceID = Service.ID "
+ "AND Service.Name = ? COLLATE NOCASE");
+ bindValues.clear();
+ bindValues.append(serviceName);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase:unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QStringList serviceDefaultInterfaces;
+ while (query.next()) {
+ serviceDefaultInterfaces << query.value(EBindIndex).toString();
+ }
+
+
+ statement = QLatin1String("DELETE FROM Service WHERE Service.Name = ? COLLATE NOCASE");
+ bindValues.clear();
+ bindValues.append(serviceName);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ statement = QLatin1String("DELETE FROM Interface WHERE Interface.ServiceID = ?");
+ foreach (const QString &serviceID, serviceIDs) {
+ bindValues.clear();
+ bindValues.append(serviceID);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+
+ statement = QLatin1String("DELETE FROM ServiceProperty WHERE ServiceID = ?");
+
+ foreach (const QString &serviceID, serviceIDs) {
+ bindValues.clear();
+ bindValues.append(serviceID);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+
+ statement = QLatin1String("DELETE FROM InterfaceProperty WHERE InterfaceID = ?");
+ foreach (const QString &interfaceID, interfaceIDs) {
+ bindValues.clear();
+ bindValues.append(interfaceID);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+
+ foreach (const QString &interfaceName, serviceDefaultInterfaces) {
+ statement = QLatin1String("SELECT ID FROM Interface WHERE Interface.Name = ? COLLATE NOCASE "
+ "ORDER BY Interface.VerMaj DESC, Interface.VerMin DESC");
+ bindValues.clear();
+ bindValues.append(interfaceName);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ if (query.next()) {
+ QString newDefaultID = query.value(EBindIndex).toString();
+ statement = QLatin1String("UPDATE Defaults SET InterfaceID = ? WHERE InterfaceName = ? COLLATE NOCASE ");
+ bindValues.clear();
+ bindValues.append(newDefaultID);
+ bindValues.append(interfaceName);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ } else {
+ statement = QLatin1String("DELETE FROM Defaults WHERE InterfaceName = ? COLLATE NOCASE ");
+ bindValues.clear();
+ bindValues.append(interfaceName);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+ }
+
+ //databaseCommit
+ if (!commitTransaction(&query)) {
+ rollbackTransaction(&query);
+ return false;
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Registers the service initialization into the database.
+*/
+bool ServiceDatabase::serviceInitialized(const QString &serviceName, const QString &securityToken)
+{
+#ifndef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+ Q_UNUSED(securityToken);
+#endif
+
+ if (!checkConnection()) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ if (!beginTransaction(&query, Write)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::serviceInitialized():-"
+ << "Problem: Unable to begin transaction"
+ << "\nReason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QString statement(QLatin1String("SELECT Service.ID from Service WHERE Service.Name = ? COLLATE NOCASE"));
+ QList<QVariant> bindValues;
+ bindValues.append(serviceName);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::serviceInitialized():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QStringList serviceIDs;
+ while(query.next()) {
+ serviceIDs << query.value(EBindIndex).toString();
+ }
+
+
+#ifdef QT_SFW_SERVICEDATABASE_USE_SECURITY_TOKEN
+ statement = QLatin1String("SELECT Value FROM ServiceProperty WHERE ServiceID = ? AND Key = ?");
+ bindValues.clear();
+ bindValues.append(serviceName);
+ bindValues.append(SECURITY_TOKEN_KEY);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::unregisterService():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QStringList securityTokens;
+ while (query.next()) {
+ securityTokens << query.value(EBindIndex).toString();
+ }
+
+ if (!securityTokens.isEmpty() && (securityTokens.first() != securityToken)) {
+ QString errorText(QLatin1String("Access denied: \"%1\""));
+ m_lastError.setError(DBError::NoWritePermissions, errorText.arg(serviceName));
+ rollbackTransaction(&query);
+ #ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::serviceInitialized():-"
+ << "Problem: Unable to update service initialization"
+ << "\nReason:" << qPrintable(m_lastError.text());
+ #endif
+ }
+#endif
+
+ statement = QLatin1String("DELETE FROM ServiceProperty WHERE ServiceID = ? AND Key = ?");
+ foreach (const QString &serviceID, serviceIDs) {
+ bindValues.clear();
+ bindValues.append(serviceID);
+ bindValues.append(QLatin1String(SERVICE_INITIALIZED_KEY));
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::serviceInitialized():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+
+ //databaseCommit
+ if (!commitTransaction(&query)) {
+ rollbackTransaction(&query);
+ return false;
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Closes the database
+
+ May set the following error codes:
+ DBError::NoError
+ DBError::InvalidDatabaseConnection
+*/
+bool ServiceDatabase::close()
+{
+ if (m_isDatabaseOpen) {
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName, false);
+ if (database.isValid()) {
+ if (database.isOpen()) {
+ database.close();
+ m_isDatabaseOpen = false;
+ return true;
+ }
+ } else {
+ m_lastError.setError(DBError::InvalidDatabaseConnection);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::close():-"
+ << "Problem: " << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Sets the path of the service database to \a databasePath
+*/
+void ServiceDatabase::setDatabasePath(const QString &databasePath)
+{
+ m_databasePath = QDir::toNativeSeparators(databasePath);
+}
+
+/*
+ Returns the path of the service database
+*/
+QString ServiceDatabase::databasePath() const
+{
+ QString path;
+ if (m_databasePath.isEmpty()) {
+#ifdef Q_OS_SYMBIAN
+ QString qtVersion(qVersion());
+ qtVersion = qtVersion.left(qtVersion.size() -2); //strip off patch version
+ path = QDir::toNativeSeparators(QCoreApplication::applicationDirPath() + "/QtServiceFramework_" +
+ qtVersion + "_system" + QLatin1String(".db"));
+#else
+ QSettings settings(QSettings::SystemScope, QLatin1String("Nokia"), QLatin1String("Services"));
+ path = settings.value(QLatin1String("ServicesDB/Path")).toString();
+ if (path.isEmpty()) {
+ path = QDir::currentPath();
+ if (path.lastIndexOf(QLatin1String(RESOLVERDATABASE_PATH_SEPARATOR)) != path.length() -1) {
+ path.append(QLatin1String(RESOLVERDATABASE_PATH_SEPARATOR));
+ }
+ path.append(QLatin1String(RESOLVERDATABASE));
+ }
+ path = QDir::toNativeSeparators(path);
+#endif
+ } else {
+ path = m_databasePath;
+ }
+
+ return path;
+}
+
+/*
+ Helper method that creates the database tables: Service, Interface,
+ Defaults, ServiceProperty and InterfaceProperty
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+bool ServiceDatabase::createTables()
+{
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ //Begin Transaction
+ if (!beginTransaction(&query, Write)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::createTables():-"
+ << "Unable to begin transaction. "
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QString statement(QLatin1String("CREATE TABLE Service("
+ "ID TEXT NOT NULL PRIMARY KEY UNIQUE,"
+ "Name TEXT NOT NULL, "
+ "Location TEXT NOT NULL)"));
+ if (!executeQuery(&query, statement)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::createTables():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ statement = QLatin1String("CREATE TABLE Interface("
+ "ID TEXT NOT NULL PRIMARY KEY UNIQUE,"
+ "ServiceID TEXT NOT NULL, "
+ "Name TEXT NOT NULL, "
+ "VerMaj INTEGER NOT NULL, "
+ "VerMin INTEGER NOT NULL)");
+ if (!executeQuery(&query, statement)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::createTables():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ statement = QLatin1String("CREATE TABLE Defaults("
+ "InterfaceName TEXT PRIMARY KEY UNIQUE NOT NULL,"
+ "InterfaceID TEXT NOT NULL)");
+ if (!executeQuery(&query, statement)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::createTables():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ statement = QLatin1String("CREATE TABLE ServiceProperty("
+ "ServiceID TEXT NOT NULL,"
+ "Key TEXT NOT NULL,"
+ "Value TEXT NOT NULL)");
+ if (!executeQuery(&query, statement)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::createTables():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ statement = QLatin1String("CREATE TABLE InterfaceProperty("
+ "InterfaceID TEXT NOT NULL,"
+ "Key TEXT NOT NULL,"
+ "Value TEXT NOT NULL)");
+
+ if (!executeQuery(&query, statement)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::createTables():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ if (!commitTransaction(&query)) {
+ rollbackTransaction(&query);
+ return false;
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*!
+ Helper method that checks if the all expected tables exist in the database
+ Returns true if they all exist and false if any of them don't
+*/
+bool ServiceDatabase::checkTables()
+{
+ bool bTables(false);
+ QStringList tables = QSqlDatabase::database(m_connectionName).tables();
+ if (tables.contains(QLatin1String(SERVICE_TABLE))
+ && tables.contains(QLatin1String(INTERFACE_TABLE))
+ && tables.contains(QLatin1String(DEFAULTS_TABLE))
+ && tables.contains(QLatin1String(SERVICE_PROPERTY_TABLE))
+ && tables.contains(QLatin1String(INTERFACE_PROPERTY_TABLE))){
+ bTables = true;
+ }
+ return bTables;
+}
+
+/*
+ This function should only ever be used on a user scope database
+ It removes an entry from the Defaults table where the default
+ refers to an interface implementation in the system scope database.
+ The particular default that is removed is specified by
+ \a interfaceID.
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::IfaceIDNotExternal
+ DBError::SqlError
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+bool ServiceDatabase::removeExternalDefaultServiceInterface(const QString &interfaceID)
+{
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+
+ //begin transaction
+ if (!beginTransaction(&query, Write)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::removeExternalDefaultServiceInterface():-"
+ << "Problem: Unable to begin transaction. "
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ QString statement(QLatin1String("SELECT Name FROM Interface WHERE Interface.ID = ?"));
+ QList<QVariant> bindValues;
+ bindValues.append(interfaceID);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::removeDefaultServiceInterface():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ if (query.next()) {
+ QString interfaceName = query.value(EBindIndex).toString();
+ QString errorText(QLatin1String("Local interface implementation exists for interface \"%1\" "
+ "with interfaceID: \"%2\""));
+ m_lastError.setError(DBError::IfaceIDNotExternal,
+ errorText.arg(interfaceName).arg(interfaceID));
+ rollbackTransaction(&query);
+ return false;
+ }
+
+ statement = QLatin1String("DELETE FROM Defaults WHERE InterfaceID = ? COLLATE NOCASE");
+ bindValues.clear();
+ bindValues.append(interfaceID);
+ if (!executeQuery(&query, statement, bindValues)) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::removeDefaultServiceInterface():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ //end transaction
+ if (!commitTransaction(&query)){
+ rollbackTransaction(&query);
+ return false;
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Removes all tables from the database
+
+ In future this function may be deprecated or removed.
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+bool ServiceDatabase::dropTables()
+{
+ //Execute transaction for deleting the database tables
+ QSqlDatabase database = QSqlDatabase::database(m_connectionName);
+ QSqlQuery query(database);
+ QStringList expectedTables;
+ expectedTables << QLatin1String(SERVICE_TABLE)
+ << QLatin1String(INTERFACE_TABLE)
+ << QLatin1String(DEFAULTS_TABLE)
+ << QLatin1String(SERVICE_PROPERTY_TABLE)
+ << QLatin1String(INTERFACE_PROPERTY_TABLE);
+
+ if (database.tables().count() > 0) {
+ if (!beginTransaction(&query, Write)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::dropTables():-"
+ << "Unable to begin transaction. "
+ << "Reason:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ QStringList actualTables = database.tables();
+
+ foreach (const QString expectedTable, expectedTables) {
+ if ((actualTables.contains(expectedTable))
+ && (!executeQuery(&query, QLatin1String("DROP TABLE ") + expectedTable))) {
+ rollbackTransaction(&query);
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::dropTables():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ }
+ if (!commitTransaction(&query)) {
+ rollbackTransaction(&query);
+ return false;
+ }
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Checks if the database is open
+*/
+bool ServiceDatabase::isOpen() const
+{
+ return m_isDatabaseOpen;
+}
+
+/*
+ Checks the database connection.
+
+ May set the last error to one of the following error codes:
+ DBError::DatabaseNotOpen
+ DBError::InvalidDatabaseConnection
+*/
+bool ServiceDatabase::checkConnection()
+{
+ if (!m_isDatabaseOpen)
+ {
+ m_lastError.setError(DBError::DatabaseNotOpen);
+ return false;
+ }
+
+ if (!QSqlDatabase::database(m_connectionName).isValid())
+ {
+ m_lastError.setError(DBError::InvalidDatabaseConnection);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ Begins a transcaction based on the \a type which can be Read or Write.
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+ DBError::NoWritePermissions
+ DBError::InvalidDatabaseFile
+*/
+bool ServiceDatabase::beginTransaction(QSqlQuery *query, TransactionType type)
+{
+ bool success;
+ if (type == Read)
+ success = query->exec(QLatin1String("BEGIN"));
+ else
+ success = query->exec(QLatin1String("BEGIN IMMEDIATE"));
+
+ if (!success) {
+ int result = query->lastError().number();
+ if (result == 26 || result == 11) {//SQLITE_NOTADB || SQLITE_CORRUPT
+ qWarning() << "Service Framework:- Database file is corrupt or invalid:" << databasePath();
+ m_lastError.setError(DBError::InvalidDatabaseFile, query->lastError().text());
+ }
+ else if (result == 8) { //SQLITE_READONLY
+ qWarning() << "Service Framework:- Insufficient permissions to write to database:" << databasePath();
+ m_lastError.setError(DBError::NoWritePermissions, query->lastError().text());
+ }
+ else
+ m_lastError.setError(DBError::SqlError, query->lastError().text());
+ return false;
+ }
+
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Commits a transaction
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+*/
+bool ServiceDatabase::commitTransaction(QSqlQuery *query)
+{
+ Q_ASSERT(query != NULL);
+ query->finish();
+ query->clear();
+ if (!query->exec(QLatin1String("COMMIT"))) {
+ m_lastError.setError(DBError::SqlError, query->lastError().text());
+ return false;
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Rolls back a transaction
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+*/
+bool ServiceDatabase::rollbackTransaction(QSqlQuery *query)
+{
+ Q_ASSERT(query !=NULL);
+ query->finish();
+ query->clear();
+
+ if (!query->exec(QLatin1String("ROLLBACK"))) {
+ m_lastError.setError(DBError::SqlError, query->lastError().text());
+ return false;
+ }
+ return true;
+}
+
+/*
+ Helper function that populates a service \a interface descriptor
+ with interface related attributes corresponding to the interface
+ represented by \a interfaceID
+
+ It is already assumed that a transaction has been started by the time
+ this function is called. This function will not rollback/commit the
+ transaction.
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+*/
+bool ServiceDatabase::populateInterfaceProperties(QServiceInterfaceDescriptor *interface, const QString &interfaceID)
+{
+ QSqlQuery query(QSqlDatabase::database(m_connectionName));
+ QString statement(QLatin1String("SELECT Key, Value FROM InterfaceProperty WHERE InterfaceID = ?"));
+ QList<QVariant> bindValues;
+ bindValues.append(interfaceID);
+ if (!executeQuery(&query, statement, bindValues)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::populateInterfaceProperties():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ bool isFound = false;
+ QString attribute;
+ while (query.next()) {
+ isFound = true;
+ attribute = query.value(EBindIndex).toString();
+ if (attribute == QLatin1String(INTERFACE_CAPABILITY_KEY)) {
+ const QStringList capabilities = query.value(EBindIndex1).toString().split(QLatin1String(","));
+ if (capabilities.count() == 1 && capabilities[0].isEmpty()) {
+ interface->d->attributes[QServiceInterfaceDescriptor::Capabilities]
+ = QStringList();
+ } else {
+ interface->d->attributes[QServiceInterfaceDescriptor::Capabilities]
+ = capabilities;
+ }
+ } else if (attribute == QLatin1String(INTERFACE_DESCRIPTION_KEY)) {
+ interface->d->attributes[QServiceInterfaceDescriptor::InterfaceDescription]
+ = query.value(EBindIndex1).toString();
+ } else if (attribute.startsWith(QLatin1String("c_"))) {
+ interface->d->customAttributes[attribute.mid(2)]
+ = query.value(EBindIndex1).toString();
+ }
+ }
+
+ if (!isFound) {
+ QString errorText(QLatin1String("Database integrity corrupted, Properties for InterfaceID: %1 does not exist in the InterfaceProperty table for interface \"%2\""));
+ m_lastError.setError(DBError::SqlError, errorText.arg(interfaceID).arg(interface->interfaceName()));
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::populateInterfaceProperties():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+/*
+ Helper function that populates a service \a interface descriptor
+ with service related attributes corresponding to the service
+ represented by \a serviceID
+
+ It is already assumed that a transaction has been started by the time
+ this function is called. This function will not rollback/commit the
+ transaction.
+
+ May set the last error to one of the following error codes:
+ DBError::NoError
+ DBError::SqlError
+*/
+bool ServiceDatabase::populateServiceProperties(QServiceInterfaceDescriptor *interface, const QString &serviceID)
+{
+ QSqlQuery query(QSqlDatabase::database(m_connectionName));
+ QString statement(QLatin1String("SELECT Key, Value FROM ServiceProperty WHERE ServiceID = ?"));
+ QList<QVariant> bindValues;
+ bindValues.append(serviceID);
+ if (!executeQuery(&query, statement, bindValues)) {
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::populateServiceProperties():-"
+ << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+
+ bool isFound = false;
+ QString attribute;
+ while (query.next()) {
+ isFound = true;
+ attribute = query.value(EBindIndex).toString();
+ if (attribute == QLatin1String(SERVICE_DESCRIPTION_KEY)) {
+ interface->d->attributes[QServiceInterfaceDescriptor::ServiceDescription]
+ = query.value(EBindIndex1).toString();
+ }
+ // fetch initialized and put it as a custom attribute
+ if (attribute == QLatin1String(SERVICE_INITIALIZED_KEY)) {
+ interface->d->customAttributes[attribute] = query.value(EBindIndex1).toString();
+ }
+ }
+
+ if (!isFound) {
+ QString errorText(QLatin1String("Database integrity corrupted, Service Properties for ServiceID: \"%1\" does not exist in the ServiceProperty table for service \"%2\""));
+ m_lastError.setError(DBError::SqlError, errorText.arg(serviceID).arg(interface->serviceName()));
+#ifdef QT_SFW_SERVICEDATABASE_DEBUG
+ qWarning() << "ServiceDatabase::populateServiceProperties():-"
+ << "Problem:" << qPrintable(m_lastError.text());
+#endif
+ return false;
+ }
+ m_lastError.setError(DBError::NoError);
+ return true;
+}
+
+#include "moc_servicedatabase_p.cpp"
+
+QTM_END_NAMESPACE