bea.com | products | dev2dev | support | askBEA

Download Docs | Site Map | Glossary
Search

Programming WebLogic HTTP Servlets

Programming Tasks

The following sections describe how to write HTTP servlets in a WebLogic Server environment:

- Initializing a Servlet

- Providing an HTTP Response

- Retrieving Client Input

- Session Tracking from a Servlet

- Using Cookies in a Servlet

- Response Caching

- Using WebLogic Services from an HTTP Servlet

- Accessing Databases

- Threading Issues in HTTP Servlets

- Dispatching Requests to Another Resource

Initializing a Servlet

Normally, WebLogic Server initializes a servlet when the first request is made for the servlet. Subsequently, if the servlet is modified, the destroy() method is called on the existing version of the servlet. Then, after a request is made for the modified servlet, the init() method of the modified servlet is executed. For more information, see Servlet Development Tips.

When a servlet is initialized, WebLogic Server executes the init() method of the servlet. Once the servlet is initialized, it is not initialized again until you restart WebLogic Server or the servlet code when the servlet is modified. If you choose to override the init() method, your servlet can perform certain tasks, such as establishing database connections, when the servlet is initialized. (See Overriding the init() Method)

Initializing a Servlet when WebLogic Server Starts

Rather than having WebLogic Server initialize a servlet when the first request is made for it, you can first configure WebLogic Server to initialize a servlet when the server starts. You do this by specifying the servlet class in the <load-on-startup> element in the Web Application deployment descriptor. For more information see "Servlet Element". The use of startup classes to load servlets is no longer supported.

You can pass parameters to an HTTP servlet during initialization by defining these parameters in the Web Application containing the servlet. You can use these parameters to pass values to your servlet every time the servlet is initialized without having to rewrite the servlet. For more information, see Writing Web Application Deployment Descriptors.

For example, the following entries in the Web Application deployment descriptor define two initialization parameters: greeting, which has a value of Welcome and person, which has a value of WebLogic Developer.

>     <servlet>
>       ...
>       <init-param>
>         <param-name>greeting</param-name>
>         <param-value>Welcome</param-value>
>         <description>The salutation</description>
>       </init-param>
>       <init-param>
>         <param-name>person</param-name>
>         <param-value>WebLogic Developer</param-value>
>         <description>name</description>
>       </init-param>
>     </servlet>

To retrieve initialization parameters, call the getInitParameter(String name) method from the parent javax.servlet.GenericServlet class. When passed the name of the parameter, this method returns the parameter's value as a String.

Overriding the init() Method

You can have your servlet execute tasks at initialization time by overriding the init() method. The following code fragment reads the <init-param> tags that define a greeting and a name in the Web Application deployment descriptor:

>     String defaultGreeting;
>     String defaultName;

>     public void init(ServletConfig config)
>         throws ServletException {
>       if ((defaultGreeting = getInitParameter("greeting")) == null)
>         defaultGreeting = "Hello";

>       if ((defaultName = getInitParameter("person")) == null)
>         defaultName = "World";
>     }

The values of each parameter are stored in the class instance variables defaultGreeting and defaultName. The first code tests whether the parameters have null values, and if null values are returned, provides appropriate default values.

You can then use the service() method to include these variables in the response. For example:

>       out.print("<body><h1>");
>       out.println(defaultGreeting + " " + defaultName + "!");
>       out.println("</h1></body></html>");

The full source code and instructions for compiling, installing, and trying out an example called HelloWorld2.java, which illustrates the use of the init() method, can be found in the samples/examples/servlets directory of your WebLogic Server distribution.

The init() method of a servlet does whatever initialization work is required when WebLogic Server loads the servlet. The default init() method does all of the initial work that WebLogic Server requires, so you do not need to override it unless you have special initialization requirements. If you do override init(), first call super.init() so that the default initialization actions are done first.

Providing an HTTP Response

This section describes how to provide a response to the client in your HTTP servlet. Deliver all responses by using the HttpServletResponse object that is passed as a parameter to the service() method of your servlet.

1. Configure the HttpServletResponse.

   Using the HttpServletResponse object, you can set several servlet properties that are translated into HTTP header information:

   - At a minimum, set the content type using the setContentType() method before you obtain the output stream to which you write the page contents. For HTML pages, set the content type to text/html. For example:

     >     res.setContentType("text/html");

   - (optional) You can also use the setContentType() method to set the character encoding. For example:

     >     res.setContentType("text/html;ISO-88859-4");

   - Set header attributes using the setHeader() method. For dynamic responses, it is useful to set the "Pragma" attribute to no-cache, which causes the browser to always reload the page and ensures the data is current. For example:

     >     res.setHeader("Pragma", "no-cache");

2. Compose the HTML page.

   The response that your servlet sends back to the client must look like regular HTTP content, essentially formatted as an HTML page.Your servlet returns an HTTP response through an output stream that you obtain using the response parameter of the service() method. To send an HTTP response:

   1. Obtain an output stream by using the HttpServletResponse object and one of the methods shown in the following two examples:

   - PrintWriter out = res.getWriter();

   - ServletOutputStream out = res.getOutputStream();

     You can use both PrintWriter and ServletOutputStream in the same servlet (or in another servlet that is included in a servlet). The output of both is written to the same buffer.

   1. Write the contents of the response to the output stream using the print() method. You can use HTML tags in these statements. For example:

      >     out.print("<html><head><title>My Servlet</title>");
      >     out.print("</head><body><h1>");
      >     out.print("Welcome");
      >     out.print("</h1></body></html>");

      Any time you print data that a user has previously supplied, BEA recommends that you remove any HTML special characters that a user might have entered. If you do not remove these characters, your Web site could be exploited by cross-site scripting. For more information, refer to Securing Client Input in Servlets.

      Do not close the output stream by using the close() method, and avoid flushing the contents of the stream. If you do not close or flush the output stream, WebLogic Server can take advantage of persistent HTTP connections, as described in the next step.

