Simple account client and server

In this third simple example, we will see how we can setup 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 5-7. Account IDL file

   interface Account {
      void deposit (in unsigned long amount);
      void withdraw (in unsigned long amount);
      readonly attribute long balance;
   };

Account Client

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 5-8. Account Client Source

/*------- Account client (account-client.c)-----------------------------------------*/
#include <stdio.h>
#include <orb/orbit.h>
#include "account.h" 
 
int main (int argc, char *argv[]) {

  Account acc_client;         /* The client-side Account object */
  CORBA_long val;             /* client stores the balance here */
  PortableServer_POA poa;
  CORBA_ORB orb=NULL;         /* This is our ORB */
  char *ior;                  /* an IOR is one way a client can locate a server */
  CORBA_Environment ev;       /* CORBA exception information */

  CORBA_exception_init(&ev);

  /* Initialize the orb. The initialization routine checks the command
   * line parameters for directives that affect the orb */
  orb = CORBA_ORB_init(&argc, argv, "orbit-local-orb", &ev);
 
  /* Bind  our client Account to the account on the server using the
   * IOR to look it up (the ior was gotten above in the server code) */

  acc_client = CORBA_ORB_string_to_object(orb, argv[1], &ev);
  if (!acc_client) {
    printf("Cannot bind to %s\n (pass the IOR as arg 1)\n", argv[1]);
    exit(1);
  }
  val = Account__get_balance(acc_client, &ev);
  printf ("Initial balance is %d.\n",val);

  printf("Make a deposit, how :");
  scanf("%d",&val); 
  Account_deposit(acc_client, (CORBA_unsigned_long) val, &ev);
  val = Account__get_balance(acc_client, &ev);
  printf ("Balance is %d.\n",val);

  Account_withdraw(acc_client, (CORBA_unsigned_long) 5, &ev);
  val = Account__get_balance(acc_client, &ev);
  printf ("Balance is %d.\n",val);

  /* We're done now. Do some cleanup */
  CORBA_Object_release(acc_client, &ev);
  exit (0);
}

Account Server

For the server, like in the previous example, we first generate the source file account-skelimpl.cthat will receive the implementation code for the methods. This is done once again with orbit-idl --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 5-9. Skeleton Implementation for the account server

/*** App-specific servant structures ***/
typedef struct {
POA_Account servant;
PortableServer_POA poa;

CORBA_long attr_balance;

} impl_POA_Account;
So, the server methods (withdraw and deposit) will have to manage the balance of the account through the servant->attr_balance(the servant variable is passed as parameter to each method).

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:

static void
impl_Account_deposit(impl_POA_Account *servant,
CORBA_unsigned_long amount,
CORBA_Environment *ev)
{
    printf("server calls deposit method...\n");
    servant->attr_balance += amount;
}

static void
impl_Account_withdraw(impl_POA_Account *servant,
CORBA_unsigned_long amount,
CORBA_Environment *ev)
{
    printf("server calls withdraw method...\n");
    servant->attr_balance -= amount;
}

static CORBA_long
impl_Account__get_balance(impl_POA_Account *servant,
CORBA_Environment *ev)
{
CORBA_long retval;
     retval= servant->attr_balance;
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 publish an IOR for the server object.

Example 5-10. Account Server C Source

/*------- Account server (account-server.c)-----------------------------------------*/
#include "account-skelimpl.c"
#include <stdio.h>

int main (int argc, char *argv[]) {
  CORBA_ORB                 orb;
  CORBA_Environment*        ev;
  PortableServer_ObjectId*  oid;
  Account                   account;
  PortableServer_POA        root_poa;
  PortableServer_POAManager pm;
  CORBA_char*               ior;

  ev = g_new0(CORBA_Environment,1);
  CORBA_exception_init(ev);
  
  /* Initialize the orb. The initialization routine checks the command
   * line parameters for directives that affect the orb */

  orb = CORBA_ORB_init(&argc, argv, "orbit-local-orb", ev);

  root_poa = (PortableServer_POA)
    CORBA_ORB_resolve_initial_references(orb, "RootPOA", ev);

  /* ask for instanciation of one object account */
  account = impl_Account__create(root_poa, ev);

  /* Here we get the IOR for the acc object.  Our "client" will use
   * the IOR to  find the server to connect to */
  ior = CORBA_ORB_object_to_string(orb, account, ev);

  /* print out the IOR, just so you can see what it looks like */
  printf ("%s\n", ior);

  pm = PortableServer_POA__get_the_POAManager(root_poa, ev);
  PortableServer_POAManager_activate(pm,ev);

  CORBA_ORB_run(orb, ev);
  return (0);
}

Compiling the Server and the Client

The Makefile is the roughly the same as the one in the Calculator example. This time however, some variables have been introduced to make it more customizable. By changing the PROJECT variable, you should be able to reuse this Makefile for numerous small projects.

Example 5-11. Makefile for the Account example

PROJECT = account
CC = gcc
ORBIT_IDL = /usr/bin/orbit-idl
ORBIT_CFLAGS = -I/usr/lib/glib/include -I/usr/include
ORBIT_LIBS = -L/usr/lib -lORBit -lIIOP -lORBitutil -lglib -lm
CFLAGS = $(ORBIT_CFLAGS)
LFLAGS = $(ORBIT_LIBS)

all : idltargets $(PROJECT)-client $(PROJECT)-server

$(PROJECT)-client : $(PROJECT)-client.o $(PROJECT)-common.o $(PROJECT)-stubs.o
    $(CC) -o $(PROJECT)-client $(PROJECT)-client.o $(PROJECT)-stubs.o $(PROJECT)-common.o  -lIIOP -lORBit -lORBitutil $(LFLAGS)

$(PROJECT)-server : $(PROJECT)-server.o $(PROJECT)-skels.o $(PROJECT)-common.o
    $(CC) -o $(PROJECT)-server $(PROJECT)-server.o $(PROJECT)-skels.o $(PROJECT)-common.o  -lIIOP -lORBit -lORBitutil $(LFLAGS)


clean :
    rm *.[oa] $(PROJECT)-client $(PROJECT)-server

real-clean : clean
    rm -f $(PROJECT)-stubs.[oc] $(PROJECT)-skels.[oc] $(PROJECT).h $(PROJECT)-common.[oc]
 
idltargets : $(PROJECT).idl
    $(ORBIT_IDL) $(PROJECT).idl

# individual rules

$(PROJECT)-stubs.c : $(PROJECT).idl
    $(ORBIT_IDL) $(PROJECT).idl

$(PROJECT)-common.c : $(PROJECT).idl
    $(ORBIT_IDL) $(PROJECT).idl

$(PROJECT)-skels.c : $(PROJECT).idl
    $(ORBIT_IDL) $(PROJECT).idl

$(PROJECT)-client.c : $(PROJECT).h

$(PROJECT)-server.c : $(PROJECT).h

$(PROJECT).h : $(PROJECT).idl
    $(ORBIT_IDL) $(PROJECT).idl