Optimisation of xreq
[src/app-framework-binder.git] / src / afb-svc.c
1 /*
2  * Copyright (C) 2016, 2017 "IoT.bzh"
3  * Author: José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21
22 #include <json-c/json.h>
23
24 #include <afb/afb-req-itf.h>
25 #include <afb/afb-service-itf.h>
26
27 #include "afb-session.h"
28 #include "afb-context.h"
29 #include "afb-evt.h"
30 #include "afb-msg-json.h"
31 #include "afb-svc.h"
32 #include "afb-xreq.h"
33 #include "afb-apis.h"
34 #include "verbose.h"
35
36 /*
37  * Structure for recording service
38  */
39 struct afb_svc
40 {
41         /* session of the service */
42         struct afb_session *session;
43
44         /* event listener of the service or NULL */
45         struct afb_evt_listener *listener;
46
47         /* on event callback for the service */
48         void (*on_event)(const char *event, struct json_object *object);
49 };
50
51 /*
52  * Structure for requests initiated by the service
53  */
54 struct svc_req
55 {
56         struct afb_xreq xreq;
57
58         /* the args */
59         void (*callback)(void*, int, struct json_object*);
60         void *closure;
61 };
62
63 /* functions for services */
64 static void svc_on_event(void *closure, const char *event, int eventid, struct json_object *object);
65 static void svc_call(void *closure, const char *api, const char *verb, struct json_object *args,
66                                 void (*callback)(void*, int, struct json_object*), void *cbclosure);
67
68 /* the interface for services */
69 static const struct afb_service_itf service_itf = {
70         .call = svc_call
71 };
72
73 /* the interface for events */
74 static const struct afb_evt_itf evt_itf = {
75         .broadcast = svc_on_event,
76         .push = svc_on_event
77 };
78
79 /* functions for requests of services */
80 static void svcreq_destroy(struct afb_xreq *xreq);
81 static void svcreq_reply(struct afb_xreq *xreq, int iserror, json_object *obj);
82
83 /* interface for requests of services */
84 const struct afb_xreq_query_itf afb_svc_xreq_itf = {
85         .unref = svcreq_destroy,
86         .reply = svcreq_reply
87 };
88
89 /* the common session for services sharing their session */
90 static struct afb_session *common_session;
91
92 /*
93  * Allocates a new service
94  */
95 static struct afb_svc *afb_svc_alloc(int share_session, void (*on_event)(const char *event, struct json_object *object))
96 {
97         struct afb_svc *svc;
98
99         /* allocates the svc handler */
100         svc = malloc(sizeof * svc);
101         if (svc == NULL)
102                 goto error;
103
104         /* instanciate the session */
105         if (share_session) {
106                 /* session shared with other svcs */
107                 if (common_session == NULL) {
108                         common_session = afb_session_create (NULL, 0);
109                         if (common_session == NULL)
110                                 goto error2;
111                 }
112                 svc->session = afb_session_addref(common_session);
113         } else {
114                 /* session dedicated to the svc */
115                 svc->session = afb_session_create (NULL, 0);
116                 if (svc->session == NULL)
117                         goto error2;
118         }
119
120         /* initialises the listener if needed */
121         svc->on_event = on_event;
122         if (on_event == NULL)
123                 svc->listener = NULL;
124         else {
125                 svc->listener = afb_evt_listener_create(&evt_itf, svc);
126                 if (svc->listener == NULL)
127                         goto error3;
128         }
129
130         return svc;
131
132 error3:
133         afb_session_unref(svc->session);
134 error2:
135         free(svc);
136 error:
137         return NULL;
138 }
139
140 /*
141  * Creates a new service
142  */
143 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))
144 {
145         int rc;
146         struct afb_svc *svc;
147
148         /* allocates the svc handler */
149         svc = afb_svc_alloc(share_session, on_event);
150         if (svc == NULL)
151                 goto error;
152
153         /* initialises the svc now */
154         rc = init((struct afb_service){ .itf = &service_itf, .closure = svc });
155         if (rc < 0)
156                 goto error2;
157
158         return svc;
159
160 error2:
161         if (svc->listener != NULL)
162                 afb_evt_listener_unref(svc->listener);
163         afb_session_unref(svc->session);
164         free(svc);
165 error:
166         return NULL;
167 }
168
169 /*
170  * Creates a new service
171  */
172 struct afb_svc *afb_svc_create_v2(
173                         int share_session,
174                         void (*on_event)(const char *event, struct json_object *object),
175                         int (*start)(const struct afb_binding_interface *interface, struct afb_service service),
176                         const struct afb_binding_interface *interface)
177 {
178         int rc;
179         struct afb_svc *svc;
180
181         /* allocates the svc handler */
182         svc = afb_svc_alloc(share_session, on_event);
183         if (svc == NULL)
184                 goto error;
185
186         /* initialises the svc now */
187         rc = start(interface, (struct afb_service){ .itf = &service_itf, .closure = svc });
188         if (rc < 0)
189                 goto error2;
190
191         return svc;
192
193 error2:
194         if (svc->listener != NULL)
195                 afb_evt_listener_unref(svc->listener);
196         afb_session_unref(svc->session);
197         free(svc);
198 error:
199         return NULL;
200 }
201
202 /*
203  * Propagates the event to the service
204  */
205 static void svc_on_event(void *closure, const char *event, int eventid, struct json_object *object)
206 {
207         struct afb_svc *svc = closure;
208         svc->on_event(event, object);
209         json_object_put(object);
210 }
211
212 /*
213  * Initiates a call for the service
214  */
215 static void svc_call(void *closure, const char *api, const char *verb, struct json_object *args, void (*callback)(void*, int, struct json_object*), void *cbclosure)
216 {
217         struct afb_svc *svc = closure;
218         struct svc_req *svcreq;
219
220         /* allocates the request */
221         svcreq = calloc(1, sizeof *svcreq);
222         if (svcreq == NULL) {
223                 ERROR("out of memory");
224                 json_object_put(args);
225                 callback(cbclosure, 1, afb_msg_json_internal_error());
226                 return;
227         }
228
229         /* initialises the request */
230         afb_xreq_init(&svcreq->xreq, &afb_svc_xreq_itf);
231         afb_context_init(&svcreq->xreq.context, svc->session, NULL);
232         svcreq->xreq.context.validated = 1;
233         svcreq->xreq.api = api;
234         svcreq->xreq.verb = verb;
235         svcreq->xreq.listener = svc->listener;
236         svcreq->xreq.json = args;
237         svcreq->callback = callback;
238         svcreq->closure = cbclosure;
239
240         /* terminates and frees ressources if needed */
241         afb_apis_call(&svcreq->xreq);
242         afb_xreq_unref(&svcreq->xreq);
243 }
244
245 static void svcreq_destroy(struct afb_xreq *xreq)
246 {
247         struct svc_req *svcreq = CONTAINER_OF_XREQ(struct svc_req, xreq);
248         afb_context_disconnect(&svcreq->xreq.context);
249         json_object_put(svcreq->xreq.json);
250         free(svcreq);
251 }
252
253 static void svcreq_reply(struct afb_xreq *xreq, int iserror, json_object *obj)
254 {
255         struct svc_req *svcreq = CONTAINER_OF_XREQ(struct svc_req, xreq);
256         svcreq->callback(svcreq->closure, iserror, obj);
257         json_object_put(obj);
258 }
259