3. Optimize the response.

   By default, WebLogic Server attempts to use HTTP persistent connections whenever possible. A persistent connection attempts to reuse the same HTTP TCP/IP connection for a series of communications between client and server. Application performance improves because a new connection need not be opened for each request. Persistent connections are useful for HTML pages containing many in-line images, where each requested image would otherwise require a new TCP/IP connection.

   Using the WebLogic Server Administration Console, you can configure the amount of time that WebLogic Server keeps an HTTP connection open.

   WebLogic Server must know the length of the HTTP response in order to establish a persistent connection and automatically adds a Content-Length property to the HTTP response header. In order to determine the content length, WebLogic Server must buffer the response. However, if your servlet explicitly flushes the ServletOutputStream, WebLogic Server cannot determine the length of the response and therefore cannot use persistent connections. For this reason, you should avoid explicitly flushing the HTTP response in your servlets.

   You may decide that, in some cases, it is better to flush the response early to display information in the client before the page has completed; for example, to display a banner advertisement while some time-consuming page content is calculated. Conversely, you may want to increase the size of the buffer used by the servlet engine to accommodate a larger response before flushing the response. You can manipulate the size of the response buffer by using the related methods of the javax.servlet.ServletResponse interface.

   The default value of the WebLogic Server response buffer is 12K and the buffer size is internally calculated in terms of CHUNK_SIZE where CHUNK_SIZE = 4088 or 4Kb; if the user sets 5Kb the server rounds the request up to the nearest multiple of CHUNK_SIZE which is 2. and the buffer is set to 8176 or 8Kb.

Retrieving Client Input

The HTTP servlet API provides a interface for retrieving user input from Web pages.

An HTTP request from a Web browser can contain more than the URL, such as information about the client, the browser, cookies, and user query parameters. Use query parameters to carry user input from the browser. Use the GET method appends paramters to the URL address, and the POST method includes them in the HTTP request body.

HTTP servlets need not deal with these details; information in a request is available through the HttpServletRequest object and can be accessed using the request.getParameter() method, regardless of the send method.

Read the following for more detailed information about the ways to send query parameters from the client:

- Encode the parameters directly into the URL of a link on a page. This approach uses the GET method for sending parameters. The parameters are appended to the URL after a ? character. Multiple parameters are separated by a & character. Parameters are always specified in name=value pairs so the order in which they are listed is not important. For example, you might include the following link in a Web page, which sends the parameter color with the value purple to an HTTP servlet called ColorServlet:

  >     <a href=
  >        "http://localhost:7001/myWebApp/ColorServlet?color=purple">
  >        Click Here For Purple!</a>

- Manually enter the URL, with query parameters, into the browser location field. This is equivalent to clicking the link shown in the previous example.

- Query the user for input with an HTML form. The contents of each user input field on the form are sent as query parameters when the user clicks the form's Submit button. Specify the method used by the form to send the query parameters (POST or GET) in the <FORM> tag using the METHOD="GET|POST" attribute.

Query parameters are always sent in name=value pairs, and are accessed through the HttpServletRequest object. You can obtain an Enumeration of all parameter names in a query, and fetch each parameter value by using its parameter name. A parameter usually has only one value, but it can also hold an array of values. Parameter values are always interpreted as Strings, so you may need to cast them to a more appropriate type.

The following sample from a service() method examines query parameter names and their values from a form. Note that request is the HttpServletRequest object.

>       Enumeration params = request.getParameterNames();
>       String paramName = null;
>       String[] paramValues = null;

>       while (params.hasMoreElements()) {
>         paramName = (String) params.nextElement();
>         paramValues = request.getParameterValues(paramName);
>         System.out.println("\nParameter name is " + paramName);
>         for (int i = 0; i < paramValues.length; i++) {
>           System.out.println(", value " + i + " is " +
>                              paramValues[i].toString());
>         }
>       }

Note: Any time you print data that a user has supplied, BEA recommends that you remove any HTML special characters that a user might have entered. If you do not remove these characters, your Web site could be exploited by cross-site scripting. For more information, refer to Securing Client Input in Servlets.

Methods for Using the HTTP Request

This section defines the methods of the javax.servlet.HttpServletRequest interface that you can use to get data from the request object. You should keep the following limitations in mind:

- You cannot read request parameters using any of the getParameter() methods described in this section and then attempt to read the request with the getInputStream() method.

- You cannot read the request with getInputStream() and then attempt to read request parameters with one of the getParameter() methods.

If you attempt either of the preceding procedures, an illegalStateException is thrown.

You can use the following methods of javax.servlet.HttpServeletRequest to retrieve data from the request object:

  HttpServletRequest.getMethod()

  Allows you to determine the request method, such as GET or POST.

  HttpServletRequest.getQueryString()

  Allows you to access the query string. (The remainder of the requested URL, following the ? character.)

  HttpServletRequest.getParameter()

  Returns the value of a parameter.

  HttpServletRequest.getParameterNames()

  Returns an array of parameter names.

  HttpServletRequest.getParameterValues()

  Returns an array of values for a parameter.

  HttpServletRequest.getInputStream()

  Reads the body of the request as binary data. If you call this method after reading the request parameters with getParameter(), getParameterNames(), or getParameterValues(), an illegalStateException is thrown.

Example: Retrieving Input by Using Query Parameters

In this example, the HelloWorld2.java servlet example is modified to accept a username as a query parameter, in order to display a more personal greeting. (For the complete code, see the HelloWorld3.java servlet example, located in the samples/examples/servlets directory of your WebLogic Server distribution.) The service() method is shown here.

Listing 3-1 Retrieving Input with the service() Method

