From: Mu-Woong Lee Date: Wed, 17 Feb 2016 06:20:20 +0000 (+0900) Subject: Add DBusClient & IDBusClientListener X-Git-Tag: accepted/tizen/common/20160315.220846~1^2~3 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F48%2F59648%2F2;p=platform%2Fcore%2Fapi%2Fcontext.git Add DBusClient & IDBusClientListener They replace the former request_handler implemented in context-common. Note that, request_handler is now just a wrapper between DBusClient and each API, thus it will be removed later. Change-Id: If62abfbced70cb93ad8178796f7b70035e1ec2fc Signed-off-by: Mu-Woong Lee --- diff --git a/src/DBusClient.cpp b/src/DBusClient.cpp new file mode 100644 index 0000000..e85944e --- /dev/null +++ b/src/DBusClient.cpp @@ -0,0 +1,332 @@ +/* + * Copyright (c) 2016 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 +#include +#include "DBusClient.h" + +using namespace ctx; + +static const gchar __introspection[] = + "" + " " + " " + " " + " " + " " + " " + " " + " " + ""; + +GDBusConnection *DBusClient::__connection = NULL; +GDBusNodeInfo *DBusClient::__nodeInfo = NULL; +std::atomic_int DBusClient::__instanceCount(0); +std::map DBusClient::__listenerMap; + +DBusClient::DBusClient() +{ + ++__instanceCount; +} + +DBusClient::~DBusClient() +{ + if (--__instanceCount == 0) + __release(); +} + +void DBusClient::__onMethodCalled(GDBusConnection *conn, const gchar *sender, + const gchar *path, const gchar *iface, const gchar *name, + GVariant *param, GDBusMethodInvocation *invocation, gpointer userData) +{ + IF_FAIL_VOID_TAG(STR_EQ(name, METHOD_RESPOND), _W, "Invalid method: %s", name); + + gint reqId = 0; + const gchar *subject = NULL; + gint error = 0; + const gchar *data = NULL; + + g_variant_get(param, "(i&si&s)", &reqId, &subject, &error, &data); + _D("[Response] ReqId: %d, Subject: %s, Error: %d", reqId, subject, error); + IF_FAIL_VOID_TAG(subject && data, _W, "Invalid parameter"); + + auto it = __listenerMap.find(subject); + it->second->onPublish(subject, reqId, error, data); + + g_dbus_method_invocation_return_value(invocation, NULL); +} + +bool DBusClient::__init() +{ + static GMutex mutex; + ScopeMutex sm(&mutex); + + if (__connection) + return true; + + GError *gerr = NULL; + gchar *addr = NULL; + GDBusInterfaceVTable vtable; + guint regId; + + __nodeInfo = g_dbus_node_info_new_for_xml(__introspection, NULL); + IF_FAIL_RETURN_TAG(__nodeInfo != NULL, false, _E, "Initialization failed"); + + addr = g_dbus_address_get_for_bus_sync(G_BUS_TYPE_SESSION, NULL, &gerr); + HANDLE_GERROR(gerr); + IF_FAIL_CATCH_TAG(addr != NULL, _E, "Getting address failed"); + _SD("Address: %s", addr); + + __connection = g_dbus_connection_new_for_address_sync(addr, + (GDBusConnectionFlags)(G_DBUS_CONNECTION_FLAGS_AUTHENTICATION_CLIENT | G_DBUS_CONNECTION_FLAGS_MESSAGE_BUS_CONNECTION), + NULL, NULL, &gerr); + g_free(addr); + HANDLE_GERROR(gerr); + IF_FAIL_CATCH_TAG(__connection != NULL, _E, "Connection failed"); + + vtable.method_call = __onMethodCalled; + vtable.get_property = NULL; + vtable.set_property = NULL; + + regId = g_dbus_connection_register_object(__connection, DBUS_PATH, + __nodeInfo->interfaces[0], &vtable, NULL, NULL, &gerr); + HANDLE_GERROR(gerr); + IF_FAIL_CATCH_TAG(regId>0, _E, "Object registration failed"); + + _I("DBus connection established"); + _D("DBus name: %s", g_dbus_connection_get_unique_name(__connection)); + return true; + +CATCH: + __release(); + return false; +} + +void DBusClient::__release() +{ + if (__connection) { + g_dbus_connection_flush_sync(__connection, NULL, NULL); + g_dbus_connection_close_sync(__connection, NULL, NULL); + g_object_unref(__connection); + __connection = NULL; + } + + if (__nodeInfo) { + g_dbus_node_info_unref(__nodeInfo); + __nodeInfo = NULL; + } + + _I("DBus connection released"); +} + +int DBusClient::__request(int type, int reqId, const char *subject, const char *input, + std::string *result, std::string *outputData) +{ + _D("Requesting: %d, %d, %s", type, reqId, subject); + + if (input == NULL) + input = EMPTY_JSON_OBJECT; + + /* FIXME: the second param is the security cookie, which is deprected in 3.0. + * We need to completely REMOVE this parameter from the dbus protocol. */ + GVariant *param = g_variant_new("(isiss)", type, "", reqId, subject, input); + IF_FAIL_RETURN_TAG(param, ERR_OUT_OF_MEMORY, _E, "Memory allocation failed"); + + GError *err = NULL; + GVariant *response = g_dbus_connection_call_sync(__connection, DBUS_DEST, DBUS_PATH, DBUS_IFACE, + METHOD_REQUEST, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, &err); + HANDLE_GERROR(err); + IF_FAIL_RETURN_TAG(response, ERR_OPERATION_FAILED, _E, "Method call failed"); + + gint error = ERR_OPERATION_FAILED; + const gchar *resultStr = NULL; + const gchar *dataStr = NULL; + + g_variant_get(response, "(i&s&s)", &error, &resultStr, &dataStr); + + if (result && resultStr) + *result = resultStr; + + if (outputData && dataStr) + *outputData = dataStr; + + g_variant_unref(response); + + return error; +} + +int DBusClient::__request(int type, int reqId, const char* subject, const char* input) +{ + _D("Requesting: %d, %d, %s", type, reqId, subject); + + if (input == NULL) + input = EMPTY_JSON_OBJECT; + + /* FIXME: the second param is the security cookie, which is deprected in 3.0. + * We need to completely REMOVE this parameter from the dbus protocol. */ + GVariant *param = g_variant_new("(isiss)", type, "", reqId, subject, input); + IF_FAIL_RETURN_TAG(param, ERR_OUT_OF_MEMORY, _E, "Memory allocation failed"); + + GError *err = NULL; + g_dbus_connection_call(__connection, DBUS_DEST, DBUS_PATH, DBUS_IFACE, + METHOD_REQUEST, param, NULL, G_DBUS_CALL_FLAGS_NONE, DBUS_TIMEOUT, NULL, NULL, &err); + + if (err) { + HANDLE_GERROR(err); + return ERR_OPERATION_FAILED; + } + + return ERR_NONE; +} + +int DBusClient::__generateReqId() +{ + static GMutex mutex; + static int reqId = 0; + + ctx::ScopeMutex sm(&mutex); + + /* Overflow handling */ + if (++reqId < 0) + reqId = 1; + + return reqId; +} + +void DBusClient::addListener(std::string subject, IDBusClientListener *listener) +{ + _D("Registering the listener for '%s'", subject.c_str()); + + static GMutex mutex; + ctx::ScopeMutex sm(&mutex); + + __listenerMap[subject] = listener; +} + +int DBusClient::isSupported(std::string subject) +{ + IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED); + + return __request(REQ_SUPPORT, __generateReqId(), subject.c_str(), NULL, NULL, NULL); +} + +int DBusClient::subscribe(std::string subject, json option, int *reqId, json *result) +{ + IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED); + ASSERT_NOT_NULL(reqId); + + *reqId = __generateReqId(); + + _I("[Subscribe] ReqId: %d, Subject: %s", *reqId, subject.c_str()); + + std::string resultStr; + int error = __request(REQ_SUBSCRIBE, *reqId, subject.c_str(), option.str().c_str(), &resultStr, NULL); + + if (result) + *result = resultStr; + + _D("Error: %#x", error); + _SD("Result: %s", resultStr.c_str()); + + return error; +} + +int DBusClient::unsubscribe(std::string subject, int reqId) +{ + IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED); + + _I("[Unsubscribe] ReqId: %d, Subject: %s", reqId, subject.c_str()); + + return __request(REQ_UNSUBSCRIBE, reqId, subject.c_str(), NULL, NULL, NULL); +} + +int DBusClient::read(std::string subject, json option, int *reqId, json *result) +{ + IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED); + ASSERT_NOT_NULL(reqId); + + *reqId = __generateReqId(); + + _I("[Read] ReqId: %d, Subject: %s", *reqId, subject.c_str()); + + std::string resultStr; + int error = __request(REQ_READ, *reqId, subject.c_str(), option.str().c_str(), &resultStr, NULL); + + if (result) + *result = resultStr; + + _D("Error: %#x", error); + _SD("Result: %s", resultStr.c_str()); + + return error; +} + +int DBusClient::readSync(std::string subject, json option, int *reqId, json *outputData) +{ + IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED); + ASSERT_NOT_NULL(reqId); + ASSERT_NOT_NULL(outputData); + + *reqId = __generateReqId(); + + _I("[ReadSync] ReqId: %d, Subject: %s", *reqId, subject.c_str()); + + std::string output; + int error = __request(REQ_READ_SYNC, *reqId, subject.c_str(), option.str().c_str(), NULL, &output); + + *outputData = output; + + _D("Error: %#x", error); + _SD("Data: %s", output.c_str()); + + return error; +} + +int DBusClient::write(std::string subject, json inputData) +{ + IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED); + + int reqId = __generateReqId(); + + _I("[Write] ReqId: %d, Subject: %s", reqId, subject.c_str()); + _SD("Data: %s", inputData.str().c_str()); + + int error = __request(REQ_WRITE, reqId, subject.c_str(), inputData.str().c_str()); + _D("Error: %#x", error); + + return error; +} + +int DBusClient::write(std::string subject, json inputData, json *result) +{ + IF_FAIL_RETURN(__init(), ERR_OPERATION_FAILED); + + int reqId = __generateReqId(); + + _I("[Write with reply] ReqId: %d, Subject: %s", reqId, subject.c_str()); + _SD("Data: %s", inputData.str().c_str()); + + std::string resultStr; + int error = __request(REQ_WRITE, reqId, subject.c_str(), inputData.str().c_str(), &resultStr, NULL); + + if (result) + *result = resultStr; + + _D("Error: %#x", error); + _SD("Result: %s", resultStr.c_str()); + + return error; +} diff --git a/src/DBusClient.h b/src/DBusClient.h new file mode 100644 index 0000000..4078dbc --- /dev/null +++ b/src/DBusClient.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2016 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_DBUS_CLIENT_H_ +#define _CONTEXT_DBUS_CLIENT_H_ + +#include +#include +#include +#include +#include +#include "IDBusClientListener.h" + +namespace ctx { + + class DBusClient { + public: + DBusClient(); + ~DBusClient(); + + void addListener(std::string subject, IDBusClientListener *listener); + + int isSupported(std::string subject); + + int subscribe(std::string subject, json option, int *reqId, json *result); + int unsubscribe(std::string subject, int reqId); + int read(std::string subject, json option, int *reqId, json *result); + int readSync(std::string subject, json option, int *reqId, json *outputData); + int write(std::string subject, json inputData); + int write(std::string subject, json inputData, json *result); + + private: + static void __onMethodCalled(GDBusConnection *conn, const gchar *sender, + const gchar *path, const gchar *iface, const gchar *name, + GVariant *param, GDBusMethodInvocation *invocation, gpointer userData); + + bool __init(); + void __release(); + int __request(int type, int reqId, const char *subject, const char *input, + std::string *result, std::string *outputData); + int __request(int type, int reqId, const char *subject, const char *input); + int __generateReqId(); + + + static GDBusConnection *__connection; + static GDBusNodeInfo *__nodeInfo; + static std::atomic_int __instanceCount; + static std::map __listenerMap; + + }; /* class ctx::DBusClient */ + +} /* namespace ctx */ + +#endif // _CONTEXT_DBUS_CLIENT_H_ diff --git a/src/IDBusClientListener.h b/src/IDBusClientListener.h new file mode 100644 index 0000000..69bd0d2 --- /dev/null +++ b/src/IDBusClientListener.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2016 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_I_DBUS_CLIENT_LISTENER_H_ +#define _CONTEXT_I_DBUS_CLIENT_LISTENER_H_ + +#include +#include + +namespace ctx { + + class IDBusClientListener { + public: + virtual ~IDBusClientListener() {} + virtual void onPublish(std::string subject, int reqId, int error, json event) = 0; + }; + +} /* namespace ctx */ + +#endif /* End of _CONTEXT_I_DBUS_CLIENT_LISTENER_H_ */ diff --git a/src/context_history.cpp b/src/context_history.cpp index eea980e..d418690 100644 --- a/src/context_history.cpp +++ b/src/context_history.cpp @@ -16,9 +16,9 @@ #include #include -#include #include #include +#include "request_handler.h" #define TYPE_INT 0 #define TYPE_STRING 1 diff --git a/src/context_trigger.cpp b/src/context_trigger.cpp index fe03b69..606e3fa 100644 --- a/src/context_trigger.cpp +++ b/src/context_trigger.cpp @@ -25,7 +25,7 @@ #include #include #include -#include +#include "request_handler.h" #include "rule_validator.h" #include "priv_util.h" diff --git a/src/request_handler.cpp b/src/request_handler.cpp new file mode 100644 index 0000000..920ee23 --- /dev/null +++ b/src/request_handler.cpp @@ -0,0 +1,94 @@ +/* + * Copyright (c) 2016 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. + */ + +/* TODO: Remove this request_handler. All APIs and the testsuite should use DBusClient directly. */ + +#include +#include + +#include +#include +#include "DBusClient.h" +#include "request_handler.h" + +using namespace ctx; + +class DBusClientListenerImpl : public IDBusClientListener { +public: + DBusClientListenerImpl() {} + ~DBusClientListenerImpl() {} + void setCb(std::string subject, request_handler::subject_response_cb cb); + void onPublish(std::string subject, int reqId, int error, json event); +private: + std::map __callbackMap; +}; + +void DBusClientListenerImpl::setCb(std::string subject, request_handler::subject_response_cb cb) +{ + __callbackMap[subject] = cb; +} + +void DBusClientListenerImpl::onPublish(std::string subject, int reqId, int error, json event) +{ + auto it = __callbackMap.find(subject); + IF_FAIL_VOID_TAG(it != __callbackMap.end(), _W, "Unregistered subject"); + it->second(subject.c_str(), reqId, error, event); +} + +static DBusClientListenerImpl __dbusListener; +static DBusClient __dbusClient; + +EXTAPI int ctx::request_handler::subscribe(const char* subject, ctx::json* option, int* req_id, ctx::json* request_result) +{ + return __dbusClient.subscribe(subject, option ? *option : NULL, req_id, request_result); +} + +EXTAPI int ctx::request_handler::unsubscribe(const char* subject, int req_id) +{ + return __dbusClient.unsubscribe(subject, req_id); +} + +EXTAPI int ctx::request_handler::read(const char* subject, ctx::json* option, int* req_id, ctx::json* request_result) +{ + return __dbusClient.read(subject, option ? *option : NULL, req_id, request_result); +} + +EXTAPI int ctx::request_handler::read_sync(const char* subject, ctx::json* option, int* req_id, ctx::json* data_read) +{ + return __dbusClient.readSync(subject, option ? *option : NULL, req_id, data_read); +} + +EXTAPI int ctx::request_handler::write(const char* subject, ctx::json* data) +{ + return __dbusClient.write(subject, *data); +} + +EXTAPI int ctx::request_handler::write_with_reply(const char* subject, ctx::json* data, ctx::json* request_result) +{ + return __dbusClient.write(subject, *data, request_result); +} + +EXTAPI int ctx::request_handler::is_supported(const char* subject) +{ + return __dbusClient.isSupported(subject); +} + +EXTAPI bool ctx::request_handler::register_callback(const char* subject, subject_response_cb callback) +{ + __dbusListener.setCb(subject, callback); + __dbusClient.addListener(subject, &__dbusListener); + return true; +} diff --git a/src/request_handler.h b/src/request_handler.h new file mode 100644 index 0000000..d90f6f1 --- /dev/null +++ b/src/request_handler.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2016 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_LIB_REQUEST_HANDLER_H__ +#define __CONTEXT_LIB_REQUEST_HANDLER_H__ + +#include + +namespace ctx { namespace request_handler { + + /* + */ + typedef void(* subject_response_cb)(const char* subject, int req_id, int error, ctx::json response); + + /* + */ + bool register_callback(const char* subject, subject_response_cb callback); + + /* + */ + int subscribe(const char* subject, ctx::json* option, int* req_id, ctx::json* request_result = NULL); + + /* + */ + int unsubscribe(const char* subject, int req_id); + + /* + */ + int read(const char* subject, ctx::json* option, int* req_id, ctx::json* request_result = NULL); + + /* + */ + int read_sync(const char* subject, ctx::json* option, int* req_id, ctx::json* data_read); + + /* + */ + int write(const char* subject, ctx::json* data); + + /* + */ + int write_with_reply(const char* subject, ctx::json* data, ctx::json* request_result = NULL); + + /* + */ + int is_supported(const char* subject); + +} } /* namespace ctx::request_handler */ + +#endif // __CONTEXT_LIB_REQUEST_HANDLER_H__ diff --git a/src/rule_validator.cpp b/src/rule_validator.cpp index f1a1f8a..f4bb504 100644 --- a/src/rule_validator.cpp +++ b/src/rule_validator.cpp @@ -20,7 +20,7 @@ #include #include #include -#include +#include "request_handler.h" #include "rule_validator.h" #define KEY_TEMPLATE "templates" diff --git a/testsuite/CMakeLists.txt b/testsuite/CMakeLists.txt index 65981a5..30e85d2 100644 --- a/testsuite/CMakeLists.txt +++ b/testsuite/CMakeLists.txt @@ -4,6 +4,9 @@ SET(TEST_EXEC context-test) # Source List FILE(GLOB_RECURSE TEST_SRCS src/*.cpp) +INCLUDE_DIRECTORIES( + ${CMAKE_SOURCE_DIR}/src +) ADD_EXECUTABLE(${TEST_EXEC} ${TEST_SRCS}) TARGET_LINK_LIBRARIES(${TEST_EXEC} ${api_pkg_LDFLAGS} ${target} -pie)