diff options
Diffstat (limited to 'binding/src')
-rw-r--r-- | binding/src/CMakeLists.txt | 36 | ||||
-rw-r--r-- | binding/src/binding-api.c | 65 | ||||
-rw-r--r-- | binding/src/binding-api.h | 49 | ||||
-rw-r--r-- | binding/src/verbs.cpp | 172 |
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); + } +} |