ORBit Beginners Documentation V1.2 | ||
---|---|---|
<<< Previous | First CORBA Programs | Next >>> |
In this third simple example, we will see how we can set up a client that can modify the value of a variable stored on the server. It is basically the way an account manager works. The stored variable here is balance. The idl definition (account.idl) for our account is :
Example 15. account.idl
interface Account { void deposit (in unsigned long amount); void withdraw (in unsigned long amount); readonly attribute long balance; }; |
File | Usage for Client | Usage for Server |
---|---|---|
account.h | readonly | readonly |
account-common.c | readonly | readonly |
account-stubs.c | readonly | - |
account-skels.c | - | readonly |
account-skelimpl.c | - | template for user code |
There is no difficulty in setting the client (at least no more than in the previous examples). Only one thing has been added : we test for the availabilty of the server (if (!acc_client) ...) before invoking calls to the server.
Example 16. account-client.c
/* account-client.c hacked by Frank Rehberger * <F.Rehberger@xtradyne.de>. */ #include <assert.h> #include <stdio.h> #include <orbit/orbit.h> #include "account.h" /** * test for exception */ static gboolean raised_exception(CORBA_Environment *ev) { return ((ev)->_major != CORBA_NO_EXCEPTION); } /** * in case of any exception this macro will abort the process */ static void abort_if_exception(CORBA_Environment *ev, const char* mesg) { if (raised_exception (ev)) { g_error ("%s %s", mesg, CORBA_exception_id (ev)); CORBA_exception_free (ev); abort(); } } /* * main */ int main(int argc, char* argv[]) { char* ior=NULL; CORBA_long val=0; CORBA_long balance=0; CORBA_ORB orb; Account server; CORBA_Environment ev[1]; CORBA_exception_init(ev); /* init - ORB might 'eat' arguments from command line */ orb = CORBA_ORB_init(&argc, argv, "orbit-local-orb", ev); abort_if_exception(ev, "init failed"); /* make sure command lines contains two arguments; IOR and * integer value */ if (argc<3) g_error ("usage: %s <ior> <int>", argv[0]); ior = argv[1]; val = atoi(argv[2]); /* establish servant connection */ server = (Account) CORBA_ORB_string_to_object(orb, ior, ev); abort_if_exception(ev, "bind failed"); /* * use calculator server */ balance = Account__get_balance (server, ev); abort_if_exception(ev, "service not reachable"); g_print ("balance %5d, ", balance); if (val > 0) { Account_deposit (server, val, ev); abort_if_exception(ev, "service not reachable"); } else { Account_withdraw (server, abs(val), ev); abort_if_exception(ev, "service not reachable"); } balance = Account__get_balance (server, ev); abort_if_exception(ev, "service not reachable"); g_print ("new balance %5d\n", balance); /* tear down object reference and ORB */ CORBA_Object_release(server,ev); abort_if_exception(ev, "releasing service failed"); CORBA_ORB_destroy (orb, ev); abort_if_exception(ev, "cleanup failed"); /* successfull termination */ exit(0); } |
For the server, like in the previous example, we first generate the source file account-skelimpl.c that will receive the implementation code for the methods. This is done once again with orbit-idl-2 --skeleton-impl account.idl.
Now, let us edit account-skelimpl.c. We search for the the balance attribute that was declared in the IDL file. At the beginning of the file, we can spot the way it has been translated into C by the idl compiler:
Example 17. account-skelimpl.c fragment - object declaration
typedef struct { POA_Account servant; PortableServer_POA poa; CORBA_long attr_balance; /* ------ add private attributes here ------ */ CORBA_long attr_balance; /* ------ ---------- end ------------ ------ */ } impl_POA_Account; |
Now, let us get to the end of the file and find the methods stubs. We find the impl_Account_* functions, to which we add the implementation code. This could be:
Example 18. account-skelimpl.c fragment - method definition
static void impl_Account_deposit(impl_POA_Account * servant, const CORBA_unsigned_long amount, CORBA_Environment * ev) { /* ------ insert method code here ------ */ servant->attr_balance += amount; /* ------ ---------- end ------------ ------ */ } static void impl_Account_withdraw(impl_POA_Account * servant, const CORBA_unsigned_long amount, CORBA_Environment * ev) { /* ------ insert method code here ------ */ servant->attr_balance -= amount; /* ------ ---------- end ------------ ------ */ } static CORBA_long impl_Account__get_balance(impl_POA_Account * servant, CORBA_Environment * ev) { CORBA_long retval; /* ------ insert method code here ------ */ retval = servant->attr_balance; /* ------ ---------- end ------------ ------ */ return retval; } |
The missing key stone is the constructor that establishs initial, consistent state for object on creation.
Example 19. account-skelimpl.c fragment - constructor
... static Account impl_Account__create(PortableServer_POA poa, CORBA_Environment * ev) { Account retval; impl_POA_Account *newservant; PortableServer_ObjectId *objid; newservant = g_new0(impl_POA_Account, 1); newservant->servant.vepv = &impl_Account_vepv; newservant->poa = (PortableServer_POA) CORBA_Object_duplicate((CORBA_Object) poa, ev); POA_Account__init((PortableServer_Servant) newservant, ev); /* Before servant is going to be activated all * private attributes must be initialized. */ /* ------ init private attributes here ------ */ newservant->attr_balance = 0; /* ------ ---------- end ------------- ------ */ objid = PortableServer_POA_activate_object(poa, newservant, ev); CORBA_free(objid); retval = PortableServer_POA_servant_to_reference(poa, newservant, ev); return retval; } .. |
Lastly, we have to write a rather generic code to set up the server. We call it account-server.c. It is roughly the same code as in the calculator and echo examples. The code just initializes the ORB and publishes an IOR for the server object.
Example 20. account-server.c
/* * account-server program. Hacked from Frank Rehberger * <F.Rehberger@xtradyne.de>. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <signal.h> #include <orbit/orbit.h> #include "account.h" #include "account-skelimpl.c" /** * test for exception */ static gboolean raised_exception(CORBA_Environment *ev) { return ((ev)->_major != CORBA_NO_EXCEPTION); } /** * in case of any exception this macro will abort the process */ static void abort_if_exception(CORBA_Environment *ev, const char* mesg) { if (raised_exception (ev)) { g_error ("%s %s", mesg, CORBA_exception_id (ev)); CORBA_exception_free (ev); abort(); } } static CORBA_ORB global_orb = CORBA_OBJECT_NIL; /* global orb */ /* Is called in case of process signals. it invokes CORBA_ORB_shutdown() * function, which will terminate the processes main loop. */ static void account_server_shutdown (int sig) { CORBA_Environment local_ev[1]; CORBA_exception_init(local_ev); if (global_orb != CORBA_OBJECT_NIL) { CORBA_ORB_shutdown (global_orb, FALSE, local_ev); abort_if_exception (local_ev, "ORB shutdown failed"); } } /* Inits ORB @orb using @argv arguments for configuration. For each * consumed option from vector @argv the counter of @argc_ptr * will be decremented. Signal handler is set to call * account_server_shutdown function in case of SIGINT and SIGTERM * signals. If error occures @ev points to exception object on * return. */static void account_server_init (int *argc_ptr, char *argv[], CORBA_ORB *orb, CORBA_Environment *ev) { /* init signal handling */ signal(SIGINT, account_server_shutdown); signal(SIGTERM, account_server_shutdown); /* create Object Request Broker (ORB) */ (*orb) = CORBA_ORB_init(argc_ptr, argv, "orbit-local-orb", ev); if (raised_exception(ev)) return; } /* Creates servant and registers in context of ORB @orb. The ORB will * delegate incoming requests to specific servant object. @return * object reference. If error occures @ev points to exception object * on return. */ static Account account_server_activate_service (CORBA_ORB orb, CORBA_Environment *ev) { Account servant = CORBA_OBJECT_NIL; PortableServer_POA poa = CORBA_OBJECT_NIL; PortableServer_POAManager poa_manager = CORBA_OBJECT_NIL; /* get Portable Object Adaptor (POA) */ poa = (PortableServer_POA) CORBA_ORB_resolve_initial_references(orb, "RootPOA", ev); if (raised_exception(ev)) return CORBA_OBJECT_NIL; /* create servant in context of poa container */ servant = impl_Account__create (poa, ev); if (raised_exception(ev)) return CORBA_OBJECT_NIL; /* activate POA Manager */ poa_manager = PortableServer_POA__get_the_POAManager(poa, ev); if (raised_exception(ev)) return CORBA_OBJECT_NIL; PortableServer_POAManager_activate(poa_manager, ev); if (raised_exception(ev)) return CORBA_OBJECT_NIL; return servant; } /* Writes stringified object reference of @servant to file-stream * @stream. If error occures @ev points to exception object on * return. */ static void account_server_export_service_to_stream (CORBA_ORB orb, Account servant, FILE *stream, CORBA_Environment *ev) { CORBA_char *objref = NULL; /* write objref to file */ objref = CORBA_ORB_object_to_string (orb, servant, ev); if (raised_exception(ev)) return; /* print ior to terminal */ fprintf (stream, "%s\n", objref); fflush (stream); CORBA_free (objref); } /* Entering main loop @orb handles incoming request and delegates to * servants. If error occures @ev points to exception object on * return. */ static void account_server_run (CORBA_ORB orb, CORBA_Environment *ev) { /* enter main loop until SIGINT or SIGTERM */ CORBA_ORB_run(orb, ev); if (raised_exception(ev)) return; /* user pressed SIGINT or SIGTERM and in signal handler * CORBA_ORB_shutdown(.) has been called */ } /* Releases @servant object and finally destroys @orb. If error * occures @ev points to exception object on return. */ static void account_server_cleanup (CORBA_ORB orb, Account servant, CORBA_Environment *ev) { /* releasing managed object */ CORBA_Object_release(servant, ev); if (raised_exception(ev)) return; /* tear down the ORB */ if (orb != CORBA_OBJECT_NIL) { /* going to destroy orb.. */ CORBA_ORB_destroy(orb, ev); if (raised_exception(ev)) return; } } /* * main */ int main (int argc, char *argv[]) { Account servant = CORBA_OBJECT_NIL; CORBA_Environment ev[1]; CORBA_exception_init(ev); account_server_init (&argc, argv, &global_orb, ev); abort_if_exception(ev, "init failed"); servant = account_server_activate_service (global_orb, ev); abort_if_exception(ev, "activating service failed"); account_server_export_service_to_stream (global_orb, /* ORB */ servant, /* object */ stdout, /* stream */ ev); abort_if_exception(ev, "exporting IOR failed"); account_server_run (global_orb, ev); abort_if_exception(ev, "entering main loop failed"); account_server_cleanup (global_orb, servant, ev); abort_if_exception(ev, "cleanup failed"); exit (0); } |
The Makefile is the roughly the same as the one in the Calculator example. By now the schema should be clear and you should be able to reuse this Makefile for numerous small projects.
Example 21. Makefile for the Account example
PREFIX=/usr/local CC = gcc TARGETS=account-client account-server ORBIT_IDL=orbit-idl-2 CFLAGS=-DORBIT2=1 -D_REENTRANT -I$(PREFIX)/include/orbit-2.0 \ -I$(PREFIX)/include/linc-1.0 -I$(PREFIX)/include/glib-2.0 \ -I$(PREFIX)/lib/glib-2.0/include LDFLAGS= -Wl,--export-dynamic -L$(PREFIX)/lib -lORBit-2 -llinc -lgmodule-2.0 \ -ldl -lgobject-2.0 -lgthread-2.0 -lpthread -lglib-2.0 -lm IDLOUT=account-common.c account-stubs.c account-skels.c account.h all: $(IDLOUT) account-client account-server account-client : account-client.o account-common.o account-stubs.o account-server : account-server.o account-common.o account-skels.o $(IDLOUT): account.idl $(ORBIT_IDL) account.idl clean: rm -rf *.o *~ $(IDLOUT) distclean: clean rm -rf account-client account-server |
<<< Previous | Home | Next >>> |
Calculator Client Server | Up | How to do garbage collection under CORBA |