Page 1231
boolean * bAuthorized; { char * UserID ,* Password; WRBReturnCode Ret; /* Get the UserID and Password from WRB */ UserID = WRBGetUserID (WRBCtx); Password = WRBGetPassword (WRBCtx); /* Check to see if this user has access */ *bAuthorized = ((userID != (char*)0) && !strcmp(UserID, "scott") && (password != (char*)0) && !strcmp(Password, "tiger")) ; Return (WRB_DONE); } WRBReturnCode Hello_Shut (WRBCtx, clientcxp) void * WRBCtx; void * clientcxp; { return (WRB_DONE); } WRBReturnCode Hello_Exec (WRBCtx, clientcxp) void * WRBCtx; void * clientcxp; { /* set up the response HTML */ char * HTML = "Content-type: text/html\n<HTML>\n<HEAD>\n<TITLE>Hello, world! </TITLE>\n</HEAD>\n<BODY>Hello, world!</BODY>\n</HTML>"; /* Write the response to the WRB */ WRBClientWrite(WRBCtx, HTML, strlen(HTML)); return (WRB_DONE); }
Now I dissect the Hello_WRB.c code:
Page 1232
Essentially, the cartridge does not do much at this point. I now concentrate on performing transactions within the Oracle database via a cartridge. To show the true power of the Oracle development tools, I implement a thin layer of Pro*C code that in turn calls into a stored packaged procedure to execute the transaction. Listing 54.3 contains the package.
Listing 54.3. A PL/SQL package to insert a new customer.
CREATE OR REPLACE PACKAGE pkgCustomer as procedure InsertNew ( inName in varchar2 ,inAddress in varchar2 ,inCity in varchar2 ,inState in varchar2 ,inPhone in varchar2 ); end; / show errors; create or replace package body pkgCustomer as procedure InsertNew ( inName in varchar2 ,inAddress in varchar2 ,inCity in varchar2 ,inState in varchar2 ,inPhone in varchar2 ) as begin INSERT INTO Customer ( ID ,Name ,Address ,City ,State ,Phone ) values (
Page 1233
CustomerSeq.nextval ,inName ,inAddress ,inCity ,inState ,inPhone ); end; end; / show errors
This simple package contains a single procedure InsertNew that inserts a new row into the customer table. Next, Listing 54.4 creates some Pro*C code that provides the interface between Oracle and the cartridge.
Listing 54.4. Pro*C can become your bridge between Oracle and the cartridge code.
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <sqlproto.h> #define TO_VARCHAR(Arr, C) \ strcpy((Arr).arr, (C)); (Arr).len = strlen((C)) EXEC SQL INCLUDE sqlca; char * GetError (void); int OracleErrorHandler (char *Buffer); int OracleLogin (char *usr, char *pwd); void OracleLogout (); void InsertCustomer (char *szName, char *szAddress, char *szCity ,char *szState char *szPhone); EXEC SQL BEGIN DECLARE SECTION; VARCHAR UserName [30]; VARCHAR Password [30]; VARCHAR Name [40]; VARCHAR Address [35]; VARCHAR City [27]; VARCHAR State [ 2]; VARCHAR Phone [12]; EXEC SQL END DECLARE SECTION; char ErrorBuffer [512];
continues
Page 1234
Listing 54.4. continued
int OracleLogin(char *usr, char *pwd) { TO_VARCHAR(UserName, usr); TO_VARCHAR(Password, pwd); EXEC SQL WHENEVER SQLERROR DO OracleErrorHandler (ErrorBuffer); EXEC SQL CONNECT :UserName IDENTIFIED BY :Password; if (ErrorBuffer[0] != `int OracleLogin(char *usr, char *pwd) { TO_VARCHAR(UserName, usr); TO_VARCHAR(Password, pwd); EXEC SQL WHENEVER SQLERROR DO OracleErrorHandler (ErrorBuffer); EXEC SQL CONNECT :UserName IDENTIFIED BY :Password; if (ErrorBuffer[0] != `\0') return -1; // if error return 0; // success } void OracleLogout() { EXEC SQL COMMIT WORK RELEASE; } char * GetError (void) { return ErrorBuffer; } void InsertCustomer (char *szName, char *szAddress, char *szCity ,char *szState char *szPhone) { memset (ErrorBuffer, `\0', sizeof(ErrorBuffer)); TO_VARCHAR(Name, szName ); TO_VARCHAR(Address, szAddress ); TO_VARCHAR(City, szCity ); TO_VARCHAR(State, szState ); TO_VARCHAR(Phone, szPhone ); EXEC SQL WHENEVER SQLERROR DO OracleErrorHandler (ErrorBuffer); EXEC SQL EXECUTE BEGIN pkgCustomer.InsertNew ( :Name ,:Address ,:City ,:State ,:Phone ); END; END-EXEC; EXEC SQL COMMIT WORK; return; }') return -1; // if error return 0; // success } void OracleLogout() { EXEC SQL COMMIT WORK RELEASE; } char * GetError (void) { return ErrorBuffer; } void InsertCustomer (char *szName, char *szAddress, char *szCity ,char *szState char *szPhone) { memset(ErrorBuffer, `int OracleLogin(char *usr, char *pwd) { TO_VARCHAR(UserName, usr); TO_VARCHAR(Password, pwd); EXEC SQL WHENEVER SQLERROR DO OracleErrorHandler (ErrorBuffer); EXEC SQL CONNECT :UserName IDENTIFIED BY :Password; if (ErrorBuffer[0] != `\0') return -1; // if error return 0; // success } void OracleLogout() { EXEC SQL COMMIT WORK RELEASE; } char * GetError (void) { return ErrorBuffer; } void InsertCustomer (char *szName, char *szAddress, char *szCity ,char *szState char *szPhone) { memset (ErrorBuffer, `\0', sizeof(ErrorBuffer)); TO_VARCHAR(Name, szName ); TO_VARCHAR(Address, szAddress ); TO_VARCHAR(City, szCity ); TO_VARCHAR(State, szState ); TO_VARCHAR(Phone, szPhone ); EXEC SQL WHENEVER SQLERROR DO OracleErrorHandler (ErrorBuffer); EXEC SQL EXECUTE BEGIN pkgCustomer.InsertNew ( :Name ,:Address ,:City ,:State ,:Phone ); END; END-EXEC; EXEC SQL COMMIT WORK; return; }', sizeof(ErrorBuffer)); TO_VARCHAR(Name, szName ); TO_VARCHAR(Address, szAddress ); TO_VARCHAR(City, szCity ); TO_VARCHAR(State, szState ); TO_VARCHAR(Phone, szPhone ); EXEC SQL WHENEVER SQLERROR DO OracleErrorHandler (ErrorBuffer); EXEC SQL EXECUTE BEGIN pkgCustomer.InsertNew ( :Name ,:Address ,:City ,:State ,:Phone ); END; END-EXEC; EXEC SQL COMMIT WORK; return; }
Page 1235
int OracleErrorHandler (char* Buffer) { EXEC SQL WHENEVER SQLERROR CONTINUE; sprintf(&Buffer[strlen(Buffer)], "Database Error: % .70s", sqlca.sqlerrm.sqlerrmc); EXEC SQL ROLLBACK RELEASE; return(-1); }
Listing 54.4 is the full source module required for the interface. The InsertCustomer function provides a mechanism to take in C scalar variables and pass them to a stored procedure in Oracle. The other functions are required to connect and disconnect as well as handle errors. This module, after it is precompiled and then compiled into object code, can be linked with a cartridge object. It is good to employ a standard template such as this when building a library of cartridges. The preceding cartridge can call these native C functions directly.
This seems on the surface to be a trivial architecture, but here is why it is so powerful. State information for a given session can be stored at any level. The C layer can store state information in variables as well as to some form of disk storage. The packaged subprograms can store information in variables as well as maintain database locks and cursor states. The lifetime of both of these layers is the lifetime of the session.
Web Application Server provides a runtime environment for server-side Java applications to run. This is a cartridge that provides instances of the Java virtual machine. All Java code is processed as a Java application, not a Java applet. This means that the Java security manager is not used. Because the Java application resides on the server, there is really no security threat. The Java cartridge provides a Web toolkit containing class libraries for creating dynamic HTML and RDBMS access. No development environment is provided to develop the Java source code. Other tools are required for development and testing, such as Symantec Visual Caf , SunSoft Java Development Kit (JDK), or others. The current version of the Java language supported is 1.0.2. Version 1.1 will be supported when Web Application Server version 3.1 is released in late 1997.
The Java cartridge is invoked only by the Web server. It routes a HTTP request to Java. The Java cartridge runs the Java request. The response of the Java application is an HTTP stream commonly in the form of HTML. The HTML is ultimately routed back to the client browser.
The following Java application code displays "Hello, world!" as standard output:
Class HelloWorld { public static void main (String args[]) { System.out.println ("Hello, world!"); } }