Appendix A: A Simple Application

What This Appendix Is About

This appendix contains a description of a one-client, one-server application called rcpsimp that uses TxRPC. The source files for this interactive application are distributed with the TUXEDO System/T software, except they are not included in the RTK binary delivery.

Some Preliminaries

Before you can run this example, the TUXEDO System/T software must be installed so that the files and commands referred to in this chapter are available.

The rpcsimp Application

rpcsimp is a very basic TUXEDO System/T application that uses TxRPC. It has one application client and one server. The client calls the remote procedure calls (operations) to_upper() and to_lower() which are implemented in the server. The operation to_upper() converts a string from lower case to upper case and returns it to the client, while to_lower() converts a string from upper case to lower case and returns it to the client. When each procedure call returns, the client displays the string output on the user's screen.

What follows is a procedure to build and run the example.

Step 1 - Create an application directory

Make a directory for rpcsimp and cd to it:


mkdir rpcsimpdir
cd rpcsimpdir
This is suggested so you will be able to see clearly the rpcsimp files you have at the start and the additional files you create along the way. Use the standard shell ( /bin/sh) or the Korn shell, not C shell, csh.

Step 2 - Set environment variables

Set and export the necessary environment variables.


TUXDIR=<pathname of the TUXEDO System/T root directory>
TUXCONFIG=<pathname of your present working directory>/TUXCONFIG
PATH=$PATH:$TUXDIR/bin
# SVR4, UnixWare
LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$TUXDIR/lib
# HPUX
SHLIB_PATH=$LD_LIBRARY_PATH:$TUXDIR/lib
# RS6000
LIBPATH=$LD_LIBRARY_PATH:$TUXDIR/lib
export TUXDIR TUXCONFIG PATH LD_LIBRARY_PATH SHLIB_PATH LIBPATH
You need TUXDIR and PATH to be able to access files in the TUXEDO System/T directory structure and to execute TUXEDO System/T commands. You need to set TUXCONFIG to be able to load the configuration file. It may also be necessary to set an environment variable (for example, LD_LIBRARY_PATH) if shared objects are being used.

Step 3 - Copy files

Copy the rpcsimp files to the application directory.


cp $TUXDIR/apps/rpcsimp/* .
You will be editing some of the files and making them executable, so it is best to begin with a copy of the files rather than the originals delivered with the software.

Step 4 - List files

List the files.


$ ls
client.c
rpcsimp.mak
rpcsimp.mk
server.c
simp.idl
ubbconfig
wclient.def
wsimpdll.def
$

This list does not include files that are used in the DCE-Gateway example described in "Appendix B".

The files that make up the application are described in the following sections.

IDL Input File - simp.idl

Fig. 1:simp.idl

[uuid(C996A680-9FC2-110F-9AEF-930269370000), version(1.0) ]
interface changecase
{
/* change a string to upper case */
void to_upper([in, out, string] char *str);
/* change a string to lower case */
void to_lower([in, out, string] char *str);
}

This file defines a single interface, changecase version 1.0, with two operations, to_upper and to_lower. Each of the operations takes a NULL-terminated character string, that is both an input and output parameter. Since no ACF file is provided, status variables are not used and the client program must be able to handle exceptions. Each operation has a void return indicating that no return value is generated. simp.idl is used to generate the stub functions (see below).

The Client Source Code - client.c

Fig. 2:client.c

#include <stdio.h>
#include "simp.h"
#include "atmi.h"
main(argc, argv)
int argc;
char **argv;
{
        idl_char str[100];
        unsigned char error_text[100];
        int status;
        if (argc > 1) {/* use command line argument if it exists */
                (void) strncpy(str, argv[1], 100);
                str[99] = '\0';
        }
        else
                (void) strcpy(str, "Hello, world");
        TRY
	{
        to_upper(str);
        (void) fprintf(stdout, "to_upper returns: %s\n", str);
        to_lower(str);
        (void) fprintf(stdout, "to_lower returns: %s\n", str);
        /* control flow continues after ENDTRY */
        CATCH_ALL
                {exc_report(THIS_CATCH); /* print to stderr */
                (void) tpterm();
                exit(1);
		}
        ENDTRY
        (void) tpterm();
        exit(0);
	}
}

