Reusing Legacy Code | CGI, ISAPI and NSAPI Programs |
This chapter describes how you write server-side programs for Internet applications. These apply equally to ISAPI, NSAPI and CGI programming.
NetExpress provides extensions to COBOL which make it much easier to write Internet server-side programs than with other languages. You can receive data from a form using the ACCEPT verb, and send a result back to a Web browser using either the DISPLAY verb or Embedded HTML (EHTML). EHTML enables you to construct HTML pages on the fly from within a COBOL program. This chapter covers four main areas:
When you use Form Designer to paint your HTML forms, the Internet Application Wizard can generate the skeleton of a server-side program to read in the data, and return a result. You can then fill in the additional code to process the data. This means that you can start creating applications without needing to know the detailed syntax for receiving input and returning output, although this information is useful when you start creating more sophisticated applications.
The server-side programs generated by the Internet Application Wizard use ACCEPT to receive data from a form, and EHTML to display a form.
A server-side program can be executing simultaneously several times for different users. If it is a CGI program, each time the program is executed it runs as a separate process; if it is an ISAPI or NSAPI program, each time it is executed it runs in a separate thread. If your server-side program needs to access shared resources (for example files and databases), you must include logic to handle resource contentions.
COBOL includes a rich set of functions for sharing files. For more information, see the chapter Sharing Files in File Handling.
Each control on a form has a name and a value. As explained in the chapter Forms and HTML, when the end-user submits the form, the information on the form is sent to the server-side program as a set of name/value pairs. Extensions to COBOL syntax enable you to associate the names of the controls on the form directly with COBOL data items in your server-side program. When the data in the form is posted to the server-side program, the data items are set to the values of the controls on the form.
The form below has a field that has been named "name". The end-user types in the value "Bob".
The server-side program ties into each of the named controls by having a declaration like this in Working-Storage:
01 inputdata is external-form. 03 name-field pic x(30) identified by "name". 03 email pic x(15) identified by "emailid". 03 phone-no pic x(30) identified by "phone".
When the server-side program is run by the end-user clicking the Send Form button (which is the Submit control in this example), the value Bob is transferred into data item NAME-FIELD when the following statement is executed:
accept input-form
When you create a form with Form Designer, it can generate a skeleton server-side application for you, with a copyfile which declares the data items you need to match up to each of the controls on the form.
Use the following syntax to map a COBOL data item to CGI input:
level-number data-name-1 IS EXTERNAL-FORM.
This declaration says that group item data-name-1 and its subordinate fields can have their values set by input from an HTML form. To map elementary data items to NAME attributes.
sub-level-number data-name-2 picture-string IDENTIFIED BY name.
where the parameters are:
sub-level-number | A COBOL data item level number. |
data-name-2 | A COBOL data name. |
picture-string | A COBOL picture string. PIC X(n), PIC 9(n), and numeric edited fields are allowed. Strings longer than n characters are truncated, strings less than n characters are padded from the left with spaces. |
name | The value of a Name or Groupname property on the corresponding control on the Form. |
This example shows a set of data items mapped to the fields on a form.
01 input-form is external-form. 03 name-field pic x(30) identified by "Name". 03 phone-no pic x(30) identified by "Phone". 03 email pic x(15) identified by "EmailID".
NetExpress provides two alternative methods for outputting HTML from a COBOL program to a Web browser. Both methods enable you to substitute the values of COBOL variables into the HTML output:
With EHTML you can include HTML in a COBOL program between EXEC HTML and END-EXEC delimiters. HTML statements can either be coded directly into your source program, or included as copyfiles.
The EXEC HTML mechanism provides complete program control over the output of HTML. The HTML source is actually linked into your server-side program, so if you change the HTML copyfiles, you need to recompile and relink the program
The COBOL DISPLAY verb enables you to display an HTML page held in an external file.
Embedded HTML (EHTML) enables you to output HTML directly from a COBOL program. The server-side programs generated by the Internet Application Wizard both use EHTML to return results to the end-user.
EHTML enables you to include complete HTML pages as copyfiles inside your program but with the advantage that you don't need any special data declarations. You can also use partial HTML pages as copyfiles, or output individual HTML statements, and build up a complete page under complete program control. This gives you tremendous flexibility.
You embed HTML into a COBOL program by using the EXEC HTML statement. This is translated by the EHTML preprocessor, htmlpp.
The EXEC HTML and END-EXEC keywords enable you to embed HTML directly into your COBOL source code. You must put keywords EXEC and HTML on the same line; otherwise there are no formatting restrictions.
This is the general format:
EXEC HTML [htmloutput] [copy "file.htm".] END-EXEC
where the parameters are:
htmloutput | HTML markup for output to a Web browser. |
file.htm | A file containing HTML markup |
The EHTML preprocessor does not validate or parse your HTML markup - it just outputs it directly to the Web server which started the CGI program.
With fixed format COBOL source code, the EHTML preprocessor treats column 8 as a virtual column 1 when outputting embedded HTML. This simplifies use of the HTML <PRE> tag. However, if you include a copyfile with an extension of .htm, the preprocessor treats the copyfile as free format source. You can override this behavior with the preprocessor directive, NOAUTOFORMAT.
You can substitute variable data from your COBOL program directly into the HTML output by using substitution markers. Substitution markers are COBOL data-names prefixed by a colon (:), as shown below:
:data-name
You should always use display items inside EHTML; if you use binary data items the data displayed on the form is not human-readable.
Colons followed by a space or punctuation marker are always output as colons, and not treated as substitution markers. You can also force a colon to be output as a colon by preceding it by a backslash (\) character.
working-storage section. 01 acct-code pic 9(8). ... procedure division. ... *> First colon is followed by a space, so it is *> not treated as a substitution marker by *> EXEC HTML. Second colon is followed by a *> data-name, so it is treated as a *> substitution marker. The third colon is preceded *> by a backslash, so it is treated as a colon EXEC HTML Account Code: :acct-code <BR> \:acct-code END-EXEC
To qualify a substitution marker, use a period (.) between the group item data-name and the elementary item data-name. For example:
working-storage section. 01 customer. 03 name pic x(80). 03 acct-code pic 9(8). ... procedure division. ... *> customer.acct-code equivalent inside EXEC HTML *> block to acct-code of customer in COBOL source. EXEC HTML Account Code: :customer.acct-code <BR> END-EXEC
You can also reference modify substitution markers:
working-storage section. 01 acct-code pic 9(8). ... procedure division. ... *> Reference modification outputs first four characters of *> acct-code. EXEC HTML Account Code: :acct-code(1:4) <BR> END-EXEC
Note: A :data-name reference is not treated as a substitution marker if it is preceded by any of a number of text strings. A list of these text strings can be found in the online Help. Click Help Topics on the NetExpress Help menu, then on the Contents tab click Reference then Embedded HTML, then Substitution Markers.
To run the EHTML preprocessor, you need to set the following compiler directive:
preprocess(htmlpp) [preprocessor-directives] endp
All programs generated by the Internet Application Wizard start with a $SET statement to run the EHTML preprocessor:
$SET preprocess(htmlpp) endp
You can also set EHTML preprocessor directives by creating an ASCII file called htmlpp.dir, and putting it on the $COBDIR path for your NetExpress system.
The EHTML preprocessor directives are documented in the online reference. Click Help Topics on the NetExpress Help menu, then on the Contents tab click Reference then Embedded HTML .
You can send an HTML page to a Web browser using the DISPLAY verb. The HTML page can include substitution markers for variable data to be supplied by the program. The substitution markers are not standard HTML; however the COBOL system replaces them with the variable data before sending the page to the Web browser. Each substitution marker is in one of the two following formats:
%%name%%
or
%%!s name%%
where name is a name mapped to a COBOL data item by the IDENTIFIED BY clause on a data declaration. Use %%name%% to output the data item exactly as it is. Use %%!s name%% to strip any trailing spaces from the data item. When you are outputting plain text, the Web browser usually removes trailing spaces for you, but if you are using DISPLAY to put values into form elements, you need to strip trailing blanks using this feature.
This is the syntax to map a COBOL group data item to an output page:
level-number data-name-1 IS EXTERNAL-FORM identified by "pagefile.htm".
where:
level-number | A COBOL data item level number for a group item |
data-name-1 | A COBOL data-name |
pagefile.htm | The filename of an HTML page for output |
This declaration says that HTML form pagefile.htm can have data set from group item data-name-1 and its subordinate fields. To map elementary data items to form names:
sub-level-number data-name-2 picture-string IDENTIFIED BY name.
where:
sub-level-number | A COBOL data item level number. |
data-name-2 | A COBOL data-name |
picture-string | A COBOL picture string. PIC X(n), PIC 9(n), and numeric edited fields are allowed. Strings longer than n characters are truncated, strings less than n characters are padded from the left with spaces |
name | The name of a substitution marker in the HTML page to be outputs |
This example shows a set of data items mapped to the fields on a form.
01 output-form is external-form identified by "outpage1.htm". 03 name-field pic x(30) identified by "Name". 03 phone-no pic x(30) identified by "Phone". 03 email pic x(15) identified by "EmailID".
This is an example HTML page with substitution markers set up for the names above:
<HTML><BODY> <H1>This is the output-form </H1> <P>Dear %%Name%%,</P> <P>You entered the following information</P> <UL> <LI>'phone number - <B>%%Phone%%</B></LI> <LI>Email id - <B>%%EmailID%%</B></LI> </UL> </BODY></HTML>
One of the problems with Web-based applications is maintaining application state. Each time a CGI program is run, it has no memory of what went before, or which client is using it. There are two mechanisms you can use to maintain the state of an application for each client using it:
State information is kept on the form sent to the end-user's Web browser, in fields which are not displayed in the browser. Hidden fields work with any browser that supports forms. However, state cannot be maintained between sessions, because as soon as the form is lost from the end-user's machine, all the state information is lost with it.
State information is maintained even if the end-user sends their browser to other pages or sites before returning to your application. Cookies can maintain state between sessions. However, the end-user must be using a browser that supports cookies.
There are two drawbacks to using either of these mechanisms on their own:
NetExpress provides an extra mechanism which enables you to store all the state information on your server, where it can be accessed rapidly by server-side programs. A set of call-by-name routines in the run-time system enable a server-side program to request a unique client-id for each client which connects, and store state data for the client in an indexed file.
The diagram below shows a server-side program sending a form to a client browser, together with a client-id baked inside a cookie. The next time the client browser makes a request to the Web server, it passes back the cookie. The server-side program (which might be the same as the first one, or a different one) uses the client id to retrieve the record with the state data.
Figure 7-1: The server-side state file
Note: If you intend to store sensitive information in the state file (for example, credit card details), you should implement extra security measures such as encryption, or only providing access to the application over secure network links. A malicious user could potentially access the state records for someone else by forging a different client-id before resubmitting a form. Whether or not they see any of the actual state information depends on the design of your application, and what type of information it sends back to the Web browser.
The next three sections cover:
You can use hidden fields on HTML forms. On an HTML form, add one or more hidden fields to your form. These work like HTML Inputs (entry fields), except that they can't be seen by the end-user who loads your form.
When you generate a server-side program which uses the form as an input or output form, it generates COBOL data items corresponding to the hidden fields as it does for any entry field. You can store state information for your application in the hidden field when the form is output, and read it back in when the form is submitted to another server-side program.
A cookie is a name/value pair sent to a Web browser along with an HTML page. You can record application state information in the cookie, and retrieve it the next time that particular client accesses a server-side program. Netscape Navigator 2.0 or later and Microsoft Internet Explorer 3.0 or later both support cookies; not all other browsers do.
The full cookies specification can be found on the Netscape site - click here.
By default, cookies are retained by the Web browser until the end-user closes the browser down. You can set an expiry date, in which case the information is stored in a cache maintained by the browser until the expiry date.
When you are designing an HTML page with Form Designer, you can create and associate a cookie with the page using the Cookies option on the Page menu. You can set the cookie's name, value, COBOL picture, expiry date, path, domain and security. You can use the domain and path attributes to share cookies between pages. When you click OK on the Cookies dialog box, Form Designer creates nearly all the code you need to set and read cookies, both server-side and client-side:
For more information about the Form Designer Cookie Editor, click Help Topics on the Help menu, then enter Cookie on the Index tab and select To add a cookie from the Topics Found dialog box.
To put a new value in the cookie while the page is being displayed, you need to add JavaScript code to the page to assign the value to the cookie. For further information on JavaScript coding see the chapter Client-side Programming.
In this example you create an HTML page and associate a cookie with it. The cookie is used to keep a count of how many times the page has been accessed. The server-side program increments the counter. These instructions are not at the detailed level of "now click the OK button". They explain the overall process; detailed step-by-step instructions for the Internet Application Wizard and NetExpress are available in the on-line help. If you want to get a feel for the Internet Application Wizard and NetExpress before you start, run through the tutorials in Getting Started.
To create the example and set up the cookie follow these steps:
Figure 7-2: Cookie example
add 1 to cookiecounter
move cookiecounter to hitcounter
The server-side state mechanism enables you to store information about the application state using a record format you define. One of the consequences of using the mechanism is that you always need a server-side program to output the first form in an application, to assign a client id at the start of the session.
If you are using a cookie to store the client id, you can set its expiry information so that state information is stored longer than the user's current session. This gives you the option of making an application persistent; the user can restart an application the following day, and be returned to where they were when last working on it. A program written for a persistent application needs to test the cookie with the client id to determine whether this is the first time it has been run or not; if the client id value is spaces, you need to allocate an id and start a new session, otherwise you need to restore the state information and display an appropriate form.
Note: The server-side state mechanism is implemented by a module called sstate. The source code is provided for sstate so that you can modify the subroutines it provides if you want to add extra functionality - encryption for example.
The source code, sstate.cbl, is in the \netexpress\base\demo\sstate folder. If you want to make changes to it, create a new NetExpress project, and copy sstate.cbl to your new project. To get your applications to use your new sstate module, build sstate.obj and sstate.gnt versions. Copy sstate.obj to \netexpress\base\lib, and sstate.gnt to \netexpress\base\bin.
Applications generated with the Data Access Wizard all rely on sstate. You should not change the interface to sstate, or Data Access Wizard applications may fail unpredictably. The interfaces to all the sstate calls are documented in the online help. Click Help Topics on the NetExpress Help menu, then from the Contents tab, click Reference, Library Routines, Library Routines by Function, and click the link to State Maintenance.
The flowchart below shows the logic for using the server-side state mechanism. Click on the boxes to see more information about how you carry out each step:
Figure 7-3: Using the server-side state mechanism
This is a summary of the information provided by the flowchart above:
Call the "MF_CLIENT_STATE_FILE" routine.
If this is not the first time in, read the client id from the cookie or hidden control returned by the browser and continue at step 3.
Call the "MF_CLIENT_STATE_RESTORE" routine.
Call the "MF_CLIENT_STATE_SAVE" routine.
All these routines are documented in full in the on-line reference. Click Help Topics on the NetExpress Help menu, then on the Contents tab click Reference, Library Routines, Library Routines by Function, and click the link to State Maintenance. The following example is the skeleton of a program showing the use of the state maintenance routines.
working-storage section. ... 01 state-filename pic x(255) value "MF-STATE-SAVE.DAT". 01 client-length pic xxxx comp-x. 01 client-state. 03 accessCount pic 9(9). 01 state-status pic x comp-x. 01 browserInput is external-form. 03 client-id pic x(30) identified by "clientid". *> cookie ... procedure division. *> Open the client-state file. call "MF_CLIENT_STATE_FILE" using state-filename ... accept browserInput *> read in the cookie with *> client id move length of client-state to client-length if client-id = spaces *> cookie is empty - first time in call "MF_CLIENT_STATE_ALLOCATE" using client-id client-length state-status ... *> any other first time in processing else *> use client id to restore state call "MF_CLIENT_STATE_RESTORE" using client-id client-state client-length state-status end-if ... *> Process data, and return form to client, including cookie ... *> Save client status call "MF_CLIENT_STATE_SAVE" using client-id client-state client-length state-status
Two routines are included for removing client state records when they are no longer required. One deletes a single record, and the other purges all records more than a certain number of days old.
To delete a single record, call "MF_CLIENT_STATE_DELETE". For example:
working-storage section. ... 01 state-status pic x comp-x. 01 client-id pic x(30). 01 state-filename pic x(255) value "MF-STATE-SAVE.DAT". procedure division. ... *> Open the client-state file. call "MF_CLIENT_STATE_FILE" using state-filename ... call "MF_CLIENT_STATE_DELETE " using client-id server-status
To purge all records over a certain age, call "MF_CLIENT_STATE_PURGE". For example:
working-storage section. ... 01 state-status pic x comp-x. 01 age-in-days pic x(4) comp-x. 01 state-filename pic x(255) value "MF-STATE-SAVE.DAT". procedure division. ... *> Open the client-state file. call "MF_CLIENT_STATE_FILE" using state-filename ... move 5 to age-in-days *> remove all records more than 5 days old call "MF_CLIENT_STATE_PURGE " using age-in-days server-status
These routines are documented in full in the on-line reference. Click Help Topics on the NetExpress Help menu, then on the Contents tab click Reference, Library Routines, Library Routines by Function, and click the link to State Maintenance.
Copyright © 1998 Micro Focus Limited. All rights reserved.
This document and the proprietary marks and names
used herein are protected by international law.
Reusing Legacy Code | CGI, ISAPI and NSAPI Programs |