summaryrefslogtreecommitdiffstats
path: root/binding/src
diff options
context:
space:
mode:
Diffstat (limited to 'binding/src')
-rw-r--r--binding/src/CMakeLists.txt36
-rw-r--r--binding/src/binding-api.c65
-rw-r--r--binding/src/binding-api.h49
-rw-r--r--binding/src/verbs.cpp172
4 files changed, 322 insertions, 0 deletions
diff --git a/binding/src/CMakeLists.txt b/binding/src/CMakeLists.txt
new file mode 100644
index 0000000..0ac597f
--- /dev/null
+++ b/binding/src/CMakeLists.txt
@@ -0,0 +1,36 @@
+###########################################################################
+# Copyright 2015, 2016, 2017 IoT.bzh
+#
+# author: Fulup Ar Foll <fulup@iot.bzh>
+# contrib: Romain Forlot <romain.forlot@iot.bzh>
+#
+# 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.
+###########################################################################
+
+# Add target to project dependency list
+PROJECT_TARGET_ADD(afm-poi-binding)
+
+ # Define project Targets
+ add_library(afm-poi-binding MODULE binding-api.h binding-api.c verbs.cpp)
+
+ # Binder exposes a unique public entry point
+ SET_TARGET_PROPERTIES(${TARGET_NAME} PROPERTIES
+ PREFIX "lib"
+ LABELS "BINDING"
+ LINK_FLAGS ${BINDINGS_LINK_FLAG}
+ OUTPUT_NAME ${TARGET_NAME}
+ )
+
+ # Library dependencies (include updates automatically)
+ TARGET_LINK_LIBRARIES(${TARGET_NAME} ${link_libraries})
+
diff --git a/binding/src/binding-api.c b/binding/src/binding-api.c
new file mode 100644
index 0000000..5221cda
--- /dev/null
+++ b/binding/src/binding-api.c
@@ -0,0 +1,65 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "binding-api.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+
+const afb_verb_t verbs[] = {
+ { .verb = POIAPI_BINDING_VERB_SEND_REQUEST, .callback = send_request },
+ { .verb = POIAPI_BINDING_VERB_REQUEST_PROCESSED, .callback = request_processed },
+ { .verb = POIAPI_BINDING_VERB_SUBSCRIBE_REQUESTS, .callback = subscribe_requests },
+ { .verb = POIAPI_BINDING_VERB_UNSUBSCRIBE_REQUESTS, .callback = unsubscribe_requests },
+ { .verb = POIAPI_BINDING_VERB_SUBSCRIBE_RESPONSES, .callback = subscribe_responses },
+ { .verb = POIAPI_BINDING_VERB_UNSUBSCRIBE_RESPONSES, .callback = unsubscribe_responses },
+ { .verb = NULL }
+};
+
+afb_event_t gEventRequestReceived;
+afb_event_t gEventResponseReceived;
+
+int init(afb_api_t api)
+{
+ gEventRequestReceived = afb_api_make_event(api, POIAPI_BINDING_EVENT_REQUEST_RECEIVED);
+ gEventResponseReceived = afb_api_make_event(api, POIAPI_BINDING_EVENT_RESPONSE_RECEIVED);
+ if (afb_event_is_valid(gEventRequestReceived) && afb_event_is_valid(gEventResponseReceived)) {
+
+ json_object *args = json_object_new_object();
+ // TODO Set Version
+ json_object *appid = json_object_new_string("poi-service@0.1");
+ json_object_object_add(args, "id", appid);
+
+ char *err;
+ char *info;
+ json_object *response;
+
+ // Launch poiservice
+ afb_api_call_sync(api, "afm-main", "start", args, &response, &err, &info);
+
+ if (err) {
+ AFB_API_ERROR(api, "ERROR %s", err);
+ free(err);
+ }
+ if (info) {
+ AFB_API_INFO(api, "INFO %s", info);
+ free(info);
+ }
+
+ json_object_put(response);
+
+ return 0;
+ }
+ else {
+ AFB_API_ERROR(api, "Can't create events");
+ return -1;
+ }
+}
+
+const afb_binding_t afbBindingExport = {
+ .api = POIAPI_BINDING_API_NAME,
+ .verbs = verbs,
+ .init = init,
+ .noconcurrency = 0
+};
diff --git a/binding/src/binding-api.h b/binding/src/binding-api.h
new file mode 100644
index 0000000..e274f9a
--- /dev/null
+++ b/binding/src/binding-api.h
@@ -0,0 +1,49 @@
+#pragma once
+
+#ifndef AFB_BINDING_VERSION
+#define AFB_BINDING_VERSION 3
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <afb/afb-binding.h>
+
+extern afb_event_t gEventRequestReceived;
+extern afb_event_t gEventResponseReceived;
+
+// Verbs
+extern void send_request(afb_req_t req);
+extern void request_processed(afb_req_t req);
+extern void subscribe_requests(afb_req_t req);
+extern void unsubscribe_requests(afb_req_t req);
+extern void subscribe_responses(afb_req_t req);
+extern void unsubscribe_responses(afb_req_t req);
+
+#define POIAPI_BINDING_API_NAME "poi"
+
+#define POIAPI_BINDING_VERB_SEND_REQUEST "send-request"
+#define POIAPI_BINDING_VERB_REQUEST_PROCESSED "request-processed"
+#define POIAPI_BINDING_VERB_SUBSCRIBE_REQUESTS "subscribe-requests"
+#define POIAPI_BINDING_VERB_UNSUBSCRIBE_REQUESTS "unsubscribe-requests"
+#define POIAPI_BINDING_VERB_SUBSCRIBE_RESPONSES "subscribe-responses"
+#define POIAPI_BINDING_VERB_UNSUBSCRIBE_RESPONSES "unsubscribe-responses"
+
+#define POIAPI_BINDING_EVENT_REQUEST_RECEIVED "reqest-received-event"
+#define POIAPI_BINDING_EVENT_RESPONSE_RECEIVED "response-received-event"
+
+// Some definitions is not used in this project
+
+#define POIAPI_BINDING_PARAMETER_KEY_REQUEST_ID "request-id"
+#define POIAPI_BINDING_PARAMETER_KEY_REQUEST_OBJECT "request-object"
+#define POIAPI_BINDING_PARAMETER_KEY_RESPONSE_OBJECT "response-object"
+
+#define POIAPI_BINDING_REQUEST_OBJECT_KEY_REQUEST_TYPE "request-type"
+#define POIAPI_BINDING_REQUEST_OBJECT_KEY_REQUEST_PARAMETER "request-parameter"
+
+#define POIAPI_BINDING_REQUEST_TYPE_EXTERNAL_ONLINE_REQUEST_HTTP_GET_JSON "external-online-request-http-get-json"
+
+#ifdef __cplusplus
+} // extern "C"
+#endif
diff --git a/binding/src/verbs.cpp b/binding/src/verbs.cpp
new file mode 100644
index 0000000..f71698a
--- /dev/null
+++ b/binding/src/verbs.cpp
@@ -0,0 +1,172 @@
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include "binding-api.h"
+
+#include <map>
+#include <mutex>
+#include <string>
+#include <stdexcept>
+
+extern "C" {
+// json-c/json.h
+#include <json.h>
+}
+
+static std::mutex mut;
+static int32_t gRequestId = 0;
+static std::map<decltype(gRequestId), json_object *> gRequests;
+
+static inline bool pushRequest(json_object *reqJson) {
+ // Guard `gRequests' and `gRequestId'
+ std::lock_guard<std::mutex> lock(mut);
+
+ gRequestId++;
+
+ json_object *reqToSvc = json_object_new_object();
+ json_object_object_add(reqToSvc, POIAPI_BINDING_PARAMETER_KEY_REQUEST_ID, json_object_new_string(std::to_string(gRequestId).c_str()));
+
+ // reqJson(1), reqToSvc(1)
+ // Associate `reqJson' to `reqToSvc' but the refcount of `reqJson' is not incremented
+ json_object_object_add(reqToSvc, POIAPI_BINDING_PARAMETER_KEY_REQUEST_OBJECT, reqJson);
+
+ // reqJson(1->2->1), reqToSvc(1->2->1)
+ // Each refcount of `reqJson' or `reqToSvc' is decrement, and we increment the refcounts before the decrementations
+ int rc = afb_event_push(gEventRequestReceived, json_object_get(reqToSvc));
+ if (rc == -1 || rc == 0) { // Discard the request if the event was not pushed or there are no subscriber
+ // reqJson(1->2), reqToSvc(1->1)
+ json_object_get(reqJson);
+ // reqJson(2->1), reqToSvc(1->0)
+ // Delete reqToSvc
+ json_object_put(reqToSvc);
+ return false;
+ }
+
+ // reqJson(1->2), reqToSvc(1)
+ // Increment the refcount of `refJson' to preserve `reqToSvc'.
+ // The refcount of reqJson will be decremented in afb_req_success
+ json_object_get(reqJson);
+ gRequests.emplace(gRequestId, reqToSvc);
+
+ return true;
+}
+
+// Verb: request-processed
+void send_request(afb_req_t req)
+{
+ json_object *reqJson = afb_req_json(req);
+ if (!pushRequest(reqJson)) {
+ AFB_REQ_ERROR(req, "Failed to push an event");
+ // reqJson(1->0)
+ afb_req_fail(req, "error", "Failed to push an event");
+ return;
+ }
+
+ json_object *response = json_object_new_object();
+ json_object_object_add(response, POIAPI_BINDING_PARAMETER_KEY_REQUEST_ID, json_object_new_string(std::to_string(gRequestId).c_str()));
+
+ // `response' will be released
+ // The refcount of `reqJson' will be 1; `reqJson' belonges to a element of `gRequests'
+ afb_req_success(req, response, POIAPI_BINDING_VERB_SEND_REQUEST);
+}
+
+// Verb: request-processed
+void request_processed(afb_req_t req)
+{
+ json_object *resultJson = afb_req_json(req);
+ json_object *ridj;
+ json_object_object_get_ex(resultJson, POIAPI_BINDING_PARAMETER_KEY_REQUEST_ID, &ridj);
+ if (!json_object_is_type(ridj, json_type_string)) {
+ AFB_REQ_ERROR(req, "Received response was bad formatted: %s", json_object_to_json_string_ext(afb_req_json(req), JSON_C_TO_STRING_PRETTY));
+ afb_req_fail(req, "error", "Received response was badly formatted");
+ return;
+ }
+
+ const char *rids = json_object_get_string(ridj);
+ uint32_t rid;
+ try {
+ rid = std::stoi(rids);
+ }
+ catch (const std::invalid_argument &e) {
+ AFB_REQ_ERROR(req, "Received response was bad formatted: %s", json_object_to_json_string_ext(afb_req_json(req), JSON_C_TO_STRING_PRETTY));
+ afb_req_fail(req, "error", "Received response was badly formatted");
+ return;
+ }
+ catch (const std::out_of_range &e) {
+ AFB_REQ_ERROR(req, "Received response was bad formatted: %s", json_object_to_json_string_ext(afb_req_json(req), JSON_C_TO_STRING_PRETTY));
+ afb_req_fail(req, "error", "Received response was badly formatted");
+ return;
+ }
+
+ decltype(gRequests)::iterator it = gRequests.find(rid);
+ if (it == gRequests.end()) {
+ AFB_REQ_ERROR(req, "Received an invalid request id: %d", rid);
+ afb_req_fail_f(req, "error", "Received an invalid request id: %d %s", rid, rids);
+ return;
+ }
+
+ // Preserve `resultJson' because afb_req_success use it
+ if (afb_event_push(gEventResponseReceived, json_object_get(resultJson)) == -1) {
+ AFB_REQ_ERROR(req, "Failed to push an event");
+ afb_req_fail(req, "error", "Failed to push an event");
+ return;
+ }
+
+ afb_req_success(req, nullptr, nullptr);
+
+ // Guard `gRequests'
+ std::lock_guard<std::mutex> lock(mut);
+
+ // Delete the processed request data
+ json_object *processedReq = it->second;
+ gRequests.erase(it);
+ int rc = json_object_put(processedReq);
+ if (rc != 1) {
+ AFB_REQ_ERROR(req, "bad-ref-count %d:%s", rc, POIAPI_BINDING_VERB_REQUEST_PROCESSED);
+ }
+}
+
+// Verb: subscribe-requests
+void subscribe_requests(afb_req_t req)
+{
+ if (afb_req_subscribe(req, gEventRequestReceived) == 0) {
+ afb_req_success(req, nullptr, nullptr);
+ } else {
+ AFB_REQ_ERROR(req, "Failed to %s", POIAPI_BINDING_VERB_SUBSCRIBE_REQUESTS);
+ afb_req_fail_f(req, "error", "Failed to %s", POIAPI_BINDING_VERB_SUBSCRIBE_REQUESTS);
+ }
+}
+
+// Verb: unsubscribe-requests
+void unsubscribe_requests(afb_req_t req)
+{
+ if (afb_req_unsubscribe(req, gEventRequestReceived) == 0) {
+ afb_req_success(req, nullptr, nullptr);
+ } else {
+ AFB_REQ_ERROR(req, "Failed to %s", POIAPI_BINDING_VERB_UNSUBSCRIBE_REQUESTS);
+ afb_req_fail_f(req, "error", "Failed to %s", POIAPI_BINDING_VERB_UNSUBSCRIBE_REQUESTS);
+ }
+}
+
+// Verb: subscribe-responses
+void subscribe_responses(afb_req_t req)
+{
+ if (afb_req_subscribe(req, gEventResponseReceived) == 0) {
+ afb_req_success(req, nullptr, nullptr);
+ } else {
+ AFB_REQ_ERROR(req, "Failed to %s", POIAPI_BINDING_VERB_SUBSCRIBE_RESPONSES);
+ afb_req_fail_f(req, "error", "Failed to %s", POIAPI_BINDING_VERB_SUBSCRIBE_RESPONSES);
+ }
+}
+
+// Verb: unsubscribe-responses
+void unsubscribe_responses(afb_req_t req)
+{
+ if (afb_req_unsubscribe(req, gEventResponseReceived) == 0) {
+ afb_req_success(req, nullptr, nullptr);
+ } else {
+ AFB_REQ_ERROR(req, "Failed to %s", POIAPI_BINDING_VERB_UNSUBSCRIBE_RESPONSES);
+ afb_req_fail_f(req, "error", "Failed to %s", POIAPI_BINDING_VERB_UNSUBSCRIBE_RESPONSES);
+ }
+}