The header, simp.h, which is generated by the IDL compiler based on simp.idl, has the function prototypes for the two operations. The simp.h header also includes the header files for the RPC runtime functions (none appear in this example) and exception handling. The atmi.h header file is included because tpterm() is called. If an argument is provided on the command line, then it is used for the conversion to upper and lower case (the default being "hello world"). Exception handling is used to catch any errors. For example, exceptions are generated for unavailable servers, memory allocation failures, communication failures, and so forth. The TRY block encapsulates the two remote procedure calls. If an error occurs, the execution will jump to the CATCH_ALL block which converts the exception ( THIS_CATCH) into a string, prints it to the standard error output using exc_report, and exits. Note that in both the abnormal and normal execution, tpterm() is called to leave the application gracefully. If this is not done, a warning is printed in the userlog() for non-workstation clients, and resources are tied up (until the connection times out, for workstation clients).

The Server Source Code - server.c

Fig. 3:server.c

#include <stdio.h>
#include <ctype.h>
#include "tx.h"
#include "simp.h"
int
tpsvrinit(argc, argv)
int argc;
char **argv;
{
        if (tx_open() != TX_OK) {
                (void) userlog("tx_open failed");
                return(-1);
        }
        (void) userlog("tpsvrinit() succeeds.");
        return(1);
}
void
to_upper(str)
idl_char *str;
{
        idl_char *p;
        for (p=str; *p != '\0'; p++)
                *p = toupper((int)*p);
        return;
}
void
to_lower(str)
idl_char *str;
{
        idl_char *p;
        for (p=str; *p != '\0'; p++)
                *p = tolower((int)*p);
        return;
}

As with client.c, this file includes simp.h.

It also includes tx.h since tx_open() is called (as required by the X/OPEN TxRPC Specification, even if no resource manager is accessed). A tpsvrinit() function is provided to ensure that tx_open() is called once at boot time. This is done automatically, so you may not need to supply it. On failure, -1 is returned and the server fails to boot.

The two operation functions are provided to do the application work, in this case, converting to upper and lower case.

Makefile - rpcsimp.mk

Fig. 4:rpcsimp.mk

CC=cc
CFLAGS=
TIDL=$(TUXDIR)/bin/tidl
LIBTRPC=-ltrpc
all:	client server
# TUXEDO client
client:	simp.h simp_cstub.o
	CC=$(CC) CFLAGS=$(CFLAGS) $(TUXDIR)/bin/buildclient -oclient \
		-fclient.c -fsimp_cstub.o -f$(LIBTRPC)
# TUXEDO server
server:	simp.h simp_sstub.o
	CC=$(CC) CFLAGS=$(CFLAGS) $(TUXDIR)/bin/buildserver -oserver \
		-s changecasev1_0 -fserver.c -fsimp_sstub.o -f$(LIBTRPC)
simp_cstub.o simp_sstub.o simp.h:	simp.idl
	$(TIDL) -cc_cmd "$(CC) $(CFLAGS) -c" simp.idl
#
# THIS PART OF THE FILE DEALING WITH THE DCE GATEWAY IS OMITTED
#
# Cleanup
clean::
	rm -f *.o server $(ALL2) ULOG.* TUXCONFIG
	rm -f stderr stdout *stub.c *.h simpdce.idl gwinit.c
clobber: clean

The makefile builds the executable client and server programs.

The part of the makefile dealing with the DCE Gateway described in "Appendix B" is omitted from the figure.

The client is dependent on the simp.h header file and the client stub object file. buildclient is executed to create the output client executable, using the client.c source file, the client stub object file, and the -ltrpc RPC runtime library.

The server is dependent on the simp.h header file and the server stub object file. buildserver is an output server executable, using the server.c source file, the server stub object file, and the -ltrpc RPC runtime library.

The client and server stub object files and the simp.h header file are all created by running the tidl compiler on the IDL input file.

The clean target removes any files that are created while building or running the application.

The Configuration File - ubbconfig

The following is a sample ASCII configuration file. The machine name, TUXCONFIG, TUXDIR, and APPDIR must be set based on your configuration.

