summaryrefslogtreecommitdiff
path: root/tutorial/c_glib
diff options
context:
space:
mode:
authorRoger Meier <roger@apache.org>2014-09-29 21:01:58 +0200
committerRoger Meier <roger@apache.org>2014-09-29 21:01:58 +0200
commit4606d7db90343014908acd80267034f2d750d660 (patch)
treeabaf3632bea08c85382336f2badb9febca50a385 /tutorial/c_glib
parent15df0767d3ddeb87b5f1b2fa832e1502b9a6884f (diff)
downloadthrift-4606d7db90343014908acd80267034f2d750d660.tar.gz
THRIFT-976 c_glib tutorial
Patch: Simon South
Diffstat (limited to 'tutorial/c_glib')
-rwxr-xr-xtutorial/c_glib/Makefile.am12
-rw-r--r--tutorial/c_glib/c_glib_client.c68
-rw-r--r--tutorial/c_glib/c_glib_server.c527
3 files changed, 573 insertions, 34 deletions
diff --git a/tutorial/c_glib/Makefile.am b/tutorial/c_glib/Makefile.am
index 33d6c9369..24075dd49 100755
--- a/tutorial/c_glib/Makefile.am
+++ b/tutorial/c_glib/Makefile.am
@@ -43,8 +43,16 @@ libtutorialgencglib_la_LIBADD = \
noinst_PROGRAMS = \
+ tutorial_server \
tutorial_client
+tutorial_server_SOURCES = \
+ c_glib_server.c
+
+tutorial_server_LDADD = \
+ libtutorialgencglib.la \
+ $(top_builddir)/lib/c_glib/libthrift_c_glib.la
+
tutorial_client_SOURCES = \
c_glib_client.c
@@ -59,8 +67,12 @@ $(nodist_libtutorialgencglib_la_SOURCES): $(top_srcdir)/tutorial/tutorial.thrift
clean-local:
$(RM) -r gen-c_glib
+tutorialserver: all
+ ./tutorial_server
+
tutorialclient: all
./tutorial_client
EXTRA_DIST = \
+ c_glib_server.c \
c_glib_client.c
diff --git a/tutorial/c_glib/c_glib_client.c b/tutorial/c_glib/c_glib_client.c
index d3f2a34ee..3cea0c288 100644
--- a/tutorial/c_glib/c_glib_client.c
+++ b/tutorial/c_glib/c_glib_client.c
@@ -26,12 +26,12 @@
#include "gen-c_glib/calculator.h"
-int main (void) {
+int main (void)
+{
ThriftSocket *socket;
ThriftTransport *transport;
ThriftProtocol *protocol;
- CalculatorClient *client;
- CalculatorIf *iface;
+ CalculatorIf *client;
GError *error = NULL;
InvalidOperation *invalid_operation = NULL;
@@ -43,16 +43,20 @@ int main (void) {
int exit_status = 0;
- socket = g_object_new (THRIFT_TYPE_SOCKET,
- "hostname", "localhost",
- "port", 9090,
- NULL);
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init ();
+#endif
+
+ socket = g_object_new (THRIFT_TYPE_SOCKET,
+ "hostname", "localhost",
+ "port", 9090,
+ NULL);
transport = g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT,
"transport", socket,
NULL);
- protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
- "transport", transport,
- NULL);
+ protocol = g_object_new (THRIFT_TYPE_BINARY_PROTOCOL,
+ "transport", transport,
+ NULL);
thrift_transport_open (transport, &error);
@@ -63,10 +67,9 @@ int main (void) {
service through an instance of CalculatorClient, which implements
CalculatorIf. */
client = g_object_new (TYPE_CALCULATOR_CLIENT,
- "input_protocol", protocol,
+ "input_protocol", protocol,
"output_protocol", protocol,
NULL);
- iface = CALCULATOR_IF (client);
/* Each of the client methods requires at least two parameters: A
pointer to the client-interface implementation (the client
@@ -76,13 +79,13 @@ int main (void) {
On success, client methods return TRUE. A return value of FALSE
indicates an error occured and the error parameter has been
set. */
- if (!error && calculator_client_ping (iface, &error)) {
+ if (!error && calculator_if_ping (client, &error)) {
puts ("ping()");
}
/* Service methods that return a value do so by passing the result
back via an output parameter (here, "sum"). */
- if (!error && calculator_client_add (iface, &sum, 1, 1, &error)) {
+ if (!error && calculator_if_add (client, &sum, 1, 1, &error)) {
printf ("1+1=%d\n", sum);
}
@@ -99,12 +102,12 @@ int main (void) {
/* Exceptions are passed back from service methods in a manner
similar to return values. */
- if (calculator_client_calculate (iface,
- NULL,
- 1,
- work,
- &invalid_operation,
- &error)) {
+ if (calculator_if_calculate (client,
+ NULL,
+ 1,
+ work,
+ &invalid_operation,
+ &error)) {
puts ("Whoa? We can divide by zero!");
}
else {
@@ -123,8 +126,7 @@ int main (void) {
invalid_operation = NULL;
}
- g_error_free (error);
- error = NULL;
+ g_clear_error (&error);
}
}
@@ -136,12 +138,12 @@ int main (void) {
"op", OPERATION_SUBTRACT,
NULL);
- if (calculator_client_calculate (iface,
- &diff,
- 1,
- work,
- &invalid_operation,
- &error)) {
+ if (calculator_if_calculate (client,
+ &diff,
+ 1,
+ work,
+ &invalid_operation,
+ &error)) {
printf ("15-10=%d\n", diff);
}
}
@@ -156,10 +158,9 @@ int main (void) {
/* As defined in the Thrift file, the Calculator service extends
the SharedService service. Correspondingly, in the generated
- code CalculatorClient inherits from SharedServiceClient, and
- the parent service's methods are accessible through a simple
- cast. */
- if (shared_service_client_get_struct (SHARED_SERVICE_IF (iface),
+ code CalculatorIf inherits from SharedServiceIf, and the parent
+ service's methods are accessible through a simple cast. */
+ if (shared_service_client_get_struct (SHARED_SERVICE_IF (client),
&shared_struct,
1,
&error)) {
@@ -173,8 +174,7 @@ int main (void) {
if (error) {
printf ("ERROR: %s\n", error->message);
- g_error_free (error);
- error = NULL;
+ g_clear_error (&error);
exit_status = 1;
}
diff --git a/tutorial/c_glib/c_glib_server.c b/tutorial/c_glib/c_glib_server.c
new file mode 100644
index 000000000..27f9f959e
--- /dev/null
+++ b/tutorial/c_glib/c_glib_server.c
@@ -0,0 +1,527 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+#include <glib-object.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <thrift/c_glib/thrift.h>
+#include <thrift/c_glib/protocol/thrift_binary_protocol_factory.h>
+#include <thrift/c_glib/protocol/thrift_protocol_factory.h>
+#include <thrift/c_glib/server/thrift_server.h>
+#include <thrift/c_glib/server/thrift_simple_server.h>
+#include <thrift/c_glib/transport/thrift_buffered_transport_factory.h>
+#include <thrift/c_glib/transport/thrift_server_socket.h>
+#include <thrift/c_glib/transport/thrift_server_transport.h>
+
+#include "gen-c_glib/calculator.h"
+
+G_BEGIN_DECLS
+
+/* In the C (GLib) implementation of Thrift, the actual work done by a
+ server---that is, the code that runs when a client invokes a
+ service method---is defined in a separate "handler" class that
+ implements the service interface. Here we define the
+ TutorialCalculatorHandler class, which implements the CalculatorIf
+ interface and provides the behavior expected by tutorial clients.
+ (Typically this code would be placed in its own module but for
+ clarity this tutorial is presented entirely in a single file.)
+
+ For each service the Thrift compiler generates an abstract base
+ class from which handler implementations should inherit. In our
+ case TutorialCalculatorHandler inherits from CalculatorHandler,
+ defined in gen-c_glib/calculator.h.
+
+ If you're new to GObject, try not to be intimidated by the quantity
+ of code here---much of it is boilerplate and can mostly be
+ copied-and-pasted from existing work. For more information refer to
+ the GObject Reference Manual, available online at
+ https://developer.gnome.org/gobject/. */
+
+#define TYPE_TUTORIAL_CALCULATOR_HANDLER \
+ (tutorial_calculator_handler_get_type ())
+
+#define TUTORIAL_CALCULATOR_HANDLER(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \
+ TYPE_TUTORIAL_CALCULATOR_HANDLER, \
+ TutorialCalculatorHandler))
+#define TUTORIAL_CALCULATOR_HANDLER_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_CAST ((c), \
+ TYPE_TUTORIAL_CALCULATOR_HANDLER, \
+ TutorialCalculatorHandlerClass))
+#define IS_TUTORIAL_CALCULATOR_HANDLER(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \
+ TYPE_TUTORIAL_CALCULATOR_HANDLER))
+#define IS_TUTORIAL_CALCULATOR_HANDLER_CLASS(c) \
+ (G_TYPE_CHECK_CLASS_TYPE ((c), \
+ TYPE_TUTORIAL_CALCULATOR_HANDLER))
+#define TUTORIAL_CALCULATOR_HANDLER_GET_CLASS(obj) \
+ (G_TYPE_INSTANCE_GET_CLASS ((obj), \
+ TYPE_TUTORIAL_CALCULATOR_HANDLER, \
+ TutorialCalculatorHandlerClass))
+
+struct _TutorialCalculatorHandler {
+ CalculatorHandler parent_instance;
+
+ /* private */
+ GHashTable *log;
+};
+typedef struct _TutorialCalculatorHandler TutorialCalculatorHandler;
+
+struct _TutorialCalculatorHandlerClass {
+ CalculatorHandlerClass parent_class;
+};
+typedef struct _TutorialCalculatorHandlerClass TutorialCalculatorHandlerClass;
+
+GType tutorial_calculator_handler_get_type (void);
+
+G_END_DECLS
+
+/* ---------------------------------------------------------------- */
+
+/* The implementation of TutorialCalculatorHandler follows. */
+
+G_DEFINE_TYPE (TutorialCalculatorHandler,
+ tutorial_calculator_handler,
+ TYPE_CALCULATOR_HANDLER);
+
+/* Each of a handler's methods accepts at least two parameters: A
+ pointer to the service-interface implementation (the handler object
+ itself) and a handle to a GError structure to receive information
+ about any error that occurs.
+
+ On success, a handler method returns TRUE. A return value of FALSE
+ indicates an error occured and the error parameter has been
+ set. (Methods should not return FALSE without first setting the
+ error parameter.) */
+static gboolean
+tutorial_calculator_handler_ping (CalculatorIf *iface,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (iface);
+ THRIFT_UNUSED_VAR (error);
+
+ puts ("ping()");
+
+ return TRUE;
+}
+
+/* Service-method parameters are passed through as parameters to the
+ handler method.
+
+ If the service method returns a value an output parameter, _return,
+ is additionally passed to the handler method. This parameter should
+ be set appropriately before the method returns, whenever it
+ succeeds.
+
+ The return value from this method happens to be of a base type,
+ i32, but note if a method returns a complex type such as a map or
+ list *_return will point to a pre-allocated data structure that
+ does not need to be re-allocated and should not be destroyed. */
+static gboolean
+tutorial_calculator_handler_add (CalculatorIf *iface,
+ gint32 *_return,
+ const gint32 num1,
+ const gint32 num2,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (iface);
+ THRIFT_UNUSED_VAR (error);
+
+ printf ("add(%d,%d)\n", num1, num2);
+ *_return = num1 + num2;
+
+ return TRUE;
+}
+
+/* Any handler method can return a ThriftApplicationException to the
+ client by setting its error parameter appropriately and returning
+ FALSE. See the ThriftApplicationExceptionError enumeration defined
+ in thrift_application_exception.h for a list of recognized
+ exception types (GError codes).
+
+ If a service method can also throw a custom exception (that is, one
+ defined in the .thrift file) an additional output parameter will be
+ provided (here, "ouch") to hold an instance of the exception, when
+ necessary. Note there will be a separate parameter added for each
+ type of exception the method can throw.
+
+ Unlike return values, exception objects are never pre-created; this
+ is always the responsibility of the handler method. */
+static gboolean
+tutorial_calculator_handler_calculate (CalculatorIf *iface,
+ gint32 *_return,
+ const gint32 logid,
+ const Work *w,
+ InvalidOperation **ouch,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (error);
+
+ TutorialCalculatorHandler *self;
+
+ gint *log_key;
+ gchar log_value[12];
+ SharedStruct *log_struct;
+
+ gint num1;
+ gint num2;
+ Operation op;
+ gboolean result = TRUE;
+
+ g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface),
+ FALSE);
+ self = TUTORIAL_CALCULATOR_HANDLER (iface);
+
+ /* Remember: Exception objects are never pre-created */
+ g_assert (*ouch == NULL);
+
+ /* Fetch the contents of our Work parameter.
+
+ Note that integer properties of thirty-two bits or fewer in width
+ are _always_ of type gint, regardless of the range of values they
+ hold. A common error is trying to retrieve, say, a structure
+ member defined in the .thrift file as type i16 into a variable of
+ type gint16, which will clobber variables adjacent on the
+ stack. Remember: If you're retrieving an integer property the
+ receiving variable must be of either type gint or gint64, as
+ appropriate. */
+ g_object_get ((Work *)w,
+ "num1", &num1,
+ "num2", &num2,
+ "op", &op,
+ NULL);
+
+ printf ("calculate(%d,{%d,%d,%d})\n", logid, op, num1, num2);
+
+ switch (op) {
+ case OPERATION_ADD:
+ *_return = num1 + num2;
+ break;
+
+ case OPERATION_SUBTRACT:
+ *_return = num1 - num2;
+ break;
+
+ case OPERATION_MULTIPLY:
+ *_return = num1 * num2;
+ break;
+
+ case OPERATION_DIVIDE:
+ if (num2 == 0) {
+ /* For each custom exception type a subclass of ThriftStruct is
+ generated by the Thrift compiler. Throw an exception by
+ setting the corresponding output parameter to a new instance
+ of its type and returning FALSE. */
+ *ouch = g_object_new (TYPE_INVALID_OPERATION,
+ "what", op,
+ "why", g_strdup ("Cannot divide by 0"),
+ NULL);
+ result = FALSE;
+
+ /* Note the call to g_strdup above: All the memory used by a
+ ThriftStruct's properties belongs to the object itself and
+ will be freed on destruction. Removing this call to g_strdup
+ will lead to a segmentation fault as the object tries to
+ release memory allocated statically to the program. */
+ }
+ else {
+ *_return = num1 / num2;
+ }
+ break;
+
+ default:
+ *ouch = g_object_new (TYPE_INVALID_OPERATION,
+ "what", op,
+ "why", g_strdup ("Invalid Operation"),
+ NULL);
+ result = FALSE;
+ }
+
+ /* On success, log a record of the result to our hash table */
+ if (result) {
+ log_key = g_malloc (sizeof *log_key);
+ *log_key = logid;
+
+ snprintf (log_value, sizeof log_value, "%d", *_return);
+
+ log_struct = g_object_new (TYPE_SHARED_STRUCT,
+ "key", *log_key,
+ "value", g_strdup (log_value),
+ NULL);
+ g_hash_table_replace (self->log, log_key, log_struct);
+ }
+
+ return result;
+}
+
+/* A one-way method has the same signature as an equivalent, regular
+ method that returns no value. */
+static gboolean
+tutorial_calculator_handler_zip (CalculatorIf *iface,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (iface);
+ THRIFT_UNUSED_VAR (error);
+
+ puts ("zip()");
+
+ return TRUE;
+}
+
+/* As specified in the .thrift file (tutorial.thrift), the Calculator
+ service extends the SharedService service. Correspondingly, in the
+ generated code the Calculator interface, CalculatorIf, extends the
+ SharedService interface, SharedServiceIf, and subclasses of
+ CalculatorHandler should implement its methods as well.
+
+ Here we provide an implementation for the getStruct method from the
+ parent service. */
+static gboolean
+tutorial_calculator_handler_get_struct (SharedServiceIf *iface,
+ SharedStruct **_return,
+ const gint32 key32,
+ GError **error)
+{
+ THRIFT_UNUSED_VAR (error);
+
+ gint key = (gint)key32;
+ TutorialCalculatorHandler *self;
+ SharedStruct *log_struct;
+ gint log_key;
+ gchar *log_value;
+
+ g_return_val_if_fail (IS_TUTORIAL_CALCULATOR_HANDLER (iface),
+ FALSE);
+ self = TUTORIAL_CALCULATOR_HANDLER (iface);
+
+ /* Remember: Complex return types are always pre-created and need
+ only be populated */
+ g_assert (*_return != NULL);
+
+ printf ("getStruct(%d)\n", key);
+
+ /* If the key exists in our log, return the corresponding logged
+ data (or an empty SharedStruct structure if it does not).
+
+ Incidentally, note we _must_ here copy the values from the hash
+ table into the return structure. All memory used by the return
+ structure belongs to the structure itself and will be freed once
+ a response is sent to the client. If we merely freed *_return and
+ set it to point to our hash-table entry, that would mean memory
+ would be released (effectively, data erased) out of the hash
+ table! */
+ log_struct = g_hash_table_lookup (self->log, &key);
+ if (log_struct != NULL) {
+ g_object_get (log_struct,
+ "key", &log_key,
+ "value", &log_value,
+ NULL);
+ g_object_set (*_return,
+ "key", log_key,
+ "value", g_strdup (log_value),
+ NULL);
+ }
+
+ return TRUE;
+}
+
+/* TutorialCalculatorHandler's instance finalizer (destructor) */
+static void
+tutorial_calculator_handler_finalize (GObject *object)
+{
+ TutorialCalculatorHandler *self =
+ TUTORIAL_CALCULATOR_HANDLER (object);
+
+ /* Free our calculation-log hash table */
+ g_hash_table_unref (self->log);
+ self->log = NULL;
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (tutorial_calculator_handler_parent_class)->
+ finalize (object);
+}
+
+/* TutorialCalculatorHandler's instance initializer (constructor) */
+static void
+tutorial_calculator_handler_init (TutorialCalculatorHandler *self)
+{
+ /* Create our calculation-log hash table */
+ self->log = g_hash_table_new_full (g_int_hash,
+ g_int_equal,
+ g_free,
+ g_object_unref);
+}
+
+/* TutorialCalculatorHandler's class initializer */
+static void
+tutorial_calculator_handler_class_init (TutorialCalculatorHandlerClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ SharedServiceHandlerClass *shared_service_handler_class =
+ SHARED_SERVICE_HANDLER_CLASS (klass);
+ CalculatorHandlerClass *calculator_handler_class =
+ CALCULATOR_HANDLER_CLASS (klass);
+
+ /* Register our destructor */
+ gobject_class->finalize = tutorial_calculator_handler_finalize;
+
+ /* Register our implementations of CalculatorHandler's methods */
+ calculator_handler_class->ping =
+ tutorial_calculator_handler_ping;
+ calculator_handler_class->add =
+ tutorial_calculator_handler_add;
+ calculator_handler_class->calculate =
+ tutorial_calculator_handler_calculate;
+ calculator_handler_class->zip =
+ tutorial_calculator_handler_zip;
+
+ /* Register our implementation of SharedServiceHandler's method */
+ shared_service_handler_class->get_struct =
+ tutorial_calculator_handler_get_struct;
+}
+
+/* ---------------------------------------------------------------- */
+
+/* That ends the implementation of TutorialCalculatorHandler.
+ Everything below is fairly generic code that sets up a minimal
+ Thrift server for tutorial clients. */
+
+
+/* Our server object, declared globally so it is accessible within the
+ SIGINT signal handler */
+ThriftServer *server = NULL;
+
+/* A flag that indicates whether the server was interrupted with
+ SIGINT (i.e. Ctrl-C) so we can tell whether its termination was
+ abnormal */
+gboolean sigint_received = FALSE;
+
+/* Handle SIGINT ("Ctrl-C") signals by gracefully stopping the
+ server */
+static void
+sigint_handler (int signal_number)
+{
+ THRIFT_UNUSED_VAR (signal_number);
+
+ /* Take note we were called */
+ sigint_received = TRUE;
+
+ /* Shut down the server gracefully */
+ if (server != NULL)
+ thrift_server_stop (server);
+}
+
+int main (void)
+{
+ TutorialCalculatorHandler *handler;
+ CalculatorProcessor *processor;
+
+ ThriftServerTransport *server_transport;
+ ThriftTransportFactory *transport_factory;
+ ThriftProtocolFactory *protocol_factory;
+
+ struct sigaction sigint_action;
+
+ GError *error;
+ int exit_status = 0;
+
+#if (!GLIB_CHECK_VERSION (2, 36, 0))
+ g_type_init ();
+#endif
+
+ /* Create an instance of our handler, which provides the service's
+ methods' implementation */
+ handler =
+ g_object_new (TYPE_TUTORIAL_CALCULATOR_HANDLER,
+ NULL);
+
+ /* Create an instance of the service's processor, automatically
+ generated by the Thrift compiler, which parses incoming messages
+ and dispatches them to the appropriate method in the handler */
+ processor =
+ g_object_new (TYPE_CALCULATOR_PROCESSOR,
+ "handler", handler,
+ NULL);
+
+ /* Create our server socket, which binds to the specified port and
+ listens for client connections */
+ server_transport =
+ g_object_new (THRIFT_TYPE_SERVER_SOCKET,
+ "port", 9090,
+ NULL);
+
+ /* Create our transport factory, used by the server to wrap "raw"
+ incoming connections from the client (in this case with a
+ ThriftBufferedTransport to improve performance) */
+ transport_factory =
+ g_object_new (THRIFT_TYPE_BUFFERED_TRANSPORT_FACTORY,
+ NULL);
+
+ /* Create our protocol factory, which determines which wire protocol
+ the server will use (in this case, Thrift's binary protocol) */
+ protocol_factory =
+ g_object_new (THRIFT_TYPE_BINARY_PROTOCOL_FACTORY,
+ NULL);
+
+ /* Create the server itself */
+ server =
+ g_object_new (THRIFT_TYPE_SIMPLE_SERVER,
+ "processor", processor,
+ "server_transport", server_transport,
+ "input_transport_factory", transport_factory,
+ "output_transport_factory", transport_factory,
+ "input_protocol_factory", protocol_factory,
+ "output_protocol_factory", protocol_factory,
+ NULL);
+
+ /* Install our SIGINT handler, which handles Ctrl-C being pressed by
+ stopping the server gracefully (not strictly necessary, but a
+ nice touch) */
+ memset (&sigint_action, 0, sizeof (sigint_action));
+ sigint_action.sa_handler = sigint_handler;
+ sigint_action.sa_flags = SA_RESETHAND;
+ sigaction (SIGINT, &sigint_action, NULL);
+
+ /* Start the server, which will run until its stop method is invoked
+ (from within the SIGINT handler, in this case) */
+ puts ("Starting the server...");
+ thrift_server_serve (server, &error);
+
+ /* If the server stopped for any reason other than having been
+ interrupted by the user, report the error */
+ if (!sigint_received) {
+ g_message ("thrift_server_serve: %s",
+ error != NULL ? error->message : "(null)");
+ g_clear_error (&error);
+ }
+
+ puts ("done.");
+
+ g_object_unref (server);
+ g_object_unref (transport_factory);
+ g_object_unref (protocol_factory);
+ g_object_unref (server_transport);
+
+ g_object_unref (processor);
+ g_object_unref (handler);
+
+ return exit_status;
+}