17.8 How do I…Maintain persistent states with cookies?
Problem
I know that I can use hidden fields in forms to maintain persistent variables in a session. If a lot of hidden fields are in a form, they have to travel back and forth from the client to the server, hurting performance. I want to use client cookies stored in the browser to maintain state information throughout the client session, or sometimes until the expiration date of the cookie. How do I maintain persistent state information with cookies?
Technique
Netscape introduced the concept of browser cookies also supported in Microsoft Internet Explorer. Cookies are strings that reside on the client that allow applica-tions to maintain state. Once the cookie is set, it remains in memory throughout the client session or until it expires, which depends on the expiration time set with the cookie while creating it. If the browser is exited, it is saved in a file, and when the browser is restarted it is reloaded into memory if the cookie has not expired. You will need to use cookie variables of the OWA_COOKIE.COOKIE datatype defined as:
TYPE COOKIE IS RECORD (
NAME
VARCHAR2(4096),
VALS
VC_ARR,
NUM_VALS
INTEGER);
The HTTP protocol allows cookie names to be overloaded; in other words, multiple values can be associated with the same cookie name. In the OWA_COOKIE.COOKIE datatype, VALS is a PL/SQL Index-By table of VARCHAR2 (4096) to hold all values associated with a given cookie name. The OWA_COOKIE package provides procedures and functions to send cookies to and get them from the browser, as shown in Table 17.9.
Table 17.9 Functions contained in the OWA_COOKIE package
Function/Procedure Description owa_cookie.send Transmits a cookie to the client. owa_cookie.get Returns values associated with a cookie name. owa_cookie.get_all Returns all cookie names and their values. owa_cookie.remove Forces a cookie to expire immediately. Cookies use expiration dates defined in Greenwich Mean Time (GMT). If you are not on GMT and want to change the time zone used by cookies, the OWA_INIT package provides constants that you override to set the time zone used by cookies. The system date is calculated using the constants in the OWA_INIT package. You can change the value of the dbms_server_timezone or the dbms_server_gmtdiff to give the offset of your time zone from GMT and then reload the package.
Steps
1. Run SQL*Plus and connect as the WAITE account. Run the CHP17_19.SQL file in SQL*Plus, as shown in Figure 17.39. This example uses a cookie to store a counter in the browser, indicating how many times that page was visited by the user in last 24 hours.
Line 2 declares a variable for the cookie of the OWA_COOKIE.COOKIE datatype. Line 4 calls the OWA_COOKIE.GET function to retrieve the cookie from the browser. In line 5, the OWA_UTIL.MIME_HEADER procedure is used to change the default MIME header before sending the cookie using the OWA_COOKIE.SEND procedure, which sends a Set-Cookie header to the browser, as in lines 7 and 9.
Line 11 closes the MIME header. The NUM_VALS variable in the cookie indicates the number of values in the cookie for a given cookie name. When the procedure is invoked for the first time, the cookie does not exist in the browser and NUM_VALS is 0. A cookie named COUNTER is set to 1 on the first visit. The value in the cookie is simply incremented by 1 on subsequent visits to the same page. As only one value is set for the cookie, the value is retrieved from the array of values in the cookie as COOKIE.VALS(1).
The OWA_COOKIE.REMOVE procedure is not used in the example as the expiration time was set as 24 hours by using the SYSDATE + 1 in the OWA_COOKIE.SEND call. If you are not using an expiration time with cookies, then it is recommended that cookies be explicitly removed whenever they are no longer in use.
Note that the OWA_UTIL.MIME_HEADER must be called before any other calls to procedures in the HTP package. If the default MIME header is opened by any other call, two newline characters are sent and the header is closed; all this is transparent to the user. Note that in line 5, the second parameter is set to FALSE so that only one newline is sent, and the HTTP header is still open until it is closed explicitly in line 11 using the OWA_UTIL.HTTP_HEADER_CLOSE procedure. If you are calling any other procedure from the OWA_UTIL package that manipulates with the HTTP header sent to the browser, such as the OWA_UTIL.REDIRECT_URL, make sure that you use the technique described in this step.
2. Open the URL for the cookie example in your browser. Figure 17.40 shows the document in the browser.
Exit the browser and restart it again. Open the URL for the cookie example in your browser again. Figure 17.41 shows the results in the browser on the second visit to the page.
How It Works
The OWA_COOKIE.SEND procedure and OWA_COOKIE.GET function provide a means to send and receive cookies to and from the browser. An HTTP header must be opened by calling OWA_UTIL.MIME_HEADER before sending the cookie to the browser. The HTTP header is closed by calling the OWA_UTIL.HTTP_HEADER_CLOSE.
Step 1 creates a stored procedure that generates an HTML document which maintains a counter cookie in the browser. Within the document, the cookie is sent to the browser by using the OWA_COOKIE.SEND procedure and retrieved from the browser using the OWA_COOKIE.GET function. Step 2 demonstrates the operation in Netscape Navigator. Click the Netscape Cookie Specification link if you want to find out more about cookies.
Comments
With Netscape Navigator, cookie size cannot exceed 4 KB and only 20 cookies are allowed per domain, which is far less than what would be required when building robust business applications. A fairly simple technique to achieve persistence on the server side is to store state information in the database itself. Cookies can be saved in the database itself to provide server-side cookie support. For more complex applications, it would be beneficial to exploit the Transaction Service in OWAS 3.0 to overcome HTTP statelessness.
With Microsoft Internet Explorer, cookies work differently. Only one cookie per domain is allowed as compared to Netscape’s 20. Also, cookies only from a server with a network domain are accepted.