/**
* @brief Adds a trigger context to a job.
- * @remarks This function consumes the given @c trigger.
+ * @remarks On success, this function consumes the given @c trigger.
* Using the @c trigger after calling this function leads to undefined behavior.
* @param[in] job Job handle
* @param[in] trigger Trigger context
/**
* @brief Adds a requirement context to a job.
- * @remarks This function consumes the given @c requirement.
+ * @remarks On success, this function consumes the given @c requirement.
* Using the @c requirement after calling this function leads to undefined behavior.
* @param[in] job Job handle
* @param[in] requirement Requirement context
BuildRequires: pkgconfig(glib-2.0)
BuildRequires: pkgconfig(gio-2.0)
BuildRequires: pkgconfig(jsoncpp)
+BuildRequires: pkgconfig(sqlite3)
BuildRequires: pkgconfig(dlog)
BuildRequires: pkgconfig(bundle)
BuildRequires: pkgconfig(capi-base-common)
SET(target "${PROJECT_NAME}-server-genuine")
-SET(DEPS "${DEPS} jsoncpp context-common-server")
+SET(DEPS "${DEPS} jsoncpp sqlite3 context-common-server")
FILE(GLOB_RECURSE SRCS *.cpp ../shared/*.cpp)
MESSAGE("Sources: ${SRCS}")
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sqlite3.h>
+#include <SharedUtil.h>
+#include <PlatformDatabase.h>
+#include "JobInfoDatabase.h"
+
+#define DB_NAME "context-job-scheduler"
+#define JOB_INFO_TABLE "PersistentJobInfo"
+#define ST_STARTED "started"
+
+using namespace ctx;
+
+static Database* __database = NULL;
+static unsigned int __numDBConnection = 0;
+
+static void __initDatabase()
+{
+ const char* createJobInfoTable =
+ "CREATE TABLE IF NOT EXISTS " JOB_INFO_TABLE "(" \
+ "uid INTEGER NOT NULL," \
+ "jobId INTEGER NOT NULL," \
+ "owner TEXT NOT NULL," \
+ "jobInfo TEXT NOT NULL," \
+ "state TEXT NOT NULL DEFAULT ''," \
+ "PRIMARY KEY (uid, jobId)" \
+ ");";
+
+ __database->execute(createJobInfoTable, NULL);
+}
+
+static bool __open()
+{
+ if (__numDBConnection == 0) {
+ __database = new PlatformDatabase(DB_NAME);
+
+ if (!__database->open()) {
+ delete __database;
+ __database = NULL;
+ _E("DB open failed");
+ return false;
+ }
+
+ __initDatabase();
+ }
+
+ ++__numDBConnection;
+ return true;
+}
+
+static void __close()
+{
+ if (__numDBConnection == 1) {
+ delete __database;
+ __database = NULL;
+ }
+
+ if (__numDBConnection != 0)
+ --__numDBConnection;
+}
+
+JobInfoDatabase::JobInfoDatabase(uid_t uid) :
+ __uid(uid)
+{
+ if (!__open()) {
+ throw std::runtime_error("DB open failed");
+ }
+}
+
+JobInfoDatabase::~JobInfoDatabase()
+{
+ __close();
+}
+
+void JobInfoDatabase::add(JobInfo* jobInfo, const std::string& owner)
+{
+ char* query = sqlite3_mprintf(
+ "INSERT INTO " JOB_INFO_TABLE " (uid, jobId, owner, jobInfo) VALUES (%d, %d, '%s', '%s')",
+ static_cast<int>(__uid), jobInfo->getId(), owner.c_str(), jobInfo->serialize().c_str());
+
+ __database->execute(query, NULL);
+
+ sqlite3_free(query);
+}
+
+void JobInfoDatabase::remove(int jobId)
+{
+ char* query = sqlite3_mprintf(
+ "DELETE FROM " JOB_INFO_TABLE " WHERE uid = %d AND jobId = %d",
+ static_cast<int>(__uid), jobId);
+
+ __database->execute(query, NULL);
+
+ sqlite3_free(query);
+}
+
+void JobInfoDatabase::setStarted(int jobId, bool started)
+{
+ char* query = sqlite3_mprintf(
+ "UPDATE " JOB_INFO_TABLE " SET state = '%s' WHERE uid = %d AND jobId = %d",
+ started ? ST_STARTED : EMPTY_STR, static_cast<int>(__uid), jobId);
+
+ __database->execute(query, NULL);
+
+ sqlite3_free(query);
+}
+
+std::vector<std::pair<std::string, JobInfo*>> JobInfoDatabase::getAll()
+{
+ std::vector<std::pair<std::string, JobInfo*>> jobInfos;
+ std::vector<std::shared_ptr<Tuple>> queryResult;
+
+ std::string query =
+ "SELECT owner, jobInfo, state FROM " JOB_INFO_TABLE " WHERE uid = "
+ + std::to_string(__uid);
+
+ __database->execute(query, &queryResult);
+
+ for (auto& tuple : queryResult) {
+ const char* owner = NULL;
+ const char* jobInfoStr = NULL;
+ const char* state = NULL;
+
+ if (!tuple->getAt(0, &owner)) continue;
+ if (!tuple->getAt(1, &jobInfoStr)) continue;
+ if (!tuple->getAt(2, &state)) continue;
+
+ JobInfo* jobInfo = JobInfo::deserialize(jobInfoStr);
+ if (!jobInfo) continue;
+
+ jobInfo->setStarted(STR_EQ(state, ST_STARTED));
+
+ jobInfos.emplace_back(owner, jobInfo);
+ }
+
+ return jobInfos;
+}
--- /dev/null
+/*
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __CONTEXT_JOB_SCHEDULER_JOB_INFO_DB_H__
+#define __CONTEXT_JOB_SCHEDULER_JOB_INFO_DB_H__
+
+#include <vector>
+#include <JobInfo.h>
+
+namespace ctx {
+
+ class JobInfoDatabase {
+ public:
+ JobInfoDatabase(uid_t uid);
+ ~JobInfoDatabase();
+
+ void add(JobInfo* jobInfo, const std::string& owner);
+ void remove(int jobId);
+ void setStarted(int jobId, bool started);
+ std::vector<std::pair<std::string, JobInfo*>> getAll();
+
+ private:
+ uid_t __uid;
+ };
+
+}
+
+#endif /* __CONTEXT_JOB_SCHEDULER_JOB_INFO_DB_H__ */
using namespace ctx;
+static int __lastJobId = 0;
+
JobManager::JobManager(uid_t uid) :
- __uid(uid)
+ __uid(uid),
+ __jobInfoDatabase(uid)
{
+ _I("Initialize JobManager-%u", static_cast<unsigned>(__uid));
+ __restore();
+ __dump();
}
JobManager::~JobManager()
{
+ _I("Release JobManager-%u", static_cast<unsigned>(__uid));
+ __release();
}
bool JobManager::isSupported(JobContext::Type type, const std::string& uri)
{
- //TODO
+ //TODO: availability check
return true;
}
int JobManager::addJob(JobInfo* jobInfo, IClient* owner)
{
- if (jobInfo->getType() == JobInfo::Type::PERIODIC)
- return __addPeriodicJob(static_cast<PeriodicJobInfo*>(jobInfo), owner);
- else if (jobInfo->getType() == JobInfo::Type::ON_DEMAND)
- return __addOnDemandJob(static_cast<OnDemandJobInfo*>(jobInfo), owner);
+ IF_FAIL_RETURN(!owner->getName().empty(), E_ACCESS);
+
+ int jobId = 0;
+
+ //TODO: duplication test
+
+ if (jobInfo->getType() == JobInfo::Type::PERIODIC) {
+ jobId = __addPeriodicJob(static_cast<PeriodicJobInfo*>(jobInfo), owner);
+
+ } else if (jobInfo->getType() == JobInfo::Type::ON_DEMAND) {
+ jobId = __addOnDemandJob(static_cast<OnDemandJobInfo*>(jobInfo), owner);
+
+ } else {
+ return E_PARAM;
+ }
+
+ if (jobInfo->isPersistent())
+ __jobInfoDatabase.add(jobInfo, owner->getName());
- return E_PARAM;
+ return jobId;
}
int JobManager::__addPeriodicJob(PeriodicJobInfo* jobInfo, IClient* owner)
{
__verifyPeriodicJob(jobInfo, owner);
- const std::string& ownerId = owner->getName();
- IF_FAIL_RETURN(!ownerId.empty(), E_ACCESS);
-
jobInfo->setId(__generateJobId());
- //TODO: insert to the DB, if it is a persistent job
-
- JobRunner* runner = new JobRunner(jobInfo);
- __jobRunners[ownerId].push_back(runner);
+ unsigned int cnt = __addRunner(new JobRunner(owner->getName(), jobInfo));
+ _D("%u jobs have been registered", cnt);
return jobInfo->getId();
}
{
__verifyOnDemandJob(jobInfo, owner);
- const std::string& ownerId = owner->getName();
- IF_FAIL_RETURN(!ownerId.empty(), E_ACCESS);
-
jobInfo->setId(__generateJobId());
- //TODO: insert to the DB, if it is a persistent job
-
- JobRunner* runner = new JobRunner(jobInfo);
- __jobRunners[ownerId].push_back(runner);
+ unsigned int cnt = __addRunner(new JobRunner(owner->getName(), jobInfo));
+ _D("%u jobs have been registered", cnt);
return jobInfo->getId();
}
int JobManager::startJob(int jobId, IClient* owner)
{
- JobRunner* jobRunner = __findJobRunner(owner->getName(), jobId);
+ JobRunner* jobRunner = __findRunner(owner->getName(), jobId);
IF_FAIL_RETURN(jobRunner, E_PARAM);
- if (jobRunner->getJobInfo()->isStarted()) {
- _D("Job-%d of %s has started already", jobId, owner->getName());
+ if (jobRunner->isStarted()) {
+ _D("Job-%d of %s has started already", jobId, owner->getName().c_str());
return E_RULE_ON;
}
- jobRunner->getJobInfo()->setStarted(true);
+ jobRunner->start();
- //TODO
+ if (jobRunner->isPersistent())
+ __jobInfoDatabase.setStarted(jobId, true);
return E_NONE;
}
int JobManager::stopJob(int jobId, IClient* owner)
{
- JobRunner* jobRunner = __findJobRunner(owner->getName(), jobId);
+ JobRunner* jobRunner = __findRunner(owner->getName(), jobId);
IF_FAIL_RETURN(jobRunner, E_PARAM);
- if (!jobRunner->getJobInfo()->isStarted()) {
- _D("Job-%d of %s has stopped already", jobId, owner->getName());
+ if (!jobRunner->isStarted()) {
+ _D("Job-%d of %s has stopped already", jobId, owner->getName().c_str());
return E_RULE_OFF;
}
- jobRunner->getJobInfo()->setStarted(false);
+ jobRunner->stop();
- //TODO
+ if (jobRunner->isPersistent())
+ __jobInfoDatabase.setStarted(jobId, false);
return E_NONE;
}
int JobManager::removeJob(int jobId, IClient* owner)
{
- IF_FAIL_RETURN(stopJob(jobId, owner) != E_PARAM, E_PARAM);
+ JobRunner* runner = __findRunner(owner->getName(), jobId);
+ IF_FAIL_RETURN_TAG(runner, E_PARAM, _W, "Not found");
- auto& runners = __jobRunners[owner->getName()];
+ if (runner->isStarted())
+ runner->stop();
- for (auto iter = runners.begin(); iter != runners.end(); ++iter) {
- JobRunner* runner = *iter;
+ if (runner->isPersistent())
+ __jobInfoDatabase.remove(jobId);
- if (runner->getJobInfo()->getId() != jobId)
- continue;
-
- //TODO: delete from the DB
-
- delete runner;
- runners.erase(iter);
-
- break;
- }
+ __removeRunner(runner);
return E_NONE;
}
JobInfo* JobManager::getJobInfo(int jobId, IClient* owner)
{
- JobRunner* jobRunner = __findJobRunner(owner->getName(), jobId);
+ JobRunner* jobRunner = __findRunner(owner->getName(), jobId);
IF_FAIL_RETURN(jobRunner, NULL);
return jobRunner->getJobInfo();
{
std::vector<JobInfo*> jobInfos;
- auto runnerMapIterator = __jobRunners.find(owner->getName());
- IF_FAIL_RETURN(runnerMapIterator != __jobRunners.end(), jobInfos);
-
- for (auto& runner : runnerMapIterator->second) {
- jobInfos.push_back(runner->getJobInfo());
+ for (auto& runner : __jobRunners) {
+ if (runner->getOwner() == owner->getName()) {
+ jobInfos.push_back(runner->getJobInfo());
+ }
}
return jobInfos;
bool JobManager::__isPermitted(IClient* client, const std::string& uri)
{
- //TODO
+ //TODO: permission check
return true;
}
int JobManager::__generateJobId()
{
- static int lastJobId = 0;
+ if (++__lastJobId < 0)
+ __lastJobId = 1;
- if (lastJobId == 0) {
- //TODO: get the last "persistent" job id
- }
-
- if (++lastJobId < 0)
- lastJobId = 1;
+ return __lastJobId;
+}
- return lastJobId;
+unsigned int JobManager::__addRunner(JobRunner* runner)
+{
+ __jobRunners.push_back(runner);
+ return __jobRunners.size();
}
-JobRunner* JobManager::__findJobRunner(const std::string& ownerId, int jobId)
+bool JobManager::__removeRunner(JobRunner* runner)
{
- auto runnerMapIterator = __jobRunners.find(ownerId);
- IF_FAIL_RETURN_TAG(runnerMapIterator != __jobRunners.end(), NULL, _I, "%s has no job", ownerId.c_str());
+ __jobRunners.remove(runner);
+ delete runner;
+ return true;
+}
- for (auto& runner : runnerMapIterator->second) {
- if (runner->getJobInfo()->getId() == jobId)
+JobRunner* JobManager::__findRunner(const std::string& ownerId, int jobId)
+{
+ for (auto& runner : __jobRunners) {
+ if (runner->getJobId() == jobId)
return runner;
}
_I("%s does not have the Job-%d", ownerId.c_str(), jobId);
return NULL;
}
+
+void JobManager::__restore()
+{
+ int maxJobId = 0;
+ auto storedJobs = __jobInfoDatabase.getAll();
+
+ _I("Restoring %u jobs", storedJobs.size());
+
+ for (auto& storedJob : storedJobs) {
+ const std::string& owner = storedJob.first;
+ JobInfo* jobInfo = storedJob.second;
+
+ maxJobId = MAX(maxJobId, jobInfo->getId());
+
+ JobRunner* runner = new JobRunner(owner, jobInfo);
+ if (jobInfo->isStarted())
+ runner->start();
+
+ __addRunner(runner);
+ }
+
+ __lastJobId = maxJobId;
+}
+
+void JobManager::__release()
+{
+ for (auto& runner : __jobRunners) {
+ runner->stop();
+ delete runner;
+ }
+
+ __jobRunners.clear();
+}
+
+void JobManager::__dump()
+{
+ for (auto& runner : __jobRunners) {
+ _D("%s : %d", runner->getOwner().c_str(), runner->getJobId());
+ }
+}
#ifndef __CONTEXT_JOB_SCHEDULER_JOB_MANAGER_H__
#define __CONTEXT_JOB_SCHEDULER_JOB_MANAGER_H__
-#include <vector>
-#include <map>
+#include <list>
#include <JobInfo.h>
#include <JobContext.h>
#include <IClient.h>
+#include "JobInfoDatabase.h"
namespace ctx {
void __verifyAction(JobInfo* jobInfo, IClient* owner);
int __generateJobId();
- JobRunner* __findJobRunner(const std::string& ownerId, int jobId);
+
+ unsigned int __addRunner(JobRunner* runner);
+ bool __removeRunner(JobRunner* runner);
+ JobRunner* __findRunner(const std::string& ownerId, int jobId);
+ std::vector<JobRunner*> __findRunners(const std::string& ownerId);
+
+ void __restore();
+ void __release();
+ void __dump();
uid_t __uid;
- std::map<std::string, std::vector<JobRunner*>> __jobRunners;
+ std::list<JobRunner*> __jobRunners;
+
+ JobInfoDatabase __jobInfoDatabase;
};
}
using namespace ctx;
-JobRunner::JobRunner(JobInfo* jobInfo) :
+JobRunner::JobRunner(const std::string& owner, JobInfo* jobInfo) :
+ __owner(owner),
__jobInfo(jobInfo)
{
}
+const std::string& JobRunner::getOwner()
+{
+ return __owner;
+}
+
+int JobRunner::getJobId()
+{
+ return __jobInfo->getId();
+}
+
+bool JobRunner::isStarted()
+{
+ return __jobInfo->isStarted();
+}
+
+bool JobRunner::isPersistent()
+{
+ return __jobInfo->isPersistent();
+}
+
JobInfo* JobRunner::getJobInfo()
{
return __jobInfo;
}
+
+void JobRunner::start()
+{
+ __jobInfo->setStarted(true);
+}
+
+void JobRunner::stop()
+{
+ __jobInfo->setStarted(false);
+}
class JobRunner {
public:
- JobRunner(JobInfo* jobInfo);
+ JobRunner(const std::string& owner, JobInfo* jobInfo);
+
+ const std::string& getOwner();
+ int getJobId();
+ bool isStarted();
+ bool isPersistent();
JobInfo* getJobInfo();
+ void start();
+ void stop();
+
private:
+ std::string __owner;
JobInfo* __jobInfo;
};
* limitations under the License.
*/
+#include <map>
+#include <ServerUtil.h>
#include <JobSchedulerTypesPrivate.h>
#include <JobSchedulerService.h>
#include "JobManager.h"
#include "MethodCallHandler.h"
+#define ROOT_UID 0
+
using namespace ctx;
+static std::map<uid_t, JobManager*> __jobManagers;
+
JobSchedulerService::JobSchedulerService() :
__serviceRunner(NULL),
__jobManager(NULL)
bool JobSchedulerService::prepare()
{
+ __jobManagers.emplace(ROOT_UID, new JobManager(ROOT_UID));
return true;
}
void JobSchedulerService::cleanup()
{
+ for (auto& it : __jobManagers) {
+ delete it.second;
+ }
+ __jobManagers.clear();
}
void JobSchedulerService::onUserActivated(uid_t uid)
{
+ __jobManagers.emplace(uid, new JobManager(uid));
}
void JobSchedulerService::onUserDeactivated(uid_t uid)
{
+ auto it = __jobManagers.find(uid);
+ IF_FAIL_VOID_TAG(it != __jobManagers.end(), _W, "Unknown UID");
+
+ delete it->second;
+ __jobManagers.erase(it);
}
JobManager* JobSchedulerService::getJobManager(uid_t uid)
{
- // TODO
- return NULL;
+ if (util::isSystem(uid))
+ uid = ROOT_UID;
+
+ auto it = __jobManagers.find(uid);
+ IF_FAIL_RETURN_TAG(it != __jobManagers.end(), NULL, _W, "Unknown UID");
+
+ return it->second;
}
IF_FAIL_THROW(jobInfo, static_cast<int>(E_PARAM));
methodCall.reply(g_variant_new("(s)", jobInfo->serialize().c_str()));
-
- delete jobInfo;
}
void MethodCallHandler::__getAllJob(IMethodCall& methodCall)
std::vector<JobInfo*> jobInfos = __getJobManager().getAllJobInfo(__caller);
IF_FAIL_THROW(!jobInfos.empty(), static_cast<int>(E_NO_DATA));
+ _D("%u jobs found", jobInfos.size());
+
GVariantBuilder builder;
g_variant_builder_init(&builder, G_VARIANT_TYPE("as"));
for (auto& info : jobInfos) {
g_variant_builder_add(&builder, "s", info->serialize().c_str());
- delete info;
}
GVariant* jobInfoArr = g_variant_builder_end(&builder);
delete __action;
}
+bool JobInfo::operator==(const JobInfo& rhs)
+{
+ //TODO: compare two JobInfo, but ignore jobId & started flag
+ return false;
+}
+
const std::string& JobInfo::getUserData()
{
return __userData;
return *this;
}
+bool JobInfo::isPersistent()
+{
+ return __persistent;
+}
+
JobInfo& JobInfo::setRequirementTimeout(unsigned int timeoutMs)
{
__requirementTimeout = timeoutMs;
{
}
-JobInfo::Type PeriodicJobInfo::getType()
+JobInfo::Type PeriodicJobInfo::getType() const
{
return JobInfo::Type::PERIODIC;
}
}
}
-JobInfo::Type OnDemandJobInfo::getType()
+JobInfo::Type OnDemandJobInfo::getType() const
{
return JobInfo::Type::ON_DEMAND;
}
virtual ~JobInfo();
- virtual JobInfo::Type getType() = 0;
+ virtual JobInfo::Type getType() const = 0;
+
+ bool operator==(const JobInfo& rhs);
const std::string& getUserData();
JobInfo& setOneTime(bool oneTime);
JobInfo& setPersistent(bool persistent);
+ bool isPersistent();
JobInfo& setRequirementTimeout(unsigned int timeoutMs);
// UserData should be a string that can be a string element of Json
~PeriodicJobInfo();
- JobInfo::Type getType();
+ JobInfo::Type getType() const;
private:
void __toJson(Json::Value& jsonRoot);
~OnDemandJobInfo();
- JobInfo::Type getType();
+ JobInfo::Type getType() const;
OnDemandJobInfo& addTrigger(JobTrigger* trigger);
std::list<JobTrigger*>& getTriggers();