1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20 #include <glib-object.h>
21 #include <signal.h>
22 #include <stdio.h>
23 #include <string.h>
24
25 #include <thrift/c_glib/thrift.h>
26 #include <thrift/c_glib/protocol/thrift_binary_protocol_factory.h>
27 #include <thrift/c_glib/protocol/thrift_protocol_factory.h>
28 #include <thrift/c_glib/server/thrift_server.h>
29 #include <thrift/c_glib/server/thrift_simple_server.h>
30 #include <thrift/c_glib/transport/thrift_buffered_transport_factory.h>
31 #include <thrift/c_glib/transport/thrift_server_socket.h>
32 #include <thrift/c_glib/transport/thrift_server_transport.h>
33
34 #include "gen-c_glib/calculator.h"
35
36 G_BEGIN_DECLS
37
38 /* In the C (GLib) implementation of Thrift, the actual work done by a
39 server---that is, the code that runs when a client invokes a
40 service method---is defined in a separate "handler" class that
41 implements the service interface. Here we define the
42 TutorialCalculatorHandler class, which implements the CalculatorIf
43 interface and provides the behavior expected by tutorial clients.
44 (Typically this code would be placed in its own module but for
45 clarity this tutorial is presented entirely in a single file.)
46
47 For each service the Thrift compiler generates an abstract base
48 class from which handler implementations should inherit. In our
49 case TutorialCalculatorHandler inherits from CalculatorHandler,
50 defined in gen-c_glib/calculator.h.
51
52 If you're new to GObject, try not to be intimidated by the quantity
53 of code here---much of it is boilerplate and can mostly be
54 copied-and-pasted from existing work. For more information refer to
55 the GObject Reference Manual, available online at
56 https://developer.gnome.org/gobject/. */
57
58 #define TYPE_TUTORIAL_CALCULATOR_HANDLER \
59 (tutorial_calculator_handler_get_type ())
60
61 #define TUTORIAL_CALCULATOR_HANDLER(obj) \
62 (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
63 TYPE_TUTORIAL_CALCULATOR_HANDLER, \
64 TutorialCalculatorHandler))
65 #define TUTORIAL_CALCULATOR_HANDLER_CLASS(c) \
66 (G_TYPE_CHECK_CLASS_CAST ((c), \
67 TYPE_TUTORIAL_CALCULATOR_HANDLER, \
68 TutorialCalculatorHandlerClass))
69 #define IS_TUTORIAL_CALCULATOR_HANDLER(obj) \
70 (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
71 TYPE_TUTORIAL_CALCULATOR_HANDLER))
72 #define IS_TUTORIAL_CALCULATOR_HANDLER_CLASS(c) \
73 (G_TYPE_CHECK_CLASS_TYPE ((c), \
74 TYPE_TUTORIAL_CALCULATOR_HANDLER))
75 #define TUTORIAL_CALCULATOR_HANDLER_GET_CLASS(obj) \
76 (G_TYPE_INSTANCE_GET_CLASS ((obj), \
77 TYPE_TUTORIAL_CALCULATOR_HANDLER, \
78 TutorialCalculatorHandlerClass))
79
80 struct _TutorialCalculatorHandler {
81 CalculatorHandler parent_instance;
82
83 /* private */
84 GHashTable *log;
85 };
86 typedef struct _TutorialCalculatorHandler TutorialCalculatorHandler;
87
88 struct _TutorialCalculatorHandlerClass {
89 CalculatorHandlerClass parent_class;
90 };
91 typedef struct _TutorialCalculatorHandlerClass TutorialCalculatorHandlerClass;
92
93 GType tutorial_calculator_handler_get_type (void);
94
95 G_END_DECLS
96
97 /* ---------------------------------------------------------------- */
98
99 /* The implementation of TutorialCalculatorHandler follows. */
100
G_DEFINE_TYPE(TutorialCalculatorHandler,tutorial_calculator_handler,TYPE_CALCULATOR_HANDLER)101 G_DEFINE_TYPE (TutorialCalculatorHandler,
102 tutorial_calculator_handler,
103 TYPE_CALCULATOR_HANDLER)
104
105 /* Each of a handler's methods accepts at least two parameters: A
106 pointer to the service-interface implementation (the handler object
107 itself) and a handle to a GError structure to receive information
108 about any error that occurs.
109
110 On success, a handler method returns TRUE. A return value of FALSE
111 indicates an error occurred and the error parameter has been
112 set. (Methods should not return FALSE without first setting the
113 error parameter.) */
114 static gboolean
115 tutorial_calculator_handler_ping (CalculatorIf *iface,
116 GError **error)
117 {
118 THRIFT_UNUSED_VAR (iface);
119 THRIFT_UNUSED_VAR (error);
120
121 puts ("ping()");
122
123 return TRUE;
124 }
125
126 /* Service-method parameters are passed through as parameters to the
127 handler method.
128
129 If the service method returns a value an output parameter, _return,
130 is additionally passed to the handler method. This parameter should
131 be set appropriately before the method returns, whenever it
132 succeeds.
133
134 The return value from this method happens to be of a base type,
135 i32, but note if a method returns a complex type such as a map or
136 list *_return will point to a pre-allocated data structure that
137 does not need to be re-allocated and should not be destroyed. */
138 static gboolean
tutorial_calculator_handler_add(CalculatorIf * iface,gint32 * _return,const gint32 num1,const gint32 num2,GError ** error)139 tutorial_calculator_handler_add (CalculatorIf *iface,
140 gint32 *_return,
141 const gint32 num1,
142 const gint32 num2,
143 GError **error)
144 {
145 THRIFT_UNUSED_VAR (iface);
146 THRIFT_UNUSED_VAR (error);
147
148 printf ("add(%d,%d)\n", num1, num2);
149 *_return = num1 + num2;
150
151 return TRUE;
152 }
153
154 /* Any handler method can return a ThriftApplicationException to the
155 client by setting its error parameter appropriately and returning
156 FALSE. See the ThriftApplicationExceptionError enumeration defined
157 in thrift_application_exception.h for a list of recognized
158 exception types (GError codes).
159
160 If a service method can also throw a custom exception (that is, one
161 defined in the .thrift file) an additional output parameter will be
162 provided (here, "ouch") to hold an instance of the exception, when
163 necessary. Note there will be a separate parameter added for each
164 type of exception the method can throw.
165
166 Unlike return values, exception objects are never pre-created; this
167 is always the responsibility of the handler method. */
168 static gboolean
tutorial_calculator_handler_calculate(CalculatorIf * iface,gint32 * _return,const gint32 logid,const Work * w,InvalidOperation ** ouch,GError ** error)169 tutorial_calculator_handler_calculate (CalculatorIf *iface,
170 gint32 *_return,
171 const gint32 logid,
172 const Work *w,
173 InvalidOperation **ouch,
174 GError **error)
175 {
176 TutorialCalculatorHandler *self;
177
178 gint *log_key;
179 gchar log_value[12];
180 SharedStruct *log_struct;
181
182 gint num1;
183 gint num2;
184 Operation op;
185 gboolean result = TRUE;
186
187 THRIFT_UNUSED_VAR (error);
188
189 g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface),
190 FALSE);
191 self = TUTORIAL_CALCULATOR_HANDLER (iface);
192
193 /* Remember: Exception objects are never pre-created */
194 g_assert (*ouch == NULL);
195
196 /* Fetch the contents of our Work parameter.
197
198 Note that integer properties of thirty-two bits or fewer in width
199 are _always_ of type gint, regardless of the range of values they
200 hold. A common error is trying to retrieve, say, a structure
201 member defined in the .thrift file as type i16 into a variable of
202 type gint16, which will clobber variables adjacent on the
203 stack. Remember: If you're retrieving an integer property the
204 receiving variable must be of either type gint or gint64, as
205 appropriate. */
206 g_object_get ((Work *)w,
207 "num1", &num1,
208 "num2", &num2,
209 "op", &op,
210 NULL);
211
212 printf ("calculate(%d,{%d,%d,%d})\n", logid, op, num1, num2);
213
214 switch (op) {
215 case OPERATION_ADD:
216 *_return = num1 + num2;
217 break;
218
219 case OPERATION_SUBTRACT:
220 *_return = num1 - num2;
221 break;
222
223 case OPERATION_MULTIPLY:
224 *_return = num1 * num2;
225 break;
226
227 case OPERATION_DIVIDE:
228 if (num2 == 0) {
229 /* For each custom exception type a subclass of ThriftStruct is
230 generated by the Thrift compiler. Throw an exception by
231 setting the corresponding output parameter to a new instance
232 of its type and returning FALSE. */
233 *ouch = g_object_new (TYPE_INVALID_OPERATION,
234 "whatOp", op,
235 "why", g_strdup ("Cannot divide by 0"),
236 NULL);
237 result = FALSE;
238
239 /* Note the call to g_strdup above: All the memory used by a
240 ThriftStruct's properties belongs to the object itself and
241 will be freed on destruction. Removing this call to g_strdup
242 will lead to a segmentation fault as the object tries to
243 release memory allocated statically to the program. */
244 }
245 else {
246 *_return = num1 / num2;
247 }
248 break;
249
250 default:
251 *ouch = g_object_new (TYPE_INVALID_OPERATION,
252 "whatOp", op,
253 "why", g_strdup ("Invalid Operation"),
254 NULL);
255 result = FALSE;
256 }
257
258 /* On success, log a record of the result to our hash table */
259 if (result) {
260 log_key = g_malloc (sizeof *log_key);
261 *log_key = logid;
262
263 snprintf (log_value, sizeof log_value, "%d", *_return);
264
265 log_struct = g_object_new (TYPE_SHARED_STRUCT,
266 "key", *log_key,
267 "value", g_strdup (log_value),
268 NULL);
269 g_hash_table_replace (self->log, log_key, log_struct);
270 }
271
272 return result;
273 }
274
275 /* A one-way method has the same signature as an equivalent, regular
276 method that returns no value. */
277 static gboolean
tutorial_calculator_handler_zip(CalculatorIf * iface,GError ** error)278 tutorial_calculator_handler_zip (CalculatorIf *iface,
279 GError **error)
280 {
281 THRIFT_UNUSED_VAR (iface);
282 THRIFT_UNUSED_VAR (error);
283
284 puts ("zip()");
285
286 return TRUE;
287 }
288
289 /* As specified in the .thrift file (tutorial.thrift), the Calculator
290 service extends the SharedService service. Correspondingly, in the
291 generated code the Calculator interface, CalculatorIf, extends the
292 SharedService interface, SharedServiceIf, and subclasses of
293 CalculatorHandler should implement its methods as well.
294
295 Here we provide an implementation for the getStruct method from the
296 parent service. */
297 static gboolean
tutorial_calculator_handler_get_struct(SharedServiceIf * iface,SharedStruct ** _return,const gint32 key32,GError ** error)298 tutorial_calculator_handler_get_struct (SharedServiceIf *iface,
299 SharedStruct **_return,
300 const gint32 key32,
301 GError **error)
302 {
303 gint key = (gint)key32;
304 TutorialCalculatorHandler *self;
305 SharedStruct *log_struct;
306 gint log_key;
307 gchar *log_value;
308
309 THRIFT_UNUSED_VAR (error);
310
311 g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface),
312 FALSE);
313 self = TUTORIAL_CALCULATOR_HANDLER (iface);
314
315 /* Remember: Complex return types are always pre-created and need
316 only be populated */
317 g_assert (*_return != NULL);
318
319 printf ("getStruct(%d)\n", key);
320
321 /* If the key exists in our log, return the corresponding logged
322 data (or an empty SharedStruct structure if it does not).
323
324 Incidentally, note we _must_ here copy the values from the hash
325 table into the return structure. All memory used by the return
326 structure belongs to the structure itself and will be freed once
327 a response is sent to the client. If we merely freed *_return and
328 set it to point to our hash-table entry, that would mean memory
329 would be released (effectively, data erased) out of the hash
330 table! */
331 log_struct = g_hash_table_lookup (self->log, &key);
332 if (log_struct != NULL) {
333 g_object_get (log_struct,
334 "key", &log_key,
335 "value", &log_value,
336 NULL);
337 g_object_set (*_return,
338 "key", log_key,
339 "value", g_strdup (log_value),
340 NULL);
341 }
342
343 return TRUE;
344 }
345
346 /* TutorialCalculatorHandler's instance finalizer (destructor) */
347 static void
tutorial_calculator_handler_finalize(GObject * object)348 tutorial_calculator_handler_finalize (GObject *object)
349 {
350 TutorialCalculatorHandler *self =
351 TUTORIAL_CALCULATOR_HANDLER (object);
352
353 /* Free our calculation-log hash table */
354 g_hash_table_unref (self->log);
355 self->log = NULL;
356
357 /* Chain up to the parent class */
358 G_OBJECT_CLASS (tutorial_calculator_handler_parent_class)->
359 finalize (object);
360 }
361
362 /* TutorialCalculatorHandler's instance initializer (constructor) */
363 static void
tutorial_calculator_handler_init(TutorialCalculatorHandler * self)364 tutorial_calculator_handler_init (TutorialCalculatorHandler *self)
365 {
366 /* Create our calculation-log hash table */
367 self->log = g_hash_table_new_full (g_int_hash,
368 g_int_equal,
369 g_free,
370 g_object_unref);
371 }
372
373 /* TutorialCalculatorHandler's class initializer */
374 static void
tutorial_calculator_handler_class_init(TutorialCalculatorHandlerClass * klass)375 tutorial_calculator_handler_class_init (TutorialCalculatorHandlerClass *klass)
376 {
377 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
378 SharedServiceHandlerClass *shared_service_handler_class =
379 SHARED_SERVICE_HANDLER_CLASS (klass);
380 CalculatorHandlerClass *calculator_handler_class =
381 CALCULATOR_HANDLER_CLASS (klass);
382
383 /* Register our destructor */
384 gobject_class->finalize = tutorial_calculator_handler_finalize;
385
386 /* Register our implementations of CalculatorHandler's methods */
387 calculator_handler_class->ping =
388 tutorial_calculator_handler_ping;
389 calculator_handler_class->add =
390 tutorial_calculator_handler_add;
391 calculator_handler_class->calculate =
392 tutorial_calculator_handler_calculate;
393 calculator_handler_class->zip =
394 tutorial_calculator_handler_zip;
395
396 /* Register our implementation of SharedServiceHandler's method */
397 shared_service_handler_class->get_struct =
398 tutorial_calculator_handler_get_struct;
399 }
400
401 /* ---------------------------------------------------------------- */
402
403 /* That ends the implementation of TutorialCalculatorHandler.
404 Everything below is fairly generic code that sets up a minimal
405 Thrift server for tutorial clients. */
406
407
408 /* Our server object, declared globally so it is accessible within the
409 SIGINT signal handler */
410 ThriftServer *server = NULL;
411
412 /* A flag that indicates whether the server was interrupted with
413 SIGINT (i.e. Ctrl-C) so we can tell whether its termination was
414 abnormal */
415 gboolean sigint_received = FALSE;
416
417 /* Handle SIGINT ("Ctrl-C") signals by gracefully stopping the
418 server */
419 static void
sigint_handler(int signal_number)420 sigint_handler (int signal_number)
421 {
422 THRIFT_UNUSED_VAR (signal_number);
423
424 /* Take note we were called */
425 sigint_received = TRUE;
426
427 /* Shut down the server gracefully */
428 if (server != NULL)
429 thrift_server_stop (server);
430 }
431
main(void)432 int main (void)
433 {
434 TutorialCalculatorHandler *handler;
435 CalculatorProcessor *processor;
436
437 ThriftServerTransport *server_transport;
438 ThriftTransportFactory *transport_factory;
439 ThriftProtocolFactory *protocol_factory;
440
441 struct sigaction sigint_action;
442
443 GError *error = NULL;
444 int exit_status = 0;
445
446 #if (!GLIB_CHECK_VERSION (2, 36, 0))
447 g_type_init ();
448 #endif
449
450 /* Create an instance of our handler, which provides the service's
451 methods' implementation */
452 handler =
453 g_object_new (TYPE_TUTORIAL_CALCULATOR_HANDLER,
454 NULL);
455
456 /* Create an instance of the service's processor, automatically
457 generated by the Thrift compiler, which parses incoming messages
458 and dispatches them to the appropriate method in the handler */
459 processor =
460 g_object_new (TYPE_CALCULATOR_PROCESSOR,
461 "handler", handler,
462 NULL);
463
464 /* Create our server socket, which binds to the specified port and
465 listens for client connections */
466 server_transport =
467 g_object_new (THRIFT_TYPE_SERVER_SOCKET,
468 "port", 9090,
469 NULL);
470
471 /* Create our transport factory, used by the server to wrap "raw"
472 incoming connections from the client (in this case with a
473 ThriftBufferedTransport to improve performance) */
474 transport_factory =
475 g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY,
476 NULL);
477
478 /* Create our protocol factory, which determines which wire protocol
479 the server will use (in this case, Thrift's binary protocol) */
480 protocol_factory =
481 g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY,
482 NULL);
483
484 /* Create the server itself */
485 server =
486 g_object_new (THRIFT_TYPE_SIMPLE_SERVER,
487 "processor", processor,
488 "server_transport", server_transport,
489 "input_transport_factory", transport_factory,
490 "output_transport_factory", transport_factory,
491 "input_protocol_factory", protocol_factory,
492 "output_protocol_factory", protocol_factory,
493 NULL);
494
495 /* Install our SIGINT handler, which handles Ctrl-C being pressed by
496 stopping the server gracefully (not strictly necessary, but a
497 nice touch) */
498 memset (&sigint_action, 0, sizeof (sigint_action));
499 sigint_action.sa_handler = sigint_handler;
500 sigint_action.sa_flags = SA_RESETHAND;
501 sigaction (SIGINT, &sigint_action, NULL);
502
503 /* Start the server, which will run until its stop method is invoked
504 (from within the SIGINT handler, in this case) */
505 puts ("Starting the server...");
506 thrift_server_serve (server, &error);
507
508 /* If the server stopped for any reason other than having been
509 interrupted by the user, report the error */
510 if (!sigint_received) {
511 g_message ("thrift_server_serve: %s",
512 error != NULL ? error->message : "(null)");
513 g_clear_error (&error);
514 }
515
516 puts ("done.");
517
518 g_object_unref (server);
519 g_object_unref (transport_factory);
520 g_object_unref (protocol_factory);
521 g_object_unref (server_transport);
522
523 g_object_unref (processor);
524 g_object_unref (handler);
525
526 return exit_status;
527 }
528