Fig. 5:ubbconfig

*RESOURCES
IPCKEY		187345
MODEL		SHM
MASTER		SITE1
PERM		0660
*MACHINES
UNAME	LMID=SITE1
	TUXCONFIG="<TUXCONFIG>"
	TUXDIR="<TUXDIR>"
	APPDIR="<APPDIR>"
#	MAXWSCLIENTS=10
*GROUPS
GROUP1	LMID=SITE1	GRPNO=1
*SERVERS
server SRVGRP=GROUP1 SRVID=1
#WSL SRVGRP=GROUP1 SRVID=2 RESTART=Y GRACE=0
#	CLOPT="-A -- -n <address> -x 10 -m 1 -M 10 -d <device>"
#
# TUXEDO-to-DCE Gateway
#simpgw SRVGRP=GROUP1 SRVID=2
*SERVICES
*ROUTING

The lines for MAXWSCLIENTS and WSL are uncommented and are used for a /WS configuration. The literal <address> for the /WS listener must be set as described in the WSL(5) manual page.

MS-DOS and Windows Makefile - rpcsimp.mak

Fig. 6:rpcsimp.mak

# Model for dos client
MODEL=L
WINMODEL=M
# Generate MS-DOS Client
dos: client.exe
simp.c: simp.IDL
        TIDL -cstub simp.c -keep c_source -server none simp.IDL
client.obj: client.c
        CL -I. -c -A$(MODEL) client.c
simp.obj: simp.c
        CL -I. -c -A$(MODEL) simp.c
client.exe: simp.obj client.obj
        buildclt -v -m$(MODEL) -cm -o client.exe -f "/ST:15000 /CO" \
        -f client.obj -f simp.obj -f$(MODEL)trpc.lib -l$(MODEL)libsock.lib
# Generate Windows client using MSC QuickWin
win: wclient.exe
wsimp.C:        simp.IDL
        TIDL -cstub wsimp.c -keep c_source -server none simp.IDL
wclient.c:      client.c
        copy client.c wclient.c
wclient.obj: wclient.c
        CL /mQ -A$(WINMODEL) -I. -D_TM_WIN -Od -c wclient.C
wsimp.obj: wsimp.c
        CL /mQ -A$(WINMODEL) -I. -D_TM_WIN -Od -c wsimp.C
wclient.exe: wsimp.obj wclient.obj
        link wclient.obj wsimp.obj, wclient.exe , NUL, /NOD wtrpc \
                wtuxws libw $(WINMODEL)libcewq Wlibsock,wclient.def
# Generate DLL
# Must be built with large model
dll:    WSIMPDLL.DLL
simpdll.C:      simp.IDL
        TIDL -cstub simpdll.c -keep c_source -server none simp.IDL
simpdll.OBJ:    simpdll.C
        CL -D_TMX="_far _pascal" -AL -I. -Aw -G2swx -Zp -D_TM_WIN -Od -c simpdll.C
WSIMPDLL.DLL:   simpdll.OBJ
        LINK  simpdll.OBJ , WSIMPDLL.DLL /CO /ALIGN:16, NUL,  /NOD WLIBTRPC\
                WTUXWS WLIBSOCK LIBW LDLLCEW, WSIMPDLL.DEF
        RC -K WSIMPDLL.DLL
        IMPLIB WSIMPDLL.LIB WSIMPDLL.DLL
clean:
        if exist resptmp del resptmp
        if exist simp.c del simp.c
        if exist simp.h del simp.h
        if exist wsimp.c del wsimp.c
        if exist wclient.c del wclient.c
        if exist simpdll.c del simpdll.c
        del *.obj
        del *.exe
        if exist wsimpdll.lib del wsimpdll.lib
        if exist wsimpdll.dll del wsimpdll.dll

Building MS-DOS and Windows clients is different enough from native clients that a separate makefile is desirable. This makefile builds an executable MS-DOS client, a quick Windows client using Microsoft QuickWin, and a Windows Dynamic Link Library (DLL).

The dos target builds a MS-DOS client. The first step is to execute tidl on the IDL input file (the same one that is used for native clients and servers), listed above. Due to the filename limitations, the -cstub option is used to rename the output file simp.c. Also note that the -server none option is used to inhibit output of the server stub.

