diff options
Diffstat (limited to 'src/afb-svc.c')
-rw-r--r-- | src/afb-svc.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/src/afb-svc.c b/src/afb-svc.c new file mode 100644 index 00000000..03ff4b84 --- /dev/null +++ b/src/afb-svc.c @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2016 "IoT.bzh" + * Author: José Bollo <jose.bollo@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. + */ + +#define _GNU_SOURCE + +#include <stdlib.h> + +#include <json-c/json.h> + +#include <afb/afb-req-itf.h> +#include <afb/afb-service-itf.h> + +#include "session.h" +#include "afb-context.h" +#include "afb-evt.h" +#include "afb-subcall.h" +#include "afb-svc.h" + +/* + * Structure for recording service + */ +struct afb_svc +{ + /* session of the service */ + struct AFB_clientCtx *session; + + /* event listener of the service or NULL */ + struct afb_evt_listener *listener; + + /* on event callback for the service */ + void (*on_event)(const char *event, struct json_object *object); +}; + +/* + * Structure for requests initiated by the service + */ +struct svc_req +{ + /* + * CAUTION: 'context' field should be the first because there + * is an implicit convertion to struct afb_context + */ + struct afb_context context; + + /* the service */ + struct afb_svc *svc; + + /* the count of references to the request */ + int refcount; +}; + +/* functions for services */ +static void svc_on_event(struct afb_svc *svc, const char *event, struct json_object *object); +static void svc_call(struct afb_svc *svc, const char *api, const char *verb, struct json_object *args, + void (*callback)(void*, int, struct json_object*), void *closure); + +/* the interface for services */ +static const struct afb_service_itf service_itf = { + .call = (void*)svc_call +}; + +/* functions for requests of services */ +static void svcreq_addref(struct svc_req *svcreq); +static void svcreq_unref(struct svc_req *svcreq); +static int svcreq_subscribe(struct svc_req *svcreq, struct afb_event event); +static int svcreq_unsubscribe(struct svc_req *svcreq, struct afb_event event); +static void svcreq_subcall(struct svc_req *svcreq, const char *api, const char *verb, struct json_object *args, + void (*callback)(void*, int, struct json_object*), void *closure); + +/* interface for requests of services */ +const struct afb_req_itf afb_svc_req_itf = { + .addref = (void*)svcreq_addref, + .unref = (void*)svcreq_unref, + .context_get = (void*)afb_context_get, + .context_set = (void*)afb_context_set, + .session_close = (void*)afb_context_close, + .session_set_LOA = (void*)afb_context_change_loa, + .subscribe = (void*)svcreq_subscribe, + .unsubscribe = (void*)svcreq_unsubscribe, + .subcall = (void*)svcreq_subcall +}; + +/* the common session for services sahring their session */ +static struct AFB_clientCtx *common_session; + +/* + * Creates a new service + */ +struct afb_svc *afb_svc_create(int share_session, int (*init)(struct afb_service service), void (*on_event)(const char *event, struct json_object *object)) +{ + int rc; + struct afb_svc *svc; + + /* allocates the svc handler */ + svc = malloc(sizeof * svc); + if (svc == NULL) + goto error; + + /* instanciate the session */ + if (share_session) { + /* session shared with other svcs */ + if (common_session == NULL) { + common_session = ctxClientCreate (NULL, 0); + if (common_session == NULL) + goto error2; + } + svc->session = ctxClientAddRef(common_session); + } else { + /* session dedicated to the svc */ + svc->session = ctxClientCreate (NULL, 0); + if (svc->session == NULL) + goto error2; + } + + /* initialises the listener if needed */ + if (on_event == NULL) + svc->listener = NULL; + else { + svc->listener = afb_evt_listener_create((void*)svc_on_event, svc); + if (svc->listener == NULL) + goto error3; + } + + /* initialises the svc now */ + rc = init((struct afb_service){ .itf = &service_itf, .closure = svc }); + if (rc < 0) + goto error4; + + return svc; + +error4: + if (svc->listener != NULL) + afb_evt_listener_unref(svc->listener); +error3: + ctxClientUnref(svc->session); +error2: + free(svc); +error: + return NULL; +} + +/* + * Propagates the event to the service + */ +static void svc_on_event(struct afb_svc *svc, const char *event, struct json_object *object) +{ + svc->on_event(event, object); +} + +/* + * Initiates a call for the service + */ +static void svc_call(struct afb_svc *svc, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *closure) +{ + struct svc_req *svcreq; + + /* allocates the request */ + svcreq = malloc(sizeof *svcreq); + if (svcreq == NULL) + return afb_subcall_internal_error(callback, closure); + + /* initialises the request */ + afb_context_init(&svcreq->context, svc->session, NULL); + svcreq->context.validated = 1; + svcreq->svc = svc; + svcreq->refcount = 1; + + /* makes the call */ + afb_subcall(&svcreq->context, api, verb, args, callback, closure, (struct afb_req){ .itf = &afb_svc_req_itf, .closure = svcreq }); + + /* terminates and frees ressources if needed */ + svcreq_unref(svcreq); +} + +static void svcreq_addref(struct svc_req *svcreq) +{ + svcreq->refcount++; +} + +static void svcreq_unref(struct svc_req *svcreq) +{ + if (0 == --svcreq->refcount) { + afb_context_disconnect(&svcreq->context); + free(svcreq); + } +} + +static int svcreq_subscribe(struct svc_req *svcreq, struct afb_event event) +{ + if (svcreq->svc->listener == NULL) + return -1; + return afb_evt_add_watch(svcreq->svc->listener, event); +} + +static int svcreq_unsubscribe(struct svc_req *svcreq, struct afb_event event) +{ + if (svcreq->svc->listener == NULL) + return -1; + return afb_evt_remove_watch(svcreq->svc->listener, event); +} + +static void svcreq_subcall(struct svc_req *svcreq, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *closure) +{ + afb_subcall(&svcreq->context, api, verb, args, callback, closure, (struct afb_req){ .itf = &afb_svc_req_itf, .closure = svcreq }); +} + |