• Skip Headers

    Oracle® Database Application Developer's Guide - Fundamentals
    10g Release 1 (10.1)

    Part Number B10795-01
    Go to Documentation Home
    Home
    Go to Book List
    Book List
    Go to Table of Contents
    Contents
    Go to Index
    Index
    Go to Master Index
    Master Index
    Go to Feedback page
    Feedback

    Go to previous page
    Previous
    Go to next page
    Next
    View PDF

    13
    Developing Web Applications with PL/SQL

    If you think that only new langu ages such as Java and JavaScript can do network operations and produce dynamic Web content, think again. PL/SQL has a number of featu res that you can use to Web-enable your database and make your back-office data interactive and accessible to intranet users or your customers.

    This chapter discusses the following topics:

    PL/SQL Web Applications

    Web applications written in PL/SQL are typically sets of stored procedures that interact with Web browsers thr ough the HTTP protocol. A set of interlinked dynamic HTML pages forms the user interface of a web application.

    When a Web browser user visits a Web page, follows a hypertext link, or presses a Submit button on a n HTML form, a URL is sent to the Web (HTTP) server, which causes the database server to run a stored procedure. Information the user provides in an HTML form is encoded in the URL. The URL also encodes information to identify which procedure, in which database, to call. The Web server passes this information along to the database as a set of parameters for the stored procedure.

    The stored procedure that is invoked from a URL calls subprograms from the PL/SQL Web Toolkit. Typically, so me of these subprograms, such as Htp.Print, prepare an HTML page that is displayed in the Web browser as a response to t he user.

    This process dynamically generates Web pages. Code running inside the database server produces HTML on the fly, so the generated Web page can vary depending on the database contents and the i nput parameters.

    Dynamic generation of HTML is to be distinguished from dynamic HTML (DHTML). With DHTML, code in JavaScript or some other scripting language is downloaded to the browser along with HT ML code, and this script code is processed by the browser.

    DHTML ca n be coded by hand, so that it is static code, or it can itself be generated by program. That is, the generation of Web pages using a database can be combined with downloading JavaScript or other DHTML script code. A PL/SQL Web application can dynamically create com plex DHTML that would be tedious to produce manually. A typical stored procedure might print some header information, issue a databas e query, and loop through the result set, formatting the data in an HTML list or table.

    Thi s program flow is very similar to a Perl script that operates on a text file. CGI scripts and programs running on a Web server are of ten used to dynamically produce Web pages, but they are usually not the best choice for interacting with a database. Using PL/SQL sto red procedures has the advantage of providing all the power and flexibility of database processing: it supports DML statements, dynam ic SQL, and cursors. It also eliminates the memory overhead of forking a new CGI process to treat each URL.

    A Web browser-based application can be implemented entirely in PL/SQL using the following Oracle Database components :

    PL/SQL Gateway

    PL/SQL Gateway provides support for deploying PL/SQL- based database applications on the World-Wide Web. It is part of Oracle HTTP Server (OHS), which ships with Oracle Application Server and Oracle Database.

    As part of the Oracle HTTP Server, it is the job of PL/SQL Gateway to interpret a URL sent by a Web browser to a Web server, call the appropriate PL/SQL s ubprograms to treat the browser request, then return the generated response to the browser. Typically, PL/SQL Gateway responds to a Web-browser HTTP request by constructing an HTML page to display. There are additional uses for the gateway, h owever. Here are two:

    < /a>

    Configuring mod_plsql

    As a plug-in to Oracle HTTP Server, mod_plsql causes stored procedures to be e xecuted in response to HTTP requests.

    For each URL that is processed, mod_plsql either uses a database session from its connection pool, or creates a new session on the fly and pools it. In order that mod_plsql can invoke th e appropriate database PL/SQL procedure in a URL-processing session, you must first configure a virtual path and associate that path with a Database Access Descriptor (DAD).

    A DAD is a n amed set of configuration values that specify the information necessary to create a session for a specific database and a specific da tabase user/password. This includes the database service name and the Globalization Support setting (language, currency symbol, and s o on) for the session.

    See Also:
    • mod_plsql Us er's Guide
    • Oracle HTTP Server Administrator's Guide for information on mod_plsql configuration parameters
    • "Using Caching with PL/ SQL Web Applications" in Oracle Application Server 10g Performance Guide for information on caching dy namically generated HTML pages to improve performance

    You can use PL/SQL Gateway to transfer files from a client machine to or f rom Oracle Database. You can upload and download text files or binary files.

    Uploading Files to the Database

    To upload files, you must first define a document repository using the DAD configuration, and specify how to upload the content: as a BLOB or LONG RAW value. To initiate uploading, you define and submit a multipart/form-data form, following the RFC 1867 specification.

    After you success fully upload a file, the procedure specified in the ACTION attribute of the multipart/form-data form is inv oked. This invocation is similar to that of any regular PL/SQL Gateway procedure. Subsequently, you can download files that you have uploaded, delete uploaded files from the database, and read or write their attributes.

    Downloading Files From the Database

    You can download a file from the database in several alternative ways:

    Custom Authentication With PL/SQL Gateway

    To authenticate individual Web-browser users for database purposes, it would be cumbersome to p rovide a different Database Access Descriptor (DAD) and URL for each user. Typically, a single URL, corresponding to a single database user (schema), is used for all browser users.

    This means that authentication of browser users is typically not performed by the database, but by the Web ap plication. It is the application that determines if a given Web-browser user should have access to the database. It can also det ermine which database schema (hence which URL) to use for a given user. You use the OWA_CUSTOM package of the PL/SQL Web Toolkit to perform such custom user authentication in Web applications.

    For example, suppo se you have a purchasing application that is accessed by multiple third-party vendors. Instead of creating a separate schema for each vendor, load the application into a common schema where all users log in. Using OWA_CUSTOM, your application can authen ticate each user, in any way it needs to. With OWA_CUSTOM, authentication is done only by the ap plication, not by the database.

    Custom authentication cannot be combined with dynamic usern ame/password authentication; it needs to have a static username/password stored in the DAD configuration file. PL/SQL Gateway uses th is DAD username/password to log in to the database.

    Once mod_plsql is logged in, authentica tion control is passed back to the application, by calling an application-level PL/SQL hook. This callback function is implemented by the application developers. The value returned by the callback function determines whether authentication succeeds or fails: T RUE means success; FALSE means failure.

    Depending on what kind of custo m authentication is desired, you can place the authentication function in different locations:

    PL/SQL Web Toolkit

    To develop the stored proced ures that are executed by PL/SQL Gateway at runtime, you use PL/SQL Web Toolkit: a set of PL/SQL packages that can be used to obtain information about an HTTP request; specify HTTP response headers, such as cookies, content-type, and mime-type, for HTTP headers; set cookies; and generate standard HTML tags for creating HTML pages.

    Commonly used PL/SQL Toolkit packages are listed in Table 13-1.

    Table 13-1   Some Packag es in PL/SQL Toolkit
    Package Description

    htp

    htf

    htp (Hypertext Procedures) package - Procedures that generate HTML tags. For instance, the procedure < code>htp.anchor generates the HTML anchor tag, <A>.

    htf (Hypertext Functions) package- Function versions of the procedures in the htp package. The function versions do not d irectly generate output in a Web page. Instead, they pass their output as return values to the statements that invoke them. Use these functions when you need to nest function calls.

    owa_cache

    Functions a nd procedures that enable the PL/SQL Gateway cache feature, to improve the performance of your PL/SQL Web app lication.

    You can use this package to enable expires-based and validation-based caching usin g the PL/SQL Gateway file system.

    owa_cookie

    Subprograms that send and retrieve HTTP cookies to and from a client Web browser. Cookies are strings a browser uses to maintain state between HTTP calls. Sta te can be maintained throughout a client session or longer if a cookie expiration date is included.

    owa_custom

    The authorize function used by cookies. See "Uploading and Do wnloading Files With PL/SQL Gateway".

    owa_image

    Subprograms that o btain the coordinates where a user clicked an image. Use this package when you have an image map whose destination links invoke a PL/ SQL Gateway.

    owa_opt_lock

    Subprograms that impose database optimistic locking strategies, to prevent lost updates.

    Lost updates can otherwise occur if a user sele cts, and then attempts to update, a row whose values have been changed in the meantime by another user.

    owa_pattern

    Subprograms that perform string matching and string manipulation with regular expressio n functionality.

    owa_sec

    Subprograms used by the PL/SQL Gateway for au thenticating requests.

    owa_text

    Subprograms used by package owa_ pattern for manipulating strings. You can also use them directly.

    owa_util

    Utility subprograms:

    • Dynamic SQL utilities to prod uce pages with dynamically generated SQL code.
    • HTML utilities to retrieve the values of CGI environment variables and perform URL redirects.
    • Date utilities for correct date-handling. Date values are simple strings in HTML, but must be properly treated as an Oracle Database datatype.
    • < /ul>

    wpg_docload

    Subprograms that download documents from a document repository that yo u define using the DAD configuration. See "Uploading and Downloading Files With PL/SQL Gateway".

    See Also:

    PL/SQL Packages and Types Reference

    Generating HTML Output from PL/SQL

    Traditionally, PL/SQL Web applications have used function calls to generate each HTML tag for output, using the PL/SQL Web toolkit packages that come with Oracle Database:

    owa_util.mime_header(
    'text/html');
    
    htp.htmlOpen;
    htp.headOpen;
    ht
    p.title('Title of the HTML File');
    htp.headClose;
    
    htp.bodyOpen( ca
    ttributes => 'TEXT="#000000" BGCOLOR="#FFFFFF"');
    htp.header(1, 'Heading in the HTML File');
    htp.para;
    htp.print('Some text in the HTML file.');
    htp.bodyClose;
    
    htp.htmlClose;
    
    

    You can learn the AP I calls corresponding to each tag, or just use some of the basic ones like HTP.PRINT to print the text and tags together :

    htp.print('<html>');
    htp.print('<head>');
    htp.print('<meta http-equiv="Content-Type" content="text/html">');
    htp.print('<title>Title
     of the HTML File</title>');
    htp.print('</head>');
    
    htp
    .print('<body TEXT="#000000" BGCOLOR="#FFFFFF">');
    htp.print('<h1>Heading in the HTML File</h1&g
    t;');
    htp.print('<p>Some text in the HTML file.');
    htp.print('</body>');
    
    htp.print('</html>');
    
    

    This chapter introduces an additional method, PL/SQL server pages, that lets you build on your knowledge of HTML tags, rather tha n learning a new set of function calls.

    In an application written as a set of PL/SQL server pages, you can still use functions from the PL/SQL Web toolkit to simplify the processing involved in displaying tables, storing per sistent data (cookies), and working with CGI protocol internals.

    Passing Parameters to a PL/SQL Web Application

    To be useful in a wide variety of situations, a Web application must be in teractive enough to allow user choices. To keep the attention of impatient Web surfers, you should streamline the interaction so that users can specify these choices very simply, without a lot of decision-making or data entry.

    The main methods of passing parameters to PL/SQL Web applications are:

    Passing List and Dropdown List Parameters from an HTML Form

    List boxes and dropdown lists are implemented usi ng the same HTML tag (<SELECT>).

    Use a list box for a large number of ch oices, where the user might have to scroll to see them all, or to allow multiple selections. List boxes are good for showing items in alphabetical order, so that users can find an item quickly without reading all the choices.

    Use a dropdown list for a small number of choices, or where screen space is limited, or for choices in an unusual order. The dropdo wn captures the first-time user's attention and makes them read the items. If you keep the choices and order consistent, users can me morize the motion of selecting an item from the dropdown list, allowing them to make selections quickly as they gain experience.

    Passing Radio Button and Checkbox Parameters from an HTML Form

    Radio buttons pass either a null value (if none of the radio buttons in a group is checked), or the value specified on the radio button that is checked .

    To specify a default value for a set of radio buttons, you can include the CHECKED< /code> attribute in one of the INPUT tags, or include a DEFAULT clause on the parameter within the stored p rocedure. When setting up a group of radio buttons, be sure to include a choice that indicates "no preference", because once the user selects a radio button, they can still select a different one, but they cannot clear the selection completely. For example, include a "Don't Care" or "Don't Know" selection along with "Yes" and "No" choices, in case someone makes a selection and then realizes it wa s wrong.

    Checkboxes need special handling, because your stored procedure might receive a nu ll value, a single value, or multiple values:

    All the checkboxes with the same NAME attribute make up a checkbox group. If none of the checkboxes in a group is checked, the stored procedure receives a null value for the corresponding parameter.

    If one checkbox in a group is checked, the stored procedu re receives a single VARCHAR2 parameter.

    If more than one checkbox in a group is checked, the stored procedure receives a parameter with the PL/SQL type TABLE OF VARCHAR2. You must declare a type li ke this, or use a predefined one like OWA_UTIL.IDENT_ARR. To retrieve the values, use a loop:

    CREATE OR REPLACE PROCEDURE handle_checkboxes ( checkboxes owa_util.ident_arr )
    AS
    BEGIN
      ...
      FOR i IN 1..checkboxes.count
      LOOP
        htp.print('<p>Checkbox value: ' || checkboxes(i));
      END LOOP;
    
     ...
    END;
    /
    show errors;
    

    Passing Entry Field Parameters from an HTML Form

    Entry fields require the most validation, because a user might ente r data in the wrong format, out of range, and so on. If possible, validate the data on the client side using dynamic HTML or Java, an d format it correctly for the user or prompt them to enter it again.

    For example:

    • You might prevent the user from entering alphabetic characters in a num eric entry field, or from entering characters once a length limit is reached.
    • You might silently remove spaces and dashes from a credit card number if the stored procedure expects the value in that format.
    • You might inform the user immediately when they type a number that is too large, so that they can retype it.

    Because you cannot always rely on such validation to succee d, code the stored procedures to deal with these cases anyway. Rather than forcing the user to use the Back button when they enter wrong data, display a single page with an error message and the original form with all the other values filled in.

    For sensitive information such as passwords, a special form of the entry field, <INPUT TY PE=PASSWORD>, hides the text as it is typed in.

    For example, the following proced ure accepts two strings as input. The first time it is called, the user sees a simple form prompting for the input values. When the u ser submits the information, the same procedure is called again to check if the input is correct. If the input is OK, the procedure p rocesses it. If not, the procedure prompts for new input, filling in the original values for the user.

    -- Store a name and associated zip code in the database.
    CREATE OR REPLACE PROCEDURE associate_name
    _with_zipcode
    (
      name VARCHAR2 DEFAULT NULL,
      zip VARCHAR2 DEFAUL
    T NULL
    )
    AS
      booktitle VARCHAR2(256);
    BEGIN
    -- Both entry fields must contain a value. The zip code must be 6 characters.
    -- (In a re
    al program you would perform more extensive checking.)
      IF name IS NOT NULL AND zip IS NOT NULL AND length(zip
    ) = 6 THEN
        store_name_and_zipcode(name, zip);
        htp.print('<p>The person ' ||
     name || ' has the zip code ' || zip || '.');
    -- If the input was OK, we stop here and the user does not see th
    e form again.
        RETURN;
      END IF;
    
    -- If so
    me data was entered, but it is not correct, show the error message.
      IF (name IS NULL AND zip IS NOT NULL)
        OR (name IS NOT NULL AND zip IS NULL)
        OR (zip IS NOT NULL AND length(zip) != 6)
      THEN
        htp.print('<p><b>Please re-enter the data. Fill in all fields, and use
     a 
    6-digit zip code.</b>');
      END IF;
    
    -- If the user has no
    t entered any data, or entered bad data, prompt for
    -- input values.
    
    <
    /a>-- Make the form call the same procedure to check the input values.
      htp.formOpen( 'scott.associate_name_wi
    th_zipcode', 'GET');
      htp.print('<p>Enter your name:</td>');
      htp.print('&l
    t;td valign=center><input type=text name=name value="' || name || 
    '">');
      htp.print('<p>Enter
    your zip code:</td>');
      htp.print('<td valign=center><input type=text name=zip value="' || zip
    || 
    '">');
      htp.formSubmit(NULL, 'Submit');
      htp.formClose;
    EN
    D;
    /
    show errors;
    
    
    

    Passing Hidden Parameters from an HTML Form

    One technique for passing information through a sequenc e of stored procedures, without requiring the user to specify the same choices each time, is to include hidden parameters in the form that calls a stored procedure. The first stored procedure places information, such as a user name, into the HTML form that it genera tes. The value of the hidden parameter is passed to the next stored procedure, as if the user had entered it through a radio button o r entry field.

    Other techniques for passing information from one stored procedure to anothe r include:

    • Sending a "cookie" containing the persistent inform ation to the browser. The browser then sends this same information back to the server when accessing other Web pages from the same si te. Cookies are set and retrieved through the HTTP headers that are transferred between the browser and the Web server before the HTM L text of each Web page.
    • Storing the information in the database itself, where later stored procedures can retrieve it. This technique involves some extra overhead on the database server, and you must still find a way to keep track of each user as multiple users access the server at the same time.

    Uploading a File from an HTML Form

    < !--/TOC=h2-->

    You can use an HTML form to choose a file on a client system, and transfer it to the server. A stored procedure can insert the file into the database as a CLOB, BLOB, or other type that ca n hold large amounts of data.

    The PL/SQL Web toolkit and the PL/SQL Gateways like mod_plsql have the notion of a "document table" that holds uploaded files.

    See Also:

    mod_plsql User's Gui de

    Submitting a Completed HTML Form

    By default, an HTML form must have a Submit button, which transmits the data from the form to a stored procedure or CGI program. You can label this button with text of your choice, such as "Search", "Register", and so on.

    Y ou can have multiple forms on the same page, each with its own form elements and Submit button. You can even have forms consisting entirely of hidden parameters, where the user makes no choice other than clicking the button.

    Using JavaScript or other scripting languages, you can do away with the Submit button and have the form submitted in re sponse to some other action, such as selecting from a dropdown list. This technique is best when the user only makes a single selecti on, and the confirmation step of the Submit button is not essential.

    Handling Missing Input from an HTML Form

    When an HTML form is submitted, your stored procedure receives null parameters for any form elements that are not filled in. For example, null parameters can result from an empty entry field, a set of checkboxes, radio b uttons, or list items with none checked, or a VALUE parameter of "" (empty quotation marks).

    Regardless of any validation you do on the client side, always code stored procedures to handle the possibility that s ome parameters are null:

    • Use a DEFAULT clause in all parameter declarations, to prevent an exception when the stored procedure is called with a missing form parameter. You can set th e default to zero for numeric values (when that makes sense), and use DEFAULT NULL when you want to check whether or not the user actually specifies a value.
    • Before using an input parameter value th at has a DEFAULT NULL declaration, check if it is null.
    • Make the procedure generate sensible results even when not all input parameters are specified. You might leave some sections out of a report, or display a text string or image in a report to indicate where parameters were not specified.
    • Provide a way to fill in the missing values and run the stored procedure again, directly from the results page. For example, you could include a link that calls the same stored procedure with an additional parameter, or display the original form wit h its values filled in as part of the output.

    Maintaining State Information Between Web Pages

    Web applications are particularly concerned with the idea of state, the set of data that is current at a particular moment in time. It is easy to lose state information when switching from one Web page to another, which might result in asking the user to make the same choices over and over.

    You can pass state information between dynamic Web pages using HTML forms. The information is passed as a set of name-value pairs, which are turned into stored procedure parameters for you.

    If the user has to make multiple selec tions, or one selection from many choices, or it is important to avoid an accidental selection, use an HTML form. After the user make s and reviews all the choices, they confirm the choices with the Submit button. Subsequent pages can use forms with hidd en parameters (<INPUT TYPE=HIDDEN> tags) to pass these choices from one page to the next.

    If the user is only considering one or two choices, or the decision points are scattered throughout the Web pa ge, you can save the user from hunting around for the Submit button by representing actions as hyperlinks and including any necessary name-value pairs in the query string (the part following the ? within a URL).

    An alternative way to main state information is to use Oracle Application Server and its mod_ose module. T his approach lets you store state information in package variables that remain available as a user moves around a Web site.

    See Also: < /a>

    the Oracle Application Server documentation set at http://otn .oracle.com/documentation/

    Performing Network Operations within PL/SQL Stored Procedures

    While built-in PL/SQL features are focused on traditional database operations and prog ramming logic, Oracle Database provides packages that open up Internet computing to PL/SQL programmers.

    Sending E-Mail fro m PL/SQL

    You can send e-mail from a PL/SQL program or stored pro cedure using the UTL_SMTP package. You can find details about this package in the PL/SQL Packages and Types Reference.

    The following code example illustrates how the SMTP package might be used by an application to send email. The applic ation connects to an SMTP server at port 25 and sends a simple text message.

    PROCEDURE send
    _test_message
    IS
        mailhost    VARCHAR2(64) := 'mailhost.fictional-domain.com';
        sender      VARCHAR2(64) := 'me@fictional-domain.com';
        recipient   VARCHAR2(64) := 'you@
    fictional-domain.com';
        mail_conn  utl_smtp.connection;
    BEGIN
    
      mail_conn := utl_smtp.open_connection(mailhost, 25);
        utl_smtp.helo(mail_conn, mailhost);
        utl_smtp.mail(mail_conn, sender);
        utl_smtp.rcpt(mail_conn, recipient);
    -- I
    f we had the message in a single string, we could collapse
    -- open_data(), write_data(), and close_data() into
    a single call to data().
        utl_smtp.open_data(mail_conn);
        utl_smtp.write_data(mail_
    conn, 'This is a test message.' || chr(13));
        utl_smtp.write_data(mail_conn, 'This is line 2.' || chr(13));
        utl_smtp.close_data(mail_conn);
        utl_smtp.quit(mail_conn);
    
       EXCEPTION
            WHEN OTHERS THEN
               -- Insert error-handling code here
               NULL;
    END;
    
    

    Getting a Host Name or Address from PL/SQL

    You can determine the hostname of the local machine, or the IP address of a give n hostname from a PL/SQL program or stored procedure using the UTL_INADDR package. You can find details about this packa ge in the PL/SQL Packages and Ty pes Reference. You use the results in calls to the UTL_TCP package.

    Working with TCP/IP Connections from PL/SQL

    You can open TCP/IP connections to machines on the network, and read or writ e to the corresponding sockets, using the UTL_TCP package. You can find details about this package in the PL/SQL Packages and Types Reference.

    Retrieving t he Contents of an HTTP URL from PL/SQL

    You can retrieve the contents of an HTTP URL using the UTL_HTTP package. The contents are typically in the form of HTML-tagged text, but may be plain text, a JPEG image, or any sort of file that is downloadable from a Web server. You can find details about this package in the PL/SQL Packages and Types Reference .

    The UTL_HTTP package lets you:

    • Control the details of the HTTP session, including header lines, cookies, redirects, proxy servers, ID s and passwords for protected sites, and CGI parameters through the GET or POST methods.
    • Speed up multiple accesses to the same Web site using HTTP 1.1 persistent connections.
    • Construct and interpret URLs for use with UTL_HTTP through the ESCAP E and UNESCAPE functions in the UTL_URL package.

    Typ ically, developers have used Java or Perl to perform these operations; this package lets you do them with PL/SQL.

    CREATE OR REPLACE PROCEDURE show_url
    (
        url      IN VARCHAR2,
    <
    a name="1006423">    username IN VARCHAR2 DEFAULT NULL,
        password IN VARCHAR2 DEFAULT NULL
    ) AS
        req       utl_http.req;
        resp      utl_http.resp;
    
        name      VARCHAR2(256);
        value     VARCHAR2(1024);
        data      VARCHAR2(255
    );
        my_scheme VARCHAR2(256);
        my_realm  VARCHAR2(256);
        my
    _proxy  BOOLEAN;
    BEGIN
    -- When going through a firewall, pass requests through this host.
    
    -- Specify sites inside the firewall that don't need the proxy host.
      utl_http.set_prox
    y('proxy.my-company.com', 'corp.my-company.com');
    
    -- Ask UTL_HTTP not to raise an except
    ion for 4xx and 5xx status codes,
    -- rather than just returning the text of the error page.
    <
    /a>  utl_http.set_response_error_check(FALSE);
    
    -- Begin retrieving this Web page.
      req := utl_http.begin_request(url);
    
    -- Identify ourselves. Some sites
    serve special pages for particular browsers.
      utl_http.set_header(req, 'User-Agent', 'Mozilla/4.0');
    
    -- Specify a user ID and password for pages that require them.
      IF (userna
    me IS NOT NULL) THEN
        utl_http.set_authentication(req, username, password);
      END IF;
    
      BEGIN
    -- Start receiving the HTML text.
    
      resp := utl_http.get_response(req);
    
    -- Show the status codes and reason phrase of the
    response.
        dbms_output.put_line('HTTP response status code: ' || resp.status_code);
    
      dbms_output.put_line('HTTP response reason phrase: ' || resp.reason_phrase);
    
    -- Look f
    or client-side error and report it.
        IF (resp.status_code >= 400) AND (resp.status_code <= 499) THEN
    <
    a name="1006464">
    -- Detect whether the page is password protected, and we didn't supply
    
    -- the right authorization.
          IF (resp.status_code = utl_http.HTTP_UNAUTHORIZED) THEN
            utl_http.get_authentication(resp, my_scheme, my_realm, my_proxy);
            IF (my_proxy) THEN
              dbms_output.put_line('Web proxy server is protected.');
              dbms_output.put('P
    lease supply the required ' || my_scheme ||
                ' authentication username/password for realm ' || my_re
    alm ||
                ' for the proxy server.');
            ELSE
    
      dbms_output.put_line('Web page ' || url || ' is protected.');
              dbms_output.put('Please supplied the
    required ' || my_scheme ||
                ' authentication username/password for realm ' || my_realm ||
                ' for the Web page.');
            END IF;
          ELSE
            dbms_output.put_line('Check the URL.');
          END IF;
    
    <
    /a>      utl_http.end_response(resp);
          RETURN;
    
    -- Look for se
    rver-side error and report it.
        ELSIF (resp.status_code >= 500) AND (resp.status_code <= 599) THEN
    
          dbms_output.put_line('Check if the Web site is up.');
          utl
    _http.end_response(resp);
          RETURN;
    
        END IF;
        
    -- The HTTP header lines contain information about cookies, character sets,
    
    -- and other data that client and server can use to customize each session.
        FOR i IN 1..utl_http.get_header
    _count(resp) LOOP
          utl_http.get_header(resp, i, name, value);
          dbms_output.put_
    line(name || ': ' || value);
        END LOOP;
    
    -- Keep reading lines u
    ntil no more are left and an exception is raised.
        LOOP
          utl_http.read_line(resp,
     value);
          dbms_output.put_line(value);
        END LOOP;
      EXCEPTI
    ON
        WHEN utl_http.end_of_body THEN
        utl_http.end_response(resp);
    
      END;
    
    END;
    /
    SET serveroutput ON 
    -- The following URLs illustrate the use of this procedure,
    -- but these pages do not actually
     exist. To test, substitute
    -- URLs from your own Web server.
    exec show_url('http://www.o
    racle.com/no-such-page.html')
    exec show_url('http://www.oracle.com/protected-page.html')
    
    exec show_url('http://www.oracle.com/protected-page.html', 'scott', 'tiger')
    
    

    Working with Tables , Image Maps, Cookies, and CGI Variables from PL/SQL

    Packages fo r all of these functions are supplied with Oracle8i and higher. You use these packages in combination with th e mod_plsql plug-in of Oracle HTTP Server (OHS). You can format the results of a query in an HTML table, produce an image map, set an d get HTTP cookies, check the values of CGI variables, and combine other typical Web operations with a PL/SQL program.

    Documentation for these packages is not part of the database documentation library. The location of the d ocumentation depends on the particular application server you are running. To get started with these packages, look at their procedur e names and parameters using the SQL*Plus DESCRIBE command:

    DESCRIBE HTP;
    DESCRIBE HTF;
    DESCRIBE OWA_UTIL;
    
    

    Embedding PL/SQL Code in Web Pages (PL/SQL Server Pages)

    To include dynamic content, including the results of S QL queries, inside Web pages, you can use server-side scripting through PL/SQL Server Pages (PSP). You can au thor the Web pages in a script-friendly HTML authoring tool, and drop the pieces of PL/SQL code into place. For cutting-edge Web page s, you might find this technique more convenient than using the HTP and HTF packages to write out HTML content line by line.

    Because the processing is done on the server -- in this case, the database server rather than the W eb server -- the browser receives a plain HTML page with no special script tags, and you can support all browsers and browser levels equally. It also makes network traffic efficient by minimizing the number of server round-trips.

    Embedding the PL/SQL code in the HTML page that you create lets you write content quickly and follow a rapid, iterative develop ment process. You maintain central control of the software, with only a Web browser required on the client machine.

    The steps to implement a Web-based solution using PL/SQL server pages are:

    Choosing a Software Configuration

    To develop and deploy PL/SQL Server Pages, you need Oracle Database version 8.1.6 or later, together with the mod_plsql plug-in of Oracle HTTP Server (OHS) .

    Choosin g Between PSP and the PL/SQL Web Toolkit

    You can produce the same re sults in different ways:

    • By writing an HTML page with embedded PL/SQL code and compiling it as a PL/SQL server page. You may call procedures from the PL/SQL Web Toolkit, but not to generate every line of HTML output.
    • By writing a complete stored procedure that produces HTM L by calling the HTP and OWA_* packages in the PL/SQL Web Toolkit.

    The key factors in choosing between these techniques are:

    • What source are you using as a starting point?
      • If you have a large body of HTML, and want to include dynamic content or make it the front end of a database application, use PSP.
      • If you have a large body of PL/SQL code that produces formatted output, you may find it more c onvenient to produce HTML tags by changing your print statements to call the HTP package of the PL/SQL Web Toolkit.
    • What is the fastest and most convenient authoring environment for your group?
      • If most work is done using HTML authoring tools, use PSP.
      • If you use authoring tools that produce PL/SQL code, such as the page-building wizards in Or acle Application Server Portal, then it might be less convenient to use PSP.

    How PSP Relates to Other Scripting Solutions

    Because any kind of tags can be passed unchanged to the browser through a PL/SQL server page, you can include JavaScript or other client-side script code in a PL/SQL server page.

    You cannot mix PL/SQL server pages with other server-side script features, such as server-side includes. In many case s, you can get the same results by using the corresponding PSP features.

    PSP uses the same script tag syntax as Java Server Pages (JSP), to make it easy to switch back and forth.

    PSP uses syntax similar to that of Active Server Pages (ASP), although the syntax is not identical and you must typically translate from VBScript or JScript to PL/SQL. The best candidates for migration are pages that use the Active Data Object (ADO) interface to do dat abase operations.

    Writing the Code and Content for the PL/SQL Server Page

    You can start with an existing Web page, or with an existing stored procedure. Either way, with a few additions and changes you can crea te dynamic Web pages that perform database operations and display the results.

    The Format of the PSP File

    The file for a PL/SQL server page must have the extension .psp.

    It can contain whatever content you like, with text and tags interspersed with PSP directives, declarations, and scrip tlets:

    • In the simplest case, it is nothing more than an HTML f ile. Compiling it as a PL/SQL server page produces a stored procedure that outputs the exact same HTML file.
    • In the most complex case, it is a PL/SQL procedure that generates all the content of the Web page, inc luding the tags for title, body, and headings.
    • In the typical case, it is a mi x of HTML (providing the static parts of the page) and PL/SQL (filling in the dynamic content).

    The order and placement of the PSP directives and declarations is not significant in most cases -- only when another file is being included. For ease of maintenance, we recommend placing the directives and declarations together near the beginning of the f ile.

    The following sections discuss the way to produce various results using the PSP script ing elements. If you are familiar with dynamic HTML and want to start coding right away, you can jump forward to Syntax of PL/SQL Server Page Elements and "Examples of PL/SQL Server Pages".

    Specifying the Scripting Language

    To identify a file as a PL/SQL S erver Page, include a <%@ page language="PL/SQL" %> directive somewhere in the file. This directive is for compatibility with other scripting environments.

    Accepting User Input

    User input comes encoded in the URL that retrieves t he HTML page. You can generate the URL by hard-coding it in an HTML link, or by calling your page as the "action" of an HTML form. Yo ur page receives the input as parameters to a PL/SQL stored procedure.

    To set up parameter passing for a PL/SQL server page, include a <%@ plsql parameter="varname"  %> directive. By default, parameters are of type VARCHAR2. To use a different type, include a type="typename" attribute within the directive. To set a default value, so that the parameter becomes optional, include a default="expression" attribute in the directive. The values for this attribute are substituted directly into a PL/SQL statement, so any strings must be single-quoted, and you can use special values such as null.

    Displaying HTML

    The PL/SQL parts of the page are enclosed within special delimiters. All other content is passed along verbatim -- including any whitespace -- to the browser. To display text or HTML tags, write it as you would a typica l Web page. You do not need to call any output function.

    Sometimes you might want to displa y one line of output or another, or change the value of an attribute, based on some condition. You can include IF/THEN l ogic and variable substitution inside the PSP delimiters, as shown in subsequent sections.

    Returning XML, Text, or Other Document Types

    By default, the PL/SQL gateway transmits files as HTML documents, so that the browser formats them according to the HTML tags. If you want the browser to interpret the document as XML, plain text (with no formatting), or some other document type, include a <%@ page contentType="MIMEtype" %> directive. (The attribute name i s case-sensitive, so be sure to capitalize it as contentType.) Specify text/html, text/xml, text/plain, image/jpeg, or some other MIME type that the browser or other client program recognizes. Users ma y have to configure their browsers to recognize some MIME types.

    Typically, a PL/SQL server page is intended to be displayed in a Web browser. It could also be retrieved and interpreted by a program that can make HTTP reques ts, such as a Java or Perl application.

    Returni ng Pages Containing Different Character Sets

    By default, the PL/SQL gateway transmi ts files using the character set defined by the Web gateway. To convert the data to a different character set for displaying in a bro wser, include a <%@ page charset="encoding" %> directive. Specif y Shift_JIS, Big5, UTF-8, or another encoding that the browser or other client program recogni zes.

    You must also configure the character set setting in the database accessor descriptor (DAD) of the Web gateway. Users may have to select the same encoding in their browsers to see the data displayed properly.

    For example, a database in Japan might have a database character set that uses the EUC e ncoding, while the Web browsers are set up to display Shift_JIS encoding.

    Handling Script Errors

    Any errors in HTML t agging are handled by the browser. The PSP loading process does not check for them.

    If you make a syntax error in the PL/SQL code, the loader stops and you must fix the error before continuing. Note that any previous version of the stored procedure can be erased when you attempt to replace it and the script contains a syntax error. You might want to use o ne database for prototyping and debugging, then load the final stored procedure into a different database for production. You can swi tch databases using a command-line flag, without changing any source code.

    To handle databa se errors that occur when the script runs, you can include PL/SQL exception-handling code within a PSP file, and have any unhandled e xceptions bring up a special page. The page for unhandled exceptions is another PL/SQL server page with extension .psp. The error procedure does not receive any parameters, so to determine the cause of the error, it can call the SQLCODE and SQLERRM functions.

    You can also display a standard HTML page without any scri pting when an error occurs, but you must still give it the extension .psp and load it into the database as a stored proc edure.

    Naming the PL/SQL Stored Procedure in a PSP Script

    Each top-level PL/SQL server page corresponds to a stored procedure with in the server. By default, the procedure is given the same name as the original file, with the .psp extension removed. T o name the procedure something else, include a include a <%@ plsql procedure="procname" %> directive.

    Including the Contents of Other Files in a PSP Script

    You can set up an include mechanism to pull in the contents of other files, typically containing either static HTML content or more PL/SQL scripting code. Include a <%@ include file="filename" %> directive at the point where the other file's content should appear. Because the files are processed at the point where you load the stored procedure into the database, the substitution is done only once, not whenever the page is served.

    You can use any names and extensions for the included files. If the included files contain PL/SQL scripting code, they do not need their own set of directives to identify the procedure name, character set, and so on.

    When specifying the names of fil es to the PSP loader, you must include the names of all included files also. Specify the names of included files before the names of any .psp files.

    You can use this feature to pull in the same content, such as a navigation banner, into many different files. Or, you can use it as a macro capability to include the same section of script code i n more than one place in a page.

    Declaring Vari ables in a PSP Script

    If you need to use global variables within the script, you ca n include a declaration block inside the delimiters <%! %>. All the usual PL/SQL syntax is allowed within the block. The delimiters server as shorthand, letting you omit the DECLARE keyword. All the declarations are available to the code later on in the file.

    You can specify multiple declaration blocks; internally, the y are all merged into a single block when the PSP file is made into a stored procedure.

    You can also use explicit DECLARE blocks within the <% %> delimiters that are explained later. Thes e declarations are only visible to the following BEGIN/END block.

    Specifying Executable Statements in a PSP Script

    Yo u can include any PL/SQL statements within the delimiters <% %>. The statements can be complete, or clauses o f a compound statement, such as the IF part of an IF-THEN-ELSE statement. Any variables declared within DECLARE blocks are only visible to the following BEGIN/END block.

    Substituting an Expression Result in a PSP Script

    To include a value that depends upon the result of a PL/SQL expression, include the expression within the delimiters & lt;%= %>. Because the result is always substituted in the middle of text or tags, it must be a string value or be able to be cast to a string. For any types that cannot be implicitly cast, such as DATE, pass the value to the PL/SQL TO_CHAR function.

    The content between the <%= %> delimiters is processed by the HTP.PRN function, which trims any leading or trailing whitespace and requires that you quote any lit eral strings.

    Conventions for Quoting and Escap ing Strings in a PSP Script

    When values specified in PSP attributes are used for PL /SQL operations, they are passed exactly as you specify them in the PSP file. If PL/SQL requires a single-quoted string, you must spe cify the string with the single quotes around it -- and surround the whole thing with double quotes.

    You can also nest single-quoted strings inside single quotes. In this case, you must escape the nes ted single quotes by specifying the sequence \'.

    Most characters and character sequences can be included in a PSP file without being changed by the PSP loader. To include the sequence %>, specify the escape sequence %\>. To include the sequence <%, specify the escape sequence <\% .

    Including Comments in a PSP Script

    To put a comment in the HTML portion of a PL/SQL server page, for the benefit of people readi ng the PSP source code, use the syntax:

    <%-- Comment text --%>
    ;
    
    

    These comments do not appear in the HTML output from the PSP.

    To create a comment that is visible in the HTML output, place the comment in the HTML portio n and use the regular HTML comment syntax:

    <!-- Comment text -->
    
    

    To include a comment inside a PL/SQL block within a PSP, you can use the normal PL/SQ L comment syntax.

    For example, here is part of a PSP file showing several kinds of comments :

    <p>Today we introduce our new model XP-10.
    <%--
    This is the project with code name "Secret Project".
    People viewing the HTML page will not see this
     comment.
    --%>
    <!--
    Some pictures of the XP-10.
    People viewing the HTML page will see this comment.
    -->
    <%
    for image_file in (select pathname, width, height, description
      from image_library where model_num = 'X
    P-10')
    -- Comments interspersed with PL/SQL statements.
    -- People viewing the HTML page w
    ill not see this comment.
    loop
    %>
    <img src="<%= image_file
    .pathname %>" width=<% image_file.width %>
    height=<% image_file.height %> alt="<% image_file.
    description %>">
    <br>
    <%
    end loop;
    %>
    
    

    Retrieving a Result Set from a Query in a PSP Script

    If your background is in HTML design, he re are a few examples of retrieving data from the database and displaying it.

    To display th e results of a query that returns multiple rows, you can iterate through each row of the result set, printing the appropriate columns using HTML list or table tags:

    <% FOR item IN (SELECT * FROM some_table) LOOP %>
      <TR>
      <TD><%= item.col1 %></TD>
      <TD>
    ;<%= item.col2 %></TD>
      </TR>
    <% END LOOP; %>
    
    
    

    If you want to print out an entire database table in one operation, you can call th e OWA_UTIL.TABLEPRINT or OWA_UTIL.CELLSPRINT procedures from the PL/SQL Web toolkit:

    <% OWA_UTIL.TABLEPRINT(CTABLE => 'some_table', CATTRIBUTES => 'border=2', 
    CCOLUMNS => 'col1, col2',
    CCLAUSES => 'WHERE col1 > 5'); %>
    
    htp.tableOpen('border=2');
    
    owa_util.cellsprint( 'select col1, col2 from some_table where col1 > 5');
    htp.tableClose;
    
    

    Coding Tips f or PSP Scripts

    To share procedures, constants, and types across different PL/SQL se rver pages, compile them into a separate package in the database using a plain PL/SQL source file. Although you can reference package d procedures, constants, and types from PSP scripts, the PSP scripts can only produce standalone procedures, not packages.

    To make things easier to maintain, keep all your directives and declarations together near the beginn ing of a PL/SQL server page.

    Syntax of PL/SQL Server Page Elements

    You can fi nd examples of many of these elements in "Examples of PL/SQL Server Pages".

    Page Directive

    Specifies characteristics of the PL/SQL server pa ge:

    Note that the attribute names content Type and errorPage are case-sensitive.

    Syntax
    <%@ page [language="PL/SQL"] [contentType="content type string"] charset="encoding" [errorPage="file.psp"] %>
    

    Procedure Directive

    Specifies the name of the stored proced ure produced by the PSP file. By default, the name is the filename without the .psp extension.

    Syntax
    <%@ plsql pr
    ocedure="procedure name" %>
    
    
    < h4 class="H3">Parameter Directive

    Specifies the name, and optionally the type and default, for each parameter expected by the PSP stored proced ure. The parameters are passed using name-value pairs, typically from an HTML form. To specify a default value of a character type, u se single quotes around the value, inside the double quotes required by the directive. For example:

    <%@ parameter="username" type="varchar2" default="'nobody'" %>
    
    Syntax
    <%@ plsql parameter="parameter name" [type="PL/SQL type"] [default="value"] %>
    
    
    

    I nclude Directive

    Specifies the name of a file to be included at a sp ecific point in the PSP file. The file must have an extension other than .psp. It can contain HTML, PSP script elements, or a combination of both. The name resolution and file inclusion happens when the PSP file is loaded into the database as a stored p rocedure, so any changes to the file after that are not reflected when the stored procedure is run.

    You must specify exactly the same name in both the include directive and in the loadpsp command, including any relative path name such as ../include/.

    Syntax
    <%@ include file="path name" %>
    
    

    Declaration Block

    Declares a set of PL/SQL variables that a re visible throughout the page, not just within the next BEGIN/END block. This element typically spans multiple lines, w ith individual PL/SQL variable declarations ended by semicolons.

    Syntax
    <%! PL/SQL declaration;
        [ PL/SQL declaration; ] ... %>
    
    

    Code Block (Scriptlet)

    Executes a set of PL/SQL statements when the stored procedure is run. This element t ypically spans multiple lines, with individual PL/SQL statements ended by semicolons. The statements can include complete blocks, or can be the bracketing parts of IF/THEN/ELSE or BEGIN/END blocks. When a code block is split into multiple s criptlets, you can put HTML or other directives in the middle, and those pieces are conditionally executed when the stored procedure is run.

    Syntax
    <% PL/SQL statement;
       [ PL/SQL statement; ] ... %>
    
    

    Expression Block

    Specifies a single P L/SQL expression, such as a string, arithmetic expression, function call, or combination of those things. The result is substituted a s a string at that spot in the HTML page that is produced by the stored procedure. You do not need to end the PL/SQL expression with a semicolon.

    Syntax
    <%= PL/SQL expression %>
    
    

    Loading the PL/SQL Server Page into th e Database as a Stored Procedure

    You load one or more PSP files into the database as stored procedures. Each .psp file corresponds to one stored procedure. The pages are compiled and loade d in one step, to speed up the development cycle:

    loadpsp [ -replace ] -user username/password[@connect_string] 
        [ include_file_name ... ] [ error_file_name ] psp_file_name ...
    
    
    
    
    

    To do a "create and replace" on the stored procedures, include the -replace f lag.

    The loader logs on to the database using the specified user name, password, and connec t string. The stored procedures are created in the corresponding schema.

    Include the names of all the include files (whose names do not have the .psp extension) before the names of the PL/SQL server pages (whose names have the .psp extension). Also include the name of the file specified in the errorPage attribute of the page directive. These filenames on the loadpsp command line must match exactly the names specified within the PSP include and page directives, including any relative path name such as ../include/.

    For example:

    loadpsp -replace -user scott/tiger@orcl banner.in
    c error.psp display_order.psp
    
    

    In this example:

    • The stored procedure is created in the database orcl. The database is accessed as user scott with password tiger, both to create the stored procedure and when the stored procedure is executed.
    • banner.inc is a file containing boilerplate text and script code, that i s included by the .psp file. The inclusion happens when the PSP is loaded into the database, not when the stored procedu re is executed.
    • error.psp is a file containing code, text, or bot h that is processed when an unhandled exception occurs, to present a friendly page rather than an internal error message.
    • display_order.psp contains the main code and text for the Web page. By defau lt, the corresponding stored procedure is named DISPLAY_ORDER.

    Running a PL/SQL Server Page Through a URL

    Once the PL/SQL server page has been turned into a stored procedure, you can run it b y retrieving an HTTP URL through a Web browser or other Internet-aware client program. The virtual path in the URL depends on the way the Web gateway is configured.

    The parameters to the stored procedure are passed through e ither the POST method or the GET method of the HTTP protocol. With the POST method, the parameters are passed directly from an HTML f orm and are not visible in the URL. With the GET method, the parameters are passed as name-value pairs in the query string of the URL , separated by & characters, with most non-alphanumeric characters in encoded format (such as %20 for a space). You can use the GET method to call a PSP page from an HTML form, or you can use a hard-coded HTML link to call the stored pr ocedure with a given set of parameters.

    Sample PSP URLs

    Using METHOD=GE T, the URL might look something like this:

    http://sitename/<
    em class="Italic">schemaname/pspname?parmname1=value1&am
    p;parmname2=value2
    
    

    Using METHOD=POST, the URL does not show the parameters:

    http://sitename/schemaname/pspname
    
    

    The METHOD=GET format is more convenient for debugging and allows visitors to pass exactly the sa me parameters when they return to the page through a bookmark.

    The METHOD=POST format allows a larger volume of parameter data, and is suitable for passing sensitive information that should not be displayed in t he URL. (URLs linger on in the browser's history list and in the HTTP headers that are passed to the next-visited page.) It is not pr actical to bookmark pages that are called this way.

    Examples of PL/SQL Server Pages

    This section shows how you might start with a very simple PL/SQL server page, and produce progressively more complicated versi ons as you gain more confidence.

    As you go through each step, you can use the procedures in "Loading the PL/SQL Server Page into the Database as a Stored Procedure" and "Running a PL/SQL Server Page Through a URL" to compile the PSP files and try them in a browser.

    Sample Table

    In this example , we use a very small table representing a product catalog. It holds the name of an item, the price, and URLs for a description and p icture of the item.

    Name       Type
    ---------- -------------
    PRODUCT    VARCHAR2(100)
    PRICE      NUMBER(7,2)
    URL        VARCHAR2(200)
    PICTURE    VARCHAR2(200)              
    
    Guitar
    
    455.5
    http://auction.fictional_site.com/guitar.htm
    http://auction.fictional_site.com/guit
    ar.jpg
     
     
    Brown shoe
    79.95
    http://retail.fictional_site.com/loafers.htm
    http://retail.fictional_site.com/shoe.gif
    <
    /a> 
    Radio
    9.95
    http://promo.fictional_site.com/freegift.htm
    http://promo.fictional_site.com/alarmclock.jpg
    
    

    Dumping the Sample Table

    < a name="1006834">

    For your own debugging, you might want to display the complete contents of an SQL table. You can do this with a single call to OWA_UTIL.TABLEPRINT. In subsequent iterations, we use other techniques to get more contro l over the presentation.

    <%@ plsql procedure="show_catalog_simple" %>
    <HTML>
    <HEAD><TITLE>Show Contents of Catalog (Complete Dump)</TITLE></HEAD>
    ;
    <BODY>
    <% 
    declare
    dummy boolean;
    begin
    dummy := owa_util.tableprint('catalog','border');
    end;
    %>
    </BODY>
    </HTML>
    
    
    

    Printing the Sample Table using a Loop

    Next, we loop through the it ems in the table and explicitly print just the pieces we want.

    • We could adjust the SELECT statement to retrieve only a subset of the rows or columns.
    • We could change the HTML or the location of the expressions to change the appearance of each item, or the orde r in which the columns are shown.
    • At this early stage, we pick a very simple p resentation, a set of list items, to avoid any problems from mismatched or unclosed table tags.
      <%@ plsql procedure="show_catalog_raw" %>
      <HTML>
      <HEAD><TITLE>S
      how Contents of Catalog (Raw Form)</TITLE></HEAD>
      <BODY>
      <UL>
      <% for item in (select * from catalog order by price desc) loop %>
      <LI>
      Item = <%= item.product %><BR>
      price = <%= item.price %><BR>
      URL = <I><%= item.url %></I><BR>
      picture = <I><%= item.picture %>
      </I>
      <% end loop; %>
      </UL>
      </BODY>
      </HTML>
      
      

    Once the previous simple ex ample is working, we can display the contents in a more usable format.

    • We use some HTML tags around certain values for emphasis.
    • Instead of printing the URLs for the description and picture, we plug them into link and image tags so that the reader can see the picture and f ollow the link.
      <%@ plsql procedure="show_catalog_pretty" %>
      <H
      TML>
      <HEAD><TITLE>Show Contents of Catalog (Better Form)</TITLE></HEAD>
      <BODY>
      <UL>
      <% for item in (select * from catalog order by price
       desc) loop %>
      <LI>
      Item = <A HREF="<%= item.url %>"><%= item.pro
      duct %></A><BR>
      price = <BIG><%= item.price %></BIG><BR>
      <IMG SRC="<%= item.picture %>">
      <% end loop; %>
      </UL>
      </BODY>
      </HTML>
      
      
      
    < a name="1006899">

    Allowing a User Selection

    We have a dynamic page, but from a user point of view it may still be dull. The results are always the same unless you update the catalog table.

    • To liven up the page, we can make it accept a minimum price, and present only the items that are more expen sive. (Your customers' buying criteria may vary.)
    • When the page is displayed i n a browser, by default the minimum price is 100 units of the appropriate currency. Later, we will see how to allow the user to pick a minimum price.
      <%@ plsql procedure="show_catalog_partial" %>
      <
      ;%@ plsql parameter="minprice" default="100" %>
      
      <HTML>
      &l
      t;HEAD><TITLE>Show Items Greater Than Specified Price</TITLE></HEAD>
      <BODY>
      <P>This report shows the items whose price is greater than <%= minprice %>.
      <UL>
      ;
      <% for item in (select * from catalog where price > minprice order by price 
      desc) loop %>
      <LI>
      Item = <A HREF="<%= item.url %>"><%= item.product %></A><BR&
      gt;
      price = <BIG><%= item.price %></BIG><BR>
      <IMG SRC="<%=
      item.picture %>">
      <% end loop; %>
      </UL>
      </BODY
      >
      </HTML>
      
      

    This technique of filtering results is fine for some applications, such as search results, where users might worry about being overwhelmed by choic es. But in a retail situation, you might want to use an alternative technique so that customers can still choose to purchase other it ems.

    • Instead of filtering the results through a WHERE clause, we can retrieve the entire result set, then take different actions for different returned rows.
    • We can change the HTML to highlight the output that meets their criteria. In this case, we use the bac kground color for an HTML table row. We could also insert a special icon, increase the font size, or use some other technique to call attention to the most important rows.
    • At this point, where we want to present a specific user experience, it becomes worth the trouble to lay out the results in an HTML table.
      <%@ plsql procedure="show_catalog_highlighted" %>
      <%@ plsql parameter="minprice" default="100" %&
      gt;
      <%! color varchar2(7); %>
      
      <HTML>
      <HEAD><TITLE>Show Items Greater Than Specified Price</TITLE></HEAD>
      <BODY>
      <
      a name="1018220"><P>This report shows all items, highlighting those whose price is
       greater than <
      %= minprice %>.
      <TABLE BORDER>
      <TR>
      <TH>Produc
      t</TH>
      <TH>Price</TH>
      <TH>Picture</TH>
      </TR>
      <%
      for item in (select * from catalog order by price desc) loop
        if item.price > minprice then
          color := '#CCCCFF';
        else
          color := '#CCCCCC';
        end if;
      %>
      <TR BGCOLOR
      ="<%= color %>">
      <TD><A HREF="<%= item.url %>"><%= item.product %></A>&l
      t;/TD>
      <TD><BIG><%= item.price %></BIG></TD>
      <TD>&
      lt;IMG SRC="<%= item.picture %>"></TD>
      </TR>
      <% end loop; %>
      <
      a name="1018242"></TABLE>
      </BODY>
      </HTML>
      
      
      

    Sample HTML Form to Call a PL/SQL Server Page

    Here is a bare-bones HTML form that allows someone to enter a price, and then calls the SHOW_CATALOG_PARTIAL stored p rocedure passing the entered value as the MINPRICE parameter.

    To avoid coding the entire URL of the stored procedure in the ACTION= attribute of the form, we can make the form a PSP file so that it goes in the same directory as the PSP file it calls. Even though this HTML file has no PL/SQL code, we can give it a .psp extension and load it as a stored procedure into the database. When the stored procedure is run, it just displays the HTML exactly as it appears in the file.

    <html>
    <body>
    <form method="POST" action="show_catalog_partial">
    <p>Enter the minimum price you want to pay
    :
    <input type="text" name="minprice">
    <input type="submit" value="Submit">
    </form>
    </body>
    </html>
    
    
    

    Note:

    An HTML form is different from other forms you might produce wi th tools and programming languages. It is part of an HTML file, delimited by <FORM> and </FORM> tags, where someone can make choices and enter data, then transmit those choices to a server-side program using the CGI protocol.

    To produce a complete application using PSP, you might need to learn the syntax of <I NPUT>, <SELECT>, and other HTML tags related to forms.


    Including JavaScript in a PSP File

    To produce an elaborate HTML file, perhaps including dynamic content such as JavaScript, you can simplify the sourc e code by implementing it as a PSP. This technique avoids having to deal with nested quotation marks, escape characters, concatenated literals and variables, and indentation of the embedded content.

    For example, here is how an HTML file containing JavaScript might be generated using a PSP:

    <%@ page language="PL
    /SQL" %>
    <%@ plsql procedure="graph" %>
    <%! 
    -- Begin w
    ith a date that does not exist in the audit table
    last_timestamp date := sysdate + 1;
    %&g
    t;
    <html>
    <head>
    <title>Usage Statistics</titl
    e>
    <script language="JavaScript">
    <!--
    d=document
    
    // Draw a horizontal graph line using a graphic that is stretched
    // by
     a scaling factor.
    function graph(howmuch)
    {
      preamble = "<img
    src='/images/graph_line.gif' height='8' width='"
      climax = howmuch * 4;
      denouement = "
    '> (" + howmuch + ")\n"
      d.write( preamble + climax + denouement )
    }
    // -->
    </script>
    </head>
    <body text="#00000
    0" bgcolor="#FFFFFF">
    <h1>Usage Statistics</h1>
    
    <
    ;table border=1>
    
    <%
    -- For each day, count how many times ea
    ch procedure was called.
    for item in (select trunc(time_stamp) t, count(*) n, procname p
    
      from audit_table group by trunc(time_stamp), procname
      order by trunc(time_stamp) desc, procname)
    loop
    -- At the start of each day's data, print the date.
      if item.t != last_
    timestamp then
        htp.print('<tr><td colspan=2><font size="+2">');
    
     htp.print(htf.bold(item.t));
        htp.print('</font></td></tr>');
        l
    ast_timestamp := item.t;
    end if;
    %>
    
    <t
    r><td><%= item.p %></a>:
    <td>
    <!-- Render an image of varia
    ble width to represent the data value. -->
    <script language="JavaScript">
    <!-
    -
    graph(<%= item.n %>)
    // -->
    </script>
    </td>
    </tr>
    
    <% end loop; %>
    
    </table>
    
    </body>
    &l
    t;/html>
    
    

    Coding this procedure as a regular PL/SQL stored proc edure would result in convoluted lines with doubled apostrophes, such as this:

    htp.print('p
    reamble = 
              "<img src=''/images/graph_line.gif'' height=''8'' width=''"');
    

    Debugging PL/SQL Server Page P roblems

    As you begin experimenting with PSP, and as you adapt your f irst simple pages into more elaborate ones, keep these guidelines in mind when you encounter problems:

    • The first step is to get all the PL/SQL syntax and PSP directive syntax right. If you make a mistake here, the file does not compile.
      • Make sure you use semic olons to terminate lines where required.
      • If a value must be quoted, quote it. You might need to enclose a single-quoted value (needed by PL/SQL) inside double quotes (needed by PSP).
      • Mistakes in the PSP directives are usually reported through PL/SQL syntax messages. Check that your direct ives use the right syntax, that directives are closed properly, and that you are using the right element (declaration, expression, or code block) depending on what goes inside it.
      • PSP attribute names are case-se nsitive. Most are specified in all lowercase; contentType and errorPage must be specified as mixed-case.
    • The next step is to run the PSP file by requesting its URL in a Web br owser. At this point, you might get an error that the file is not found.
      • Make sure you are requesting the right virtual path, depending on the way the Web gateway is configured. Typically, the path includes the hostname, optionally a port number, the schema name, and the name of the stored procedure (with no .psp ext ension).
      • Remember, if you use the -replace option when compiling the file, the old version of the stored procedure is erased. So, after a failed compilation, you must fix the error or the page is no t available. You might want to test new scripts in a separate schema until they are ready, then load them into the production schema.
      • If you copied the file from another file, remember to change any procedure na me directives in the source to match the new file name.
      • Once you get one file- not-found error, make sure to request the latest version of the page the next time. The error page might be cached by the browser. Yo u might need to press Shift-Reload in the browser to bypass its cache.
    • < /a>When the PSP script is run, and the results come back to the browser, use standard debugging techniques to check for and correct w rong output. The tricky part is to set up the interface between different HTML forms, scripts, and CGI programs so that all the right values are passed into your page. The page might return an error because of a parameter mismatch.
      • To see exactly what is being passed to your page, use METHOD=GET in the calling form s o that the parameters are visible in the URL.
      • Make sure that the form or CGI p rogram that calls your page passes the correct number of parameters, and that the names specified by the NAME= attribute s on the form match the parameter names in the PSP file. If the form includes any hidden input fields, or uses the NAME= attribute on the Submit or Reset buttons, the PSP file must declare equivalent parameters.
      • Make sure that the parameters can be cast from string into the correct PL/SQL types. For exam ple, do not include alphabetic characters if the parameter in the PSP file is declared as a NUMBER.
      • Make sure that the query string of the URL consists of name-value pairs, separated by equals signs , especially if you are passing parameters by constructing a hard-coded link to the page.
      • If you are passing a lot of parameter data, such as large strings, you might exceed the volume that can be passed with METHOD=GET. You can switch to METHOD=POST in the calling form without changing your PSP file.
      • Although the loadpsp command reports line numbers correctly when there is a synt ax error in your source file, line numbers reported for runtime errors refer to a transformed version of the source and do not match the line numbers in the original source. When you encounter errors like these, that produce an error trace instead of the expected We b page, you will need to locate the error through exception handlers and by printing debug output.

    Putting an Application usin g PL/SQL Server Pages into Production

    When you start developing an a pplication with PSP, you may spend most of your time getting the logic correct in the script. Before putting the application into pro duction, consider other issues such as usability and download speed:

    • Pages can be rendered faster in the browser if the HEIGHT= and WIDTH= attributes are specified for a ll images. You might standardize on picture sizes, or store the height and width of images in the database along with the data or URL .
    • For viewers who turn off graphics, or who use alternative browsers that read the text out loud, include a description of significant images using the ALT= attribute. You might store the descriptio n in the database along with the image.
    • Although an HTML table provides a good way to display data, a large table can make your application seem slow. Often, the reader sees a blank page until the entire table i s downloaded. If the amount of data in an HTML table is large, consider splitting the output into multiple tables.
    • If you set text, font, or background colors, test your application with different combinations o f browser color settings:
      • Test what happens if you override just the foreground color in the browser, or just the background color, or both.
      • Ge nerally, if you set one color (such as the foreground text color), you should set all the colors through the <BODY> tag, to avoid hard-to-read combinations like white text on a white background.
      • < /a>If you use a background image, specify a similar background color to provide proper contrast for viewers who do not load graphics.
      • If the information conveyed by different colors is crucial, consider using so me other method instead of or in addition to the color change. For example, you might put a graphic icon next to special items in a t able. Some of your viewers may see your page on a monochrome screen, or on browsers that cannot represent different colors. (Such bro wsers might fit in a shirt pocket and use a stylus for input.)
    • Provi ding context information prevents users from getting lost. Include a descriptive <TITLE> tag for your page. If the user is partway through a procedure, indicate which step is represented by your page. Provide links to logical points to continue wi th the procedure, return to a previous step, or cancel the procedure completely. Many pages might use a standard set of links that yo u embed using the include directive.
    • In any entry fields, users might enter in correct values. Where possible, use select lists to present a set of choices. Validate any text entered in a field before passing it to SQL. The earlier you can validate, the better; a JavaScript routine can detect incorrect data and prompt the user to correct it be fore they press the Submit button and make a call to the database.
    • Browsers tend to be lenient when displaying incorrect HTML. But what looks OK in one browser might look bad or might not display at all in another browser.
      • Pay attention to HTML rules for quotatio n marks, closing tags, and especially for anything to do with tables.
      • Minimize the dependence on tags that are only supported by a single browser. Sometimes you can provide an extra bonus using such tags, but yo ur application should still be usable with other browsers.
      • You can check the v alidity, and even in some cases the usability, of your HTML for free at many sites on the World Wide Web.

    Enabling PL/SQL Web Applications for XML

    You might find that a PL/SQL Web application ne eds to accept data in XML format, or produce tagged output that is XML rather than HTML.

    Wh en displaying output, you can set the MIME type of the Web page to text/xml so that an XML-enabled browser or other Web client software can render it as XML.

    You can also use a number of built-in features like t he XMLTYPE type, DBMS_XMLQUERY and DBMS_XMLSAVE packages, and SYS_XMLGEN and SYS_XMLAGG functions within your application. For information about these features, see Oracle XML Developer's Kit Programmer's Guide.