The client stub is simply compiled into an object file, simp.obj. The example uses the "large" memory model and the Microsoft C compiler, but the model could be "large" and a different compiler could be used. The -I option is used to include the generated header, simp.h, in the current directory. Similarly, the client application program, client.c, is compiled to the object file client.obj.

Finally, buildclt is called to link object files and libraries to form the executable, client.exe. The -v option prints out the commands being executed, the -m option is used to specify the large memory model, and the -c option is used to specify the Microsoft C compiler (see buildclt(1) for further details). The first -f option sets the stack size, the second and third -f options include the object files, and mtrpc.lib (the RPC runtime library) is also included using a -f option. The networking library, mlibsock.lib (Novell's Lan Workplace for MS-DOS), is included using a -l option.

Before running this client, the application must be booted (as described below) and the WSNADDR environment variable must be set (see the BEA TUXEDO /Workstation Guide for further details).

The win target builds a Windows client using the Microsoft QuickWin feature (Borland's Easy Win provides similar functionality). It allows a character-based C program to be compiled and run as a Windows program, without modification. The client stub is generated, as above, with a "w" prefix ( wsimp.c). The client code is copied to a new name and compiled. The /mQ compilation option invokes the QuickWin feature, as does the use of the libcewq library on the link command line. It is important to remember that when compiling any Windows program that uses the TUXEDO System software, -D_TM_WIN must be defined. The buidclt command cannot be used because non-standard libraries are being used to link the executable. The link command line contains the client stub and application object files, the RPC and TUXEDO system libraries, and the Windows, QuickWin, and networking libraries. The definition file, wclient.def, is listed below; it simply sets a valid heap and stack size.

The makefile does not demonstrate building OS/2 programs. Compilation is similar to the MS-DOS example above, but with the correct options for these platforms. Remember to use the -D_TM_OS2 when compiling for OS/2. See the bankapp sample application under $TUXDIR/apps/ws for examples of complete applications for these platforms.

The makefile does include a more interesting feature not shown in the bankapp sample, the creation of a DLL. One common use of the TxRPC interface operations is to create one or more interfaces for use in applications via a dynamic link library. Use of a DLL is necessary for most visual builders (such as Visual Basic, Gupta SQL Windows, and others) where the programmer simply indicates the name of a DLL, specifies a function prototype, and calls the function directly from the application.

The makefile takes the output of the IDL compiler and creates a DLL, wsimpdll.dll (the source for the client application, assumed to be written with a visual builder, is not provided). The client stub is generated exactly the same as for the native and MS-DOS clients (in the makefile, it is renamed simpdll.c to differentiate it from the MS-DOS client). Special Windows options (for Microsoft C, for example, -Aw -G2swx -Zp) are used to generate the DLL object file, and the -D_TM_WIN option is used for TUXEDO /WS.

Since the DLL always has a different data segment and text segment from the application code calling it, all pointers provided to and returned from the operations must be declared as far (4-byte) pointers. Similarly, functions are declared as far. Also, Windows functions are traditionally declared to have the _pascal calling convention. The header file and stub are automatically generated to easily allow for the declarations to be changed, using C pre-processor definitions. The definition _TMF (for "far") appears before all pointers in the header file and is automatically defined as "_far" when compiling for Windows or OS/2. Similarly, _TMX (for "eXport") appears before all declared functions. By default, _TMX is defined to nothing. When compiling a stub for inclusion in a DLL, _TMX should be defined using -D_TMX="_far _pascal". Also, the files to be included in the DLL must be compiled with the large memory model.

Once the simpdll.obj object file is created using the compiler, the DLL, WSIMPDLL.DLL, is created using the linker, including the object file, the RPC and TUXEDO System libraries, the Windows library, the networking library, and the definition file, wsimpdll.def (listed below). The resource compiler is run on the resulting DLL, and implib is executed to generate the output library, WSIMPDLL.LIB, used for linking with applications.

Windows Definition Files - wclient.def and wsimpldll.def

Fig. 7:wclient.def

NAME           WINDOWAPI
EXETYPE        WINDOWS 3.0
CODE           PRELOAD MOVEABLE DISCARDABLE
DATA           PRELOAD MOVEABLE
HEAPSIZE       1024
STACKSIZE      8096

This definition file is used when linking the Windows client program.

Fig. 8:wsimpdll.def

LIBRARY        WSIMPDLL
PROTMODE
DESCRIPTION    'TUXEDO /RPC SAMPLE DLL'
CODE	PRELOAD MOVEABLE DISCARDABLE
DATA           PRELOAD SINGLE
HEAPSIZE       1024
SEGMENTS     WEPSEG PRELOAD FIXED
;EXETYPE        WINDOWS
EXPORTS
        TO_UPPER           @1
        TO_LOWER           @2

This definition file is used when linking the DLL file. Note that it lists the two operation names. They are upper case because functions declared with the _pascal modifier are converted to upper case.

Step 5 - Modify the Configuration

Edit the ASCII ubbconfig configuration file to provide location-specific information (for example, your own directory pathnames and machine name), as described in the next step. The text to be replaced is enclosed in angle brackets. You need to substitute the full pathnames for TUXDIR TUXCONFIG, and APPDIR, and the name of the machine on which you are running. Here is a summary of the required values:

TUXDIR

the full pathname of the root directory of the TUXEDO System/T software, as set above.

TUXCONFIG

the full pathname of the binary configuration file, as set above.

APPDIR

the full pathname of the directory in which your application will run.

UNAME

the machine name of the machine on which your application will run; this is the output of the UNIX uname -n command

For a /WS configuration, the MAXWSCLIENTS and WSL lines must be uncommented and the address must be set for the Workstation Listener (see WSL(5) for further details).

Step 6 - Build the Application

Build the client and server programs by running


make -f rpcsimp.mk TUXDIR=$TUXDIR

Step 7 - Load the Configuration

Load the binary TUXCONFIG configuration file by running


tmloadcf -y ubbconfig

Step 8 - Boot the Configuration

Boot the application by running


tmboot -y

Step 9 - Run the client

The native client program can be run by optionally specifying a string to be converted to upper and then lower case.


$ client HeLlO
to_upper returns: HELLO
to_lower returns: hello
$
When running on /WS, the WSNADDR environment variable must be set to match the address specified for the WSL program. The MS-DOS client program can be run in exactly the same manner as the native client. The Windows client can be run by executing

>win wclient
The dynamic link library may be used in a separately developed application (for example, a visual builder).

Step 10 - Monitor the RPC Server

The RPC server can be monitored using tmadmin(1). In the following example, psr and psc are used to view the information for the server program. Note that the length of the RPC service name causes it to be truncated in terse mode (indicated by the "+"); verbose mode can be used to get the full name.

Fig. 9:tmadmin psr and psc output

$ tmadmin
> psr
a.out Name     Queue Name  Grp Name      ID RqDone Load Done Current Service
----------     ----------  --------      -- ------ --------- ---------------
BBL            587345      SITE1          0      0         0 (  IDLE )
server         00001.00001 GROUP1         1      2       100 (  IDLE )

> psc Service Name Routine Name a.out Name Grp Name ID Machine # Done Status ------------ ------------ ---------- -------- -- ------- ------ ------ ADJUNCTBB ADJUNCTBB BBL SITE1 0 SITE1 0 AVAIL ADJUNCTADMIN ADJUNCTADMIN BBL SITE1 0 SITE1 0 AVAIL changecasev+ changecasev+ server GROUP1 1 SITE1 2 AVAIL

> verbose Verbose now on.

> psc -g GROUP1 Service Name: changecasev1_0 Service Type: USER Routine Name: changecasev1_0 a.out Name: /home/sdf/trpc/rpcsimp/server Queue Name: 00001.00001 Process ID: 8602, Machine ID: SITE1 Group ID: GROUP1, Server ID: 1 Current Load: 50 Current Priority: 50 Current Trantime: 30 Requests Done: 2 Current status: AVAILABLE > quit

Step 11 - Shut down the Configuration

Shut down the application by running


tmshutdown -y

Step 12 - Clean up

Clean up the created files by running


make -f rpcsimp.mk clean