>     public void service(HttpServletRequest req,
>              HttpServletResponse res)
>          throws IOException
>     {
>       String name, paramName[];
>       if ((paramName = req.getParameterValues("name"))
>           != null) {
>         name = paramName[0];
>       }
>       else {
>         name = defaultName;
>       }

>       // Set the content type first
>       res.setContentType("text/html");
>       // Obtain a PrintWriter as an output stream
>       PrintWriter out = res.getWriter();

>       out.print("<html><head><title>" +
>                   "Hello World!" + </title></head>");
>       out.print("<body><h1>");
>       out.print(defaultGreeting + " " + name + "!");
>       out.print("</h1></body></html>");
>     }

The getParameterValues() method retrieves the value of the name parameter from the HTTP query parameters. You retrieve these values in an array of type String. A single value for this parameter is returned and is assigned to the first element in the name array. If the parameter is not present in the query data, null is returned; in this case, name is assigned to the default name that was read from the <init-param> by the init() method.

Do not base your servlet code on the assumption that parameters are included in an HTTP request. The getParameter() method has been deprecated; as a result, you might be tempted to shorthand the getParameterValues() method by tagging an array subscript to the end. However, this method can return null if the specified parameter is not available, resulting in a NullPointerException.

For example, the following code triggers a NullPointerException:

>     String myStr = req.getParameterValues("paramName")[0];

Instead, use the following code:

>     if ((String myStr[] =
>               req.getParameterValues("paramName"))!=null) {
>       // Now you can use the myStr[0];
>     }
>     else {
>       // paramName was not in the query parameters!
>     }

Securing Client Input in Servlets

This ability to retrieve and return user-supplied data can present a security vulnerability called cross-site scripting, which can be exploited to steal a user's security authorization. For a detailed description of cross-site scripting, refer to "Understanding Malicious Content Mitigation for Web Developers" (a CERT security advisory) at http://www.cert.org/tech_tips/malicious_code_mitigation.html.

To remove the security vulnerability, before you return data that a user has supplied, scan the data for any of the HTML special characters in Table 3-1. If you find any special characters, replace them with their HTML entity or character reference. Replacing the characters prevents the browser from executing the user-supplied data as HTML.

Table 3-1 HTML Special Characters that Must Be Replaced

Replace this special character: | With this entity/character reference:
--- | ---
< | &lt;
> | &gt;
( | &40;
) | &41;
# | &35;
& | &38;

Using a WebLogic Server Utility Method

WebLogic Server provides the weblogic.servlet.security.Utils.encodeXSS() method to replace the special characters in user-supplied data. To use this method, provide the user-supplied data as input. For example, to secure the user-supplied data in Listing 3-1, replace the following line:
out.print(defaultGreeting + " " + name + "!");

with the following:
out.print(defaultGreeting + " " +
weblogic.security.servlet.encodeXSS(name) + "!");

To secure an entire application, you must use the encodeXSS() method each time you return user-supplied data. While the previous example in Listing 3-1 is an obvious location in which to use the encodeXSS() method, Table 3-2 describes other locations to consider.

Table 3-2 Code that Returns User-Supplied Data

Page Type | User-Supplied Data | Example
--- | --- | ---
Error page | Erroneous input string, invalid URL, username | An error page that says "username is not permitted access."
Status page | Username, summary of input from previous pages | A summary page that asks a user to confirm input from previous pages.
Database display | Data presented from a database | A page that displays a list of database entries that have been previously entered by a user.

Session Tracking from a Servlet

Session tracking enables you to track a user's progress over multiple servlets or HTML pages, which, by nature, are stateless. A session is defined as a series of related browser requests that come from the same client during a certain time period. Session tracking ties together a series of browser requests—think of these requests as pages—that may have some meaning as a whole, such as a shopping cart application.

The following sections discuss various aspets of tracking sessions from an HTTP servlet:

- A History of Session Tracking

- Tracking a Session with an HttpSession Object

- Lifetime of a Session

- How Session Tracking Works

- Detecting the Start of a Session

- Setting and Getting Session Name/Value Attributes

- Logging Out and Ending a Session

- Configuring Session Tracking

- Using URL Rewriting Instead of Cookies

- URL Rewriting and Wireless Access Protocol (WAP)

- Making Sessions Persistent

A History of Session Tracking

Before session tracking matured conceptually, developers tried to build state into their pages by stuffing information into hidden fields on a page or embedding user choices into URLs used in links with a long string of appended characters. You can see good examples of this at most search engine sites, many of which still depend on CGI. These sites track user choices with URL parameter name=value pairs that are appended to the URL, after the reserved HTTP character ?. This practice can result in a very long URL that the CGI script must carefully parse and manage. The problem with this approach is that you cannot pass this information from session to session. Once you lose control over the URL—that is, once the user leaves one of your pages—the user information is lost forever.

Later, Netscape introduced browser cookies, which enable you to store user-related information about the client for each server. However, some browsers still do not fully support cookies, and some users prefer to turn off the cookie option in their browsers. Another factor that should be considered is that most browsers limit the amount of data that can be stored with a cookie.

Unlike the CGI approach, the HTTP servlet specification defines a solution that allows the server to store user details on the server bdyond a single session, and protects your code from the complexities of tracking sessions. Your servlets can use an HttpSession object to track a user's input over the span of a single session and to share session details among multiple servlets. Session data can be persisted using a variety of methods available with WebLogic Service.

Tracking a Session with an HttpSession Object

According to the Java Servlet API, which WebLogic Server implements and supports, each servlet can access a server-side session by using its HttpSession object. You can access an HttpSession object in the service() method of the servlet by using the HttpServletRequest object with the variable request variable, as shown:

>     HttpSession session = request.getSession(true);

An HttpSession object is created if one does not already exist for that client when the request.getSession(true)method is called with the argument true. The session object lives on WebLogic Server for the lifetime of the session, during which the session object accumulates information related to that client. Your servlet adds or removes information from the session object as necessary. A session is associated with a particular client. Each time the client visits your servlet, the same associated HttpSession object is retrieved when the getSession() method is called.

For more details on the methods supported by the HttpSession, refer to the HttpServlet API.

In the following example, the service() method counts the number of times a user requests the servlet during a session.

>     public void service(HttpServletRequest request,
>                         HttpServletResponse, response)
>                 throws IOException
>     {
>       // Get the session and the counter param attribute
>       HttpSession session = request.getSession (true);
>       Integer ival = (Integer)
>                      session.getAttribute("simplesession.counter");
>       if (ival == null) // Initialize the counter
>         ival = new Integer (1);
>       else // Increment the counter
>         ival = new Integer (ival.intValue () + 1);
>       // Set the new attribute value in the session
>       session.setAttribute("simplesession.counter", ival);
>       // Output the HTML page
>       out.print("<HTML><body>");
>       out.print("<center> You have hit this page ");
>       out.print(ival + " times!");
>       out.print("</body></html>");
>     }

Lifetime of a Session

A session tracks the selections of a user over a series of pages in a single transaction. A single transaction may consist of several tasks, such as searching for an item, adding it to a shopping cart, and then processing a payment. A session is transient, and its lifetime ends when one of the following occurs:

- A user leaves your site and the user's browser does not accept cookies.

- A user quits the browser.

- The session is timed out due to inactivity.

- The session is completed and invalidated by the servlet.

- The user logs out and is invalidated by the servlet.

For more persistent, long-term storage of data, your servlet should write details to a database using JDBC or EJB and associate the client with this data using a long-lived cookie and/or username and password. Although this document states that sessions use cookies and persistence internally, you should not use sessions as a general mechanism for storing data about a user.

How Session Tracking Works

How does WebLogic Server know which session is associated with each client? When an HttpSession is created in a servlet, it is associated with a unique ID. The browser must provide this session ID with its request in order for the server to find the session data again. The server attempts to store this ID by setting a cookie on the client. Once the cookie is set, each time the browser sends a request to the server it includes the cookie containing the ID. The server automatically parses the cookie and supplies the session data when your servlet calls the getSession() method.

If the client does not accept cookies, the only alternative is to encode the ID into the URL links in the pages sent back to the client. For this reason, you should always use the encodeURL() method when you include URLs in your servlet response. WebLogic Server detects whether the browser accepts cookies and does not unnecessarily encode URLs. WebLogic automatically parses the session ID from an encoded URL and retrieves the correct session data when you call the getSession() method. Using the encodeURL() method ensures no disruption to your servlet code, regardless of the procedure used to track sessions. For more information, see Using URL Rewriting Instead of Cookies.

Detecting the Start of a Session

After you obtain a session using the getSession(true) method, you can tell whether the session has just been created by calling the HttpSession.isNew() method. If this method returns true, then the client does not already have a valid session, and at this point it is unaware of the new session. The client does not become aware of the new session until a reply is posted back from the server.

Design your application to accommodate new or existing sessions in a way that suits your business logic. For example, your application might redirect the client's URL to a login/password page if you determine that the session has not yet started, as shown in the following code example:

>     HttpSession session = request.getSession(true);
>     if (session.isNew()) {
>       response.sendRedirect(welcomeURL);
>     }

On the login page, provide an option to log in to the system or create a new account. You can also specify a login page in your Web Application. For more information, see login-config.

Setting and Getting Session Name/Value Attributes

You can store data in an HttpSession object using name=value pairs. Data stored in a session is available through the session. To store data in a session, use these methods from the HttpSession interface:

>     getAttribute()
>     getAttributeNames()
>     setAttribute()
>     removeAttribute()

The following code fragment shows how to get all the existing name=value pairs:

>     Enumeration sessionNames = session.getAttributeNames();
>     String sessionName = null;
>     Object sessionValue = null;

>     while (sessionNames.hasMoreElements()) {
>       sessionName = (String)sessionNames.nextElement();
>       sessionValue = session.getAttribute(sessionName);
>       System.out.println("Session name is " + sessionName +
>                          ", value is " + sessionValue);
>     }

To add or overwrite a named attribute, use the setAttribute() method. To remove a named attribute altogether, use the removeAttribute() method.

Note: You can add any Java descendant of Object as a session attribute and associate it with a name. However, if you are using session persistence, your attribute value objects must implement java.io.Serializable.

Logging Out and Ending a Session

If your application deals with sensitive information, consider offering the ability to log out of the session. This is a common feature when using shopping carts and Internet email accounts. When the same browser returns to the service, the user must log back in to the system.

Using session.invalidate() for a Single Web Application

User authentication information is stored both in the users's session data and in the context of a server or virtual host that is targeted by a Web Application. Using the session.invalidate() method, which is often used to log out a user, only invalidates the current session for a user—the user's authentication information still remains valid and is stored in the context of the server or virtual host. If the server or virtual host is hosting only one Web Application, the session.invalidate()method, in effect, logs out the user.

Do not reference an invalidated session after calling session.invalidate(). If you do, an IllegalStateException is thrown. The next time a user visits your servlet from the same browser, the session data will be missing, and a new session will be created when you call the getSession(true) method. At that time you can send the user to the login page again.

Implementing Single Sign-On for Multiple Applications

If the server or virtual host is targeted by many Web Applications, another means is required to log out a user from all Web Applications. Because the Servlet specification does not provide an API for logging out a user from all Web Applications, the following methods are provided.

  weblogic.servlet.security.ServletAuthentication.logout()

  Removes the authentication data from the users's session data, which logs out a user but allows the session to remain alive.

  weblogic.servlet.security.ServletAuthentication.invalidateAll()

  Invalidates all the sessions and removes the authentication data for the current user. The cookie is also invalidated.

  weblogic.servlet.security.ServletAuthentication.killCookie()

  Invalidates the current cookie by setting the cookie so that it expires immediately when the response is sent to the browser. This method depends on a successful response reaching the user's browser. The session remains alive until it times out.

Exempting a Web Application for Single Sign-on

If you want to exempt a Web Application from participating in single sign-on, define a different cookie name for the exempted Web Application. For more information, see Configuring Session Cookies.

Configuring Session Tracking

WebLogic Server provides many configurable attributes that determine how WebLogic Server handles session tracking. For details about configuring these session tracking attributes, see "Session descriptor".

Using URL Rewriting Instead of Cookies

In some situations, a browser may not accept cookies, which means that session tracking with cookies is not possible. URL rewriting is a workaround to this scenario that can be substituted automatically when WebLogic Server detects that the browser does not accept cookies. URL rewriting involves encoding the session ID into the hyperlinks on the Web pages that your servlet sends back to the browser. When the user subsequently clicks these links, WebLogic Server extracts the ID from the URL and finds the appropriate HttpSession. Then you use the getSession() method to access session data.

To enable URL rewriting in WebLogic Server, set the UrlRewritingEnabled attribute to true in the "Session descriptor" element of the WebLogic-specific deployment descriptor.

To make sure your code correctly handles URLs in order to support URL rewriting, consider the following guidelines:

- You should avoid writing a URL straight to the output stream, as shown here:

  >     out.println("<a href=\"/myshop/catalog.jsp\">catalog</a>");

  Instead, use the HttpServletResponse.encodeURL() method. For example:

  >     out.println("<a href=\""
  >          + response.encodeURL("myshop/catalog.jsp")
  >          + "\">catalog</a>");

- Calling the encodeURL() method determines if the URL needs to be rewritten and, if necessary, rewrites the URL by including the session ID in the URL.

- Encode URLs that send redirects, as well as URLs that are returned as a response to WebLogic Server. For example:

  >     if (session.isNew())
  >      response.sendRedirect(response.encodeRedirectUrl(welcomeURL));

WebLogic Server uses URL rewriting when a session is new, even if the browser accepts cookies, because the server cannot determine, during the first visit of a session, whether the browser accepts cookies.

Your servlet may determine whether a given session was returned from a cookie by checking the Boolean returned from the HttpServletRequest.isRequestedSessionIdFromCookie() method. Your application may respond appropriately, or it may simply rely on URL rewriting by WebLogic Server.

Note: The CISCO Local Director load balancer expects a question mark "?" delimiter for URL rewriting. Because the WLS URL-rewriting mechanism uses a semicolon ";" as the delimiter, our URL re-writing is incompatible with this load balancer.

URL Rewriting and Wireless Access Protocol (WAP)

If you are writing a WAP application, you must use URL rewriting because the WAP protocol does not support cookies. In addition, some WAP devices impose a 128-character limit (including parameters) on the length of a URL, which limits the amount of data that can be transmitted using URL rewriting. To allow more space for parameters, you can limit the size of the session ID that is randomly generated by WebLogic Server by specifying the number of bytes with the IDLength attribute in the <session-descriptor> element of the WebLogic-specific deployment descriptor, weblogic.xml.

The minimum value is 8 bytes; the default value is 52 bytes; the maximum value is Integer.MAX_VALUE.

Making Sessions Persistent

You can set up WebLogic Server to record session data in a persistent store. If you are using session persistence, you can expect the following characteristics:

- Good failover, because sessions are saved when servers fail.

- Better load balancing, because any server can handle requests for any number of sessions, and use caching to optimize performance. For more information, see the cacheEntries property, under "Configuring Session Persistence".

- Sessions can be shared across clustered WebLogic Servers. Note that session persistence is no longer a requirement in a WebLogic Cluster. Instead, you can usein-memory replication of state. For more information, see Using WebLogic Server Clusters.

- For customers who want the highest in servlet session persistence, JDBC-based persistence is the best choice. For customers who want to sacrifice some amount of session persistence in favor of drastically better performance, in-memory replication is the appropriate choice. JDBC-based persistence is noticeably slower than in-memory replication. In some cases, in-memory replication has outperformed JDBC-based persistence for servlet sessions by a factor of eight.

- You can put any kind of Java object into a session, but only objects that are java.io.Serializable can be stored in a session. For more information, see "Configuring session persistence".

Scenarios to Avoid When Using Sessions

Do not use session persistence for storing long-term data between sessions. In other words, do not rely on a session still being active when a client returns to a site at some later date. Instead, your application should record long-term or important information in a database.

Sessions are not a convenience wrapper around cookies. Do not attempt to store long-term or limited-term client data in a session. Instead, your application should create and set its own cookies on the browser. Examples include an auto-login feature that allows a cookie to live for a long period, or an auto-logout feature that allows a cookie to expire after a short period of time. Here, you should not attempt to use HTTP sessions. Instead, you should write your own application-specific logic.

The format of the session id is specified internally, and may change from one version of WebLogic Server to another. For this reason, BEA Systems recommends that you do not create applications which require a specific session id format.

Use Serializable Attribute Values

When you use persistent sessions, all attribute value objects that you add to the session must implement java.io.Serializable. For more details on writing serializable classes, refer to the online java tutorial about serializable objects. If you add your own serializable classes to a persistent session, make sure that each instance variable of your class is also serializable. Otherwise, you can declare it as transient, and WebLogic Server does not attempt to save that variable to persistent storage. One common example of an instance variable that must be made transient is the HttpSession object. (See the notes on using serialized objects in sessions in the section Making Sessions Persistent.)

The HttpServletRequest, ServletContext, and HttpSession attributes will be serialized when a WebLogic Server instance detects a change in the web application classloader. The classloader changes when a webapp is redeployed, when there is a dynamic change in a servlet, or when there is a cross webapp forward or include.

To avoid having the attribute serialized,during a dynamic change in a servlet, turn off servlet-relad-check-secs in weblogic.xml. There is no way to avoid serialization of attributes for cross webapp dispatch or webapp redeployment.

Configuring Session Persistence

For details about setting up persistent sessions, see "Configuring session persistence".

Using Cookies in a Servlet

A cookie is a piece of information that the server asks the client browser to save locally on the user's disk. Each time the browser visits the same server, it sends all cookies relevant to that server with the HTTP request. Cookies are useful for identifying clients as they return to the server.

Each cookie has a name and a value. A browser that supports cookies generally allows each server domain to store up to 20 cookies of up to 4k per cookie.

Setting Cookies in an HTTP Servlet

To set a cookie on a browser, create the cookie, give it a value, and add it to the HttpServletResponse object that is the second parameter in your servlet's service method. For example:

>     Cookie myCookie = new Cookie("ChocolateChip", "100");
>     myCookie.setMaxAge(Integer.MAX_VALUE);
>     response.addCookie(myCookie);

This examples shows how to add a cookie called ChocolateChip with a value of 100 to the browser client when the response is sent. The expiration of the cookie is set to the largest possible value, which effectively makes the cookie last forever. Because cookies accept only string-type values, you should cast to and from the desired type that you want to store in the cookie. When using EJBs, a common practice is to use the home handle of an EJB instance for the cookie value and to store the user's details in the EJB for later reference.

Retrieving Cookies in an HTTP Servlet

You can retrieve a cookie object from the HttpServletRequest that is passed to your servlet as an argument to the service() method. The cookie itself is presented as a javax.servlet.http.Cookie object.

In your servlet code, you can retrieve all the cookies sent from the browser by calling the getCookies() method. For example:

>     Cookie[] cookies = request.getCookies();

This method returns an array of all cookies sent from the browser, or null if no cookies were sent by the browser. Your servlet must process the array in order to find the correct named cookie. You can get the name of a cookie using the Cookie.getName() method. It is possible to have more that one cookie with the same name, but different path attributes. If your servlets set multiple cookies with the same names, but different path attributes, you also need to compare the cookies by using the Cookie.getPath() method. The following code illustrates how to access the details of a cookie sent from the browser. It assumes that all cookies sent to this server have unique names, and that you are looking for a cookie called ChocolateChip that may have been set previously in a browser client.

>     Cookie[] cookies = request.getCookies();
>     boolean cookieFound = false;

>     for(int i=0; i < cookies.length; i++) {
>       thisCookie = cookies[i];
>       if (thisCookie.getName().equals("ChocolateChip")) {
>         cookieFound = true;
>         break;
>       }
>     }

>     if (cookieFound) {
>       // We found the cookie! Now get its value
>       int cookieOrder = String.parseInt(thisCookie.getValue());
>     }

For more details on cookies, see:

The Cookie API

Using Cookies That Are Transmitted by Both HTTP and HTTPS

Because HTTP and HTTPS requests are sent to different ports, some browsers may not include the cookie sent in an HTTP request with a subsequent HTTPS request (or vice-versa). This may cause new sessions to be created when servlet requests alternate between HTTP and HTTPS. To ensure that all cookies set by a specific domain are sent to the server every time a request in a session is made, set the CookieDomain attribute to the name of the domain. Set the CookieDomain attribute with the <session-descriptor> element of the WebLogic-specific deployment descriptor (weblogic.xml) for the Web Application that contains your servlet. For example:

>     <session-descriptor>
>       <session-param>
>         <param-name>CookieDomain</param-name>
>         <param-value>mydomain.com</param-value>
>       </session-param>
>     </session-descriptor>

The CookieDomain attribute instructs the browser to include the proper cookie(s) for all requests to hosts in the domain specified by mydomain.com. For more information about this property or configuring session cookies, see "Setting Up Session Management".

Secure Cookies

The CookieSecure parameter can be used to mark the sessionCookie as secure. If set, the client's browser will only send the cookie back over an HTTPS connection. This ensures that the cookie ID is secure and should only be used on websites that exclusively use HTTPS. Once this feature is enabled, session cookies over HTTP will no longer work; if your client is directed to a non-HTTPS location the session will not be sent. It is highly advisable to turn off URLRewriting if you use this feature; if your application attempts to encode URLs, the session ID will be shared over HTTP. Add the following to the <session-descriptor> element in your weblogic.xml to use this feature:

<session-param>

<param-name>CookieSecure</param-name>

<param-value>true</param-value>

</session-param>

<session-param>

Application Security and Cookies

Using cookies that enable automatic account access on a machine is convenient, but can be undesirable from a security perspective. When designing an application that uses cookies, follow these guidelines:

- Do not assume that a cookie is always correct for a user. Sometimes machines are shared or the same user may want to access a different account.

- Allow your users to make a choice about leaving cookies on the server. On shared machines, users may not want to leave automatic logins for their account. Do not assume that users know what a cookie is; instead, ask a question like:

  Automatically login from this computer?

- Always ask for passwords from users logging on to obtain sensitive data. Unless a user requests otherwise, you can store this preference and the password in the user's session data. Configure the session cookie to expire when the user quits the browser.

Response Caching

The cache filter works similarly to the cache tag with the following exceptions:

- It caches on a page level (or included page) instead of a JSP fragment level.

- Instead of declaring the caching parameters inside the document you can declare the parameters in the configuration of the web application.

The cache filter has some default behavior that the cache tag does not for pages that were not included from another page. The cache filter automatically caches the response headers Content-Type and Last-Modified. When it receives a request that results in a cached page it compares the If-Modified-Since request header to the Last-Modified response header to determine whether it needs to actually serve the content or if it can send an 302 SC_NOT_MODIFED status with an empty content instead.

Table 3-3

Attribute | Required | Default Value | Description
--- | --- | --- | ---
timeout | no | -1 | Cache timeout property. The amount of time, in seconds, after which the statements within the cache tag are refreshed. This is not proactive; the value is refreshed only if it is requested. If you prefer to use a unit of time other than seconds, you can specify an alternate unit by postfixing the value with desired unit: ms = milliseconds s = seconds (default) m = minutes h = hours d = days
scope | no | application | Specifies the scope in which the data is cached. Valid scopes include: parameter, requests the parameter request, requests the request requestHeader, requests the request Header responseHeader, requests the response Header session, requests the application, requests the application cluster, cluster scope (need to configure cluster listener for this) Most caches will be either session or application scope.
key | no | -- | Specifies additional values to be used when evaluating whether to cache the values contained within the tags. Typically a given cache is identified by the cache name that you configured in web.xml. If that is not specified the request uri is used as a cache name. Using keys you can specify additional values to identify a tag. For example, if you want to separate out the cache for a given end user, then in addition to the cache name you can specify the keys as the userid, values for which you want to pick it up from the request parameter scope (query param/post params) plus perhaps a client ip. So you will specify your keys as: "parameter.userid,parameter.clientip" Here "parameter" is the scope (request parameter scope) and "userid"/"clientip" are the parameters/attributes. This means the primary key for the cache becomes the cache name (request uri in this case) + value of userid request param + value of clientip request param. The list of keys is comma-separated. The value of this attribute is the name of the variable whose value you wish to use as a key into the cache. You can additionally specify a scope by prepending the name of the scope to the name. For example: parameter.key | page.key | request.key | application.key | session.key It defaults to searching through the scopes in the order shown in the preceding list. Each named key is available in the cache tag as a scripting variable. A list of keys is comma-separated.
async | no | false | If the async parameter is set to true, the cache will be updated asynchronously, if possible. The user that initiates the cache hit sees the old data.
name | no | -- | A unique name for the cache that allows caches to be shared across multiple JSP pages. This same buffer is used to store the data for all pages using the named cache. This attribute is useful for textually included pages that need cache sharing. If this attribute is not set, a unique name is chosen for the cache. We recommended that you avoid manually calculating the name of the tag; the key functionality can be used equivalently in all cases. The name is calculated as weblogic.jsp.tags.CacheTag. plus the URI plus a generated number representing the tag in the page you are caching. If different URIs reach the same JSP page, the caches are not shared in the default case. Use named caches in this case.
size | no | -1 (unlimited) | For caches that use keys, the number of entries allowed. The default is an unlimited cache of keys. With a limited number of keys the tag uses a least-used system to order the cache. Changing the value of the size attribute of a cache that has already been used does not change the size of that cache.
vars | no | -- | In addition to caching the transformed output of the cache, you can also cache calculated values within the block. These variables are specified exactly the same way as the cache keys. This type of caching is called Input caching. Variables are used to do input caching. When the cache is retrieved the variables are restored to the scope you specified. For example, for retrieving results from a database you used var1 from request parameter and var2 from session. When the cache is created the value of these variables are stored with the cache. The next time the cache is accessed these values are restored so you will be able to access them from their respective scopes. For example, var1 will be available from request and var2 from session.
flush | no | none | When set to true, the cache is flushed. This attribute must be set in an empty tag (ends with /).

Response Caching Attributes

The following example shows how to register a cache filter to cache all the HTML pages in a web app:

<filter>

<filter-name>HTML</filter-name>

<filter-class>weblogic.cache.filter.CacheFilter</filter-class>

</filter>

<filter-mapping>

<filter-name>HTML</filter-name>

<url-pattern>*.html</url-pattern>

</filter-mapping>

The cache system uses soft references for storing the cache. The garbage collector might or might not reclaim the cache depending on how recently the cache was created or accessed. It will clear the soft references in order to avoid throwing an OutOfMemoryError.

Initialization Parameters

If you wanted to make sure that if the web pages were updated at some point you got the new copies into the cache, you could add a timeout to the filter. Using the init-params you can set many of the same parameters that you can set for the cache tag:

The initialization parameters are

- Name This is the name of the cache. It defaults to the request URI for compatibility with *.extension URL patterns.

- Timeout This is the amount of time since the last cache update that the filter waits until trying to update the content in the cache again. The default unit is seconds but you can also specify it in units of ms (milliseconds), s (seconds), m (minutes), h (hours), or d (days).

- Scope The scope of the cache can be any one of request, session, application, or cluster. Request scope is useful for looping constructs in the page. The scope defaults to application. To use cluster scope you must set up the ClusterListener.

- Key This specifies that the cache is further specified not only by the name but also by values of various entries in scopes. These are specified just like the keys in the CacheTag although you do not have page scope available.

- Vars These are the variables calculated by the page that you want to cache. Typically this is used with servlets that pull information out of the database based on input parameters.

- Size This limits the number of different unique key values cached. It defaults to infinity.

The following example shows where the init-parameter is located in the filter code.

<filter>

<filter-name>HTML</filter-name>

<filter-class>weblogic.cache.filter.CacheFilter</filter-class>

<init-param>

Using WebLogic Services from an HTTP Servlet

When you write an HTTP servlet, you have access to many rich features of WebLogic Server, such as JNDI, EJB, JDBC, and JMS.

The following documents provide additional information about these features:

- Programming WebLogic EJB

- Programming WebLogic JDBC

- Programming WebLogic JNDI

- Programming WebLogic JMS

Accessing Databases

WebLogic Server supports the use of Java Database Connectivity (JDBC) from server-side Java classes, including servlets. JDBC allows you to execute SQL queries from a Java class and to process the results of those queries. For more information on JDBC and WebLogic Server, see Using WebLogic JDBC.

You can use JDBC in servlets as described in the following sections:

- Connecting to a Database Using a JDBC Connection Pool.

- Connecting to a Database Using a DataSource Object.

- Connecting Directly to a Database Using a JDBC Driver.

Connecting to a Database Using a JDBC Connection Pool

A connection pool is a named group of identical JDBC connections to a database that are created when the connection pool is registered, usually when starting WebLogic Server. Your servlets "borrow" a connection from the pool, use it, and then return it to the pool by closing it. This process is far more efficient than creating a new connection for every client each time the client needs to access the database. Another advantage is that you do not need to include details about the database in your servlet code.

When connecting to a JDBC connection pool, use one of the following multitier JDBC drivers:

- Pool driver, used for most server-side operations:

  - Driver URL: jdbc:weblogic:pool

  - Driver package name: weblogic.jdbc.pool.Driver

- JTS pool driver, used when database operations require transactional support.

  - Driver URL: jdbc:weblogic:jts

  - Driver package name: weblogic.jdbc.jts.Driver

Using a Connection Pool in a Servlet

The following example demonstrates how to use a database connection pool from a servlet.

1. Load the pool driver and cast it to java.sql.Driver. The full pathname of the driver is weblogic.jdbc.pool.Driver. For example:

   >     Driver myDriver = (Driver)
   >        Class.forName("weblogic.jdbc.pool.Driver").newInstance();

2. Create a connection using the URL for the driver, plus (optionally) the name of the registered connection pool. The URL of the pool driver is jdbc:weblogic:pool.

   You can identify the pool in either of two ways:

   - Specify the name of the connection pool in a java.util.Properties object using the key connectionPoolID. For example:

     >     Properties props = new Properties();
     >     props.put("connectionPoolID", "myConnectionPool");
     >     Connection conn =
     >       myDriver.connect("jdbc:weblogic:pool", props);

   - Add the name of the pool to the end of the URL. In this case you do not need a Properties object unless you are setting a username and password for using a connection from the pool. For example:

     >     Connection conn =
     >        myDriver.connect("jdbc:weblogic:pool:myConnectionPool", null);

   Note that the Driver.connect() method is used in these examples instead of the DriverManger.getConnection() method. Although you may use DriverManger.getConnection() to obtain a database connection, we recommend that you use Driver.connect() because this method is not synchronized and provides better performance.

   Note that the Connection returned by connect() is an instance of weblogic.jdbc.pool.Connection.

3. Call the close() method on the Connection object when you finish with your JDBC calls, so that the connection is properly returned to the pool. A good coding practice is to create the connection in a try block and then close the connection in a finally block, to make sure the connection is closed in all cases.

   >     conn.close();

Note: The finalize() method can also be used to close connections. However, using this method is not recommended because this method runs only after the Garbage Collector runs. In addition, if the finalize method runs at the same time when a WebLogic thread is cleaning up a connection, a deadlock may occur in certain driver classes.

Connecting to a Database Using a DataSource Object

A DataSource is a server-side object that references a connection pool. The connection pool registration defines the JDBC driver, database, login, and other parameters associated with a database connection. You create DataSource objects and connection pools through the Administration Console. Using a DataSource object is recommended when creating J2EE-compliant applications.

Using a DataSource in a Servlet

1. Register a connection pool using the Administration Console. For more information, see "Create a Connection Pool".

2. Register a DataSource object that points to the connection pool. For more information, see "JDBC DataSources".

3. Look up the DataSource object in the JNDI tree. For example:

   >     Context ctx = null;

   >     // Get a context for the JNDI look up
   >     ctx = new InitialContext(ht);

   >     // Look up the DataSource object
   >     javax.sql.DataSource ds
   >             = (javax.sql.DataSource) ctx.lookup ("myDataSource");

4. Use the DataSource to create a JDBC connection. For example:

   >     java.sql.Connection conn = ds.getConnection();

5. Use the connection to execute SQL statements. For example:

   >     Statement stmt = conn.createStatement();
   >     stmt.execute("select * from emp");

   >     . . .

Connecting Directly to a Database Using a JDBC Driver

Connecting directly to a database is the least efficient way of making a database connection because a new database connection must be established for each request. You can use any JDBC driver to connect to your database. BEA provides JDBC drivers for Oracle and Microsoft SQL Server. For more information, see Programming WebLogic JDBC.

Threading Issues in HTTP Servlets

When you design a servlet, you should consider how the servlet is invoked by WebLogic Server under high load. It is inevitable that more than one client will hit your servlet simultaneously. Therefore, write your servlet code to guard against sharing violations on shared resources or instance variables. The following tips can help you to design around this issue.

SingleThreadModel

An instance of a class that implements the SingleThreadModel is guaranteed not to be invoked by multiple threads simultaneously. Multiple instances of a SingleThreadModel servlet are used to service simultaneous requests, each running in a single thread.

To use the SingleThreadModel efficiently, WebLogic Server creates a pool of servlet instances for each servlet that implements SingleThreadModel. WebLogic Server creates the pool of servlet instances when the first request is made to the servlet and increments the number of servlet instances in the pool as needed.

The attribute SingleThreaded Servlet Pool Sizespecifies the initial number of servlet instances that are created when the servlet is first requested. Set this attribute to the average number of concurrent requests that you expect your SingleThreadModel servlets to handle.

When designing your servlet, consider how you use shared resources outside of the servlet class such as file and database access. Because multiple instances of identical servlets exist, and may use exactly the same resources, there are still synchronization and sharing issues that must be resolved, even if you do implement the SingleThreadModel.

Shared Resources

It is recommended that shared-resource issues be handled on an individual servlet basis. Consider the following guidelines:

- Wherever possible, avoid synchronization, because it causes subsequent servlet requests to bottleneck until the current thread completes.

- Define variables that are specific to each servlet request within the scope of the service methods. Local scope variables are stored on the stack and, therefore, are not shared by multiple threads running within the same method, which avoids the need to be synchronized.

- Access to external resources should be synchronized on a Class level, or encapsulated in a transaction.

Dispatching Requests to Another Resource

This section provides an overview of commonly used methods for dispatching requests from a servlet to another resource.

A servlet can pass on a request to another resource, such as a servlet, JSP, or HTML page. This process is referred to as request dispatching. When you dispatch requests, you use either the include() or forward() method of the RequestDispatcher interface. There are limitations regarding when output can be written to the response object using the forward() or include() methods. These limitations are also discussed in this section.

For a complete discussion of request dispatching, see section 8.1 of the Servlet 2.3 specification (see http://java.sun.com/products/
servlet/download.html#specs) from Sun Microsystems.

By using the RequestDispatcher, you can avoid sending an HTTP-redirect response back to the client. The RequestDispatcher passes the HTTP request to the requested resource.

To dispatch a request to a particular resource:

1. Get a reference to a ServletContext:

   >     ServletContext sc = getServletConfig().getServletContext();

2. Look up the RequestDispatcher object using one of the following methods:

   - RequestDispatcher rd = sc.getRequestDispatcher(String path);

     path should be relative to the root of the Web Application.

   - RequestDispatcher rd = sc.getNamedDispatcher(String name);

     Replace name with the name assigned to the servlet in a Web Application deployment descriptor with the <servlet-name> element. For details, see "Servlet element".

   - RequestDispatcher rd = ServletRequest.getRequestDispatcher(String path);

     This method returns a RequestDispatcher object and is similar to the ServletContext.getRequestDispatcher(String path) method except that it allows the path specified to be relative to the current servlet. If the path begins with a / character it is interpreted to be relative to the Web Application.

   You can obtain a RequestDispatcher for any HTTP resource within a Web Application, including HTTP Servlets, JSP pages, or plain HTML pages by requesting the appropriate URL for the resource in the getRequestDispatcher() method. Use the returned RequestDispatcher object to forward the request to another servlet.

3. Forward or include the request using the appropriate method:

   - rd.forward(request,response);

   - rd.include(request,response);

   These methods are discussed in the next two sections.

Forwarding a Request

Once you have the correct RequestDispatcher, your servlet forwards a request using the RequestDispatcher.forward() method, passing HTTPServletRequest and HTTPServletResponse as arguments. If you call this method when output has already been sent to the client an IllegalStateException is thrown. If the response buffer contains pending output that has not been committed, the buffer is reset.

The servlet must not attempt to write any previous output to the response. If the servlet retrieves the ServletOutputStream or the PrintWriter for the response before forwarding the request, an IllegalStateException is thrown.

All other output from the original servlet is ignored after the request has been forwarded.

If you are using any type of authentication, a forwarded request, by default, does not require the user to be re-authenticated. You can change this behavior to require authentication of a forwarded request by adding the <check-auth-on-forward/> element to the <container-descriptor> element of the WebLogic-specific deployment descriptor, weblogic.xml. For example:

>     <container-descriptor>
>         <check-auth-on-forward/>
>     </container-descriptor>

Note that the default behavior has changed with the release of the Servlet 2.3 specification, which states that authentication is not required for forwarded requests.

For information on editing the WebLogic-specific deployment descriptor, see Writing the WebLogic-Specific Deployment Descriptor.

Including a Request

Your servlet can include the output from another resource by using the RequestDispatcher.include() method, and passing HTTPServletRequest and HTTPServletResponse as arguments. When you include output from another resource, the included resource has access to the request object.

The included resource can write data back to the ServletOutputStream or Writer objects of the response object and then can either add data to the response buffer or call the flush() method on the response object. Any attempt to set the response status code or to set any HTTP header information from the included servlet response is ignored.

In effect, you can use the include() method to mimic a "server-side-include" of another HTTP resource from your servlet code.