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
10 * http://www.apache.org/licenses/LICENSE-2.0
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
20 #include <glib-object.h>
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>
34 #include "gen-c_glib/calculator.h"
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.)
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.
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/. */
58 #define TYPE_TUTORIAL_CALCULATOR_HANDLER \
59 (tutorial_calculator_handler_get_type ())
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))
80 struct _TutorialCalculatorHandler
{
81 CalculatorHandler parent_instance
;
86 typedef struct _TutorialCalculatorHandler TutorialCalculatorHandler
;
88 struct _TutorialCalculatorHandlerClass
{
89 CalculatorHandlerClass parent_class
;
91 typedef struct _TutorialCalculatorHandlerClass TutorialCalculatorHandlerClass
;
93 GType
tutorial_calculator_handler_get_type (void);
97 /* ---------------------------------------------------------------- */
99 /* The implementation of TutorialCalculatorHandler follows. */
101 G_DEFINE_TYPE (TutorialCalculatorHandler
,
102 tutorial_calculator_handler
,
103 TYPE_CALCULATOR_HANDLER
)
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.
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
115 tutorial_calculator_handler_ping (CalculatorIf
*iface
,
118 THRIFT_UNUSED_VAR (iface
);
119 THRIFT_UNUSED_VAR (error
);
126 /* Service-method parameters are passed through as parameters to the
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
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. */
139 tutorial_calculator_handler_add (CalculatorIf
*iface
,
145 THRIFT_UNUSED_VAR (iface
);
146 THRIFT_UNUSED_VAR (error
);
148 printf ("add(%d,%d)\n", num1
, num2
);
149 *_return
= num1
+ num2
;
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).
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.
166 Unlike return values, exception objects are never pre-created; this
167 is always the responsibility of the handler method. */
169 tutorial_calculator_handler_calculate (CalculatorIf
*iface
,
173 InvalidOperation
**ouch
,
176 TutorialCalculatorHandler
*self
;
180 SharedStruct
*log_struct
;
185 gboolean result
= TRUE
;
187 THRIFT_UNUSED_VAR (error
);
189 g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface
),
191 self
= TUTORIAL_CALCULATOR_HANDLER (iface
);
193 /* Remember: Exception objects are never pre-created */
194 g_assert (*ouch
== NULL
);
196 /* Fetch the contents of our Work parameter.
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
206 g_object_get ((Work
*)w
,
212 printf ("calculate(%d,{%d,%d,%d})\n", logid
, op
, num1
, num2
);
216 *_return
= num1
+ num2
;
219 case OPERATION_SUBTRACT
:
220 *_return
= num1
- num2
;
223 case OPERATION_MULTIPLY
:
224 *_return
= num1
* num2
;
227 case OPERATION_DIVIDE
:
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
,
235 "why", g_strdup ("Cannot divide by 0"),
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. */
246 *_return
= num1
/ num2
;
251 *ouch
= g_object_new (TYPE_INVALID_OPERATION
,
253 "why", g_strdup ("Invalid Operation"),
258 /* On success, log a record of the result to our hash table */
260 log_key
= g_malloc (sizeof *log_key
);
263 snprintf (log_value
, sizeof log_value
, "%d", *_return
);
265 log_struct
= g_object_new (TYPE_SHARED_STRUCT
,
267 "value", g_strdup (log_value
),
269 g_hash_table_replace (self
->log
, log_key
, log_struct
);
275 /* A one-way method has the same signature as an equivalent, regular
276 method that returns no value. */
278 tutorial_calculator_handler_zip (CalculatorIf
*iface
,
281 THRIFT_UNUSED_VAR (iface
);
282 THRIFT_UNUSED_VAR (error
);
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.
295 Here we provide an implementation for the getStruct method from the
298 tutorial_calculator_handler_get_struct (SharedServiceIf
*iface
,
299 SharedStruct
**_return
,
303 gint key
= (gint
)key32
;
304 TutorialCalculatorHandler
*self
;
305 SharedStruct
*log_struct
;
309 THRIFT_UNUSED_VAR (error
);
311 g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface
),
313 self
= TUTORIAL_CALCULATOR_HANDLER (iface
);
315 /* Remember: Complex return types are always pre-created and need
317 g_assert (*_return
!= NULL
);
319 printf ("getStruct(%d)\n", key
);
321 /* If the key exists in our log, return the corresponding logged
322 data (or an empty SharedStruct structure if it does not).
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
331 log_struct
= g_hash_table_lookup (self
->log
, &key
);
332 if (log_struct
!= NULL
) {
333 g_object_get (log_struct
,
337 g_object_set (*_return
,
339 "value", g_strdup (log_value
),
346 /* TutorialCalculatorHandler's instance finalizer (destructor) */
348 tutorial_calculator_handler_finalize (GObject
*object
)
350 TutorialCalculatorHandler
*self
=
351 TUTORIAL_CALCULATOR_HANDLER (object
);
353 /* Free our calculation-log hash table */
354 g_hash_table_unref (self
->log
);
357 /* Chain up to the parent class */
358 G_OBJECT_CLASS (tutorial_calculator_handler_parent_class
)->
362 /* TutorialCalculatorHandler's instance initializer (constructor) */
364 tutorial_calculator_handler_init (TutorialCalculatorHandler
*self
)
366 /* Create our calculation-log hash table */
367 self
->log
= g_hash_table_new_full (g_int_hash
,
373 /* TutorialCalculatorHandler's class initializer */
375 tutorial_calculator_handler_class_init (TutorialCalculatorHandlerClass
*klass
)
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
);
383 /* Register our destructor */
384 gobject_class
->finalize
= tutorial_calculator_handler_finalize
;
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
;
396 /* Register our implementation of SharedServiceHandler's method */
397 shared_service_handler_class
->get_struct
=
398 tutorial_calculator_handler_get_struct
;
401 /* ---------------------------------------------------------------- */
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. */
408 /* Our server object, declared globally so it is accessible within the
409 SIGINT signal handler */
410 ThriftServer
*server
= NULL
;
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
415 gboolean sigint_received
= FALSE
;
417 /* Handle SIGINT ("Ctrl-C") signals by gracefully stopping the
420 sigint_handler (int signal_number
)
422 THRIFT_UNUSED_VAR (signal_number
);
424 /* Take note we were called */
425 sigint_received
= TRUE
;
427 /* Shut down the server gracefully */
429 thrift_server_stop (server
);
434 TutorialCalculatorHandler
*handler
;
435 CalculatorProcessor
*processor
;
437 ThriftServerTransport
*server_transport
;
438 ThriftTransportFactory
*transport_factory
;
439 ThriftProtocolFactory
*protocol_factory
;
441 struct sigaction sigint_action
;
443 GError
*error
= NULL
;
446 #if (!GLIB_CHECK_VERSION (2, 36, 0))
450 /* Create an instance of our handler, which provides the service's
451 methods' implementation */
453 g_object_new (TYPE_TUTORIAL_CALCULATOR_HANDLER
,
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 */
460 g_object_new (TYPE_CALCULATOR_PROCESSOR
,
464 /* Create our server socket, which binds to the specified port and
465 listens for client connections */
467 g_object_new (THRIFT_TYPE_SERVER_SOCKET
,
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) */
475 g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY
,
478 /* Create our protocol factory, which determines which wire protocol
479 the server will use (in this case, Thrift's binary protocol) */
481 g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY
,
484 /* Create the server itself */
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
,
495 /* Install our SIGINT handler, which handles Ctrl-C being pressed by
496 stopping the server gracefully (not strictly necessary, but a
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
);
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
);
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
);
518 g_object_unref (server
);
519 g_object_unref (transport_factory
);
520 g_object_unref (protocol_factory
);
521 g_object_unref (server_transport
);
523 g_object_unref (processor
);
524 g_object_unref (handler
);