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