Skip Headers

Oracle9iAS Containers for J2EE Servlet Developer's Guide
Release 2 (9.0.3)

Part Number A97680-01
Go To Core Documentation
Core
Go To Platform Documentation
Platform
Go To Table Of Contents
Contents
Go To Index
Index

Go to previous page Go to next page

2
Servlet Development

This chapter provides basic information for developing servlets for OC4J and the Oracle9i Application Server, covering the following topics:

Servlet Development Basics and Key Considerations

Most HTTP servlets follow a standard form. They are written as public classes that extend the HttpServlet class. A servlet overrides the init() and destroy() methods when code is required for initialization work at the time the servlet is loaded by the container, or for finalization work when the container shuts the servlet down. Most servlets override either the doGet() method or the doPost() method of HttpServlet, to handle HTTP GET or POST requests. These two methods take request and response objects as parameters.

This chapter provides sample servlets that are more advanced than the HelloWorldServlet in "A First Servlet Example". For simplicity, you can test each of these servlets using the OC4J standalone default Web application. To do this, save the Java source files in the /WEB-INF/classes directory of the default Web application.

To test some of the servlets, you might have to make changes to the web.xml file in the default Web application /WEB-INF directory, as directed. When you change and save the web.xml file, OC4J restarts and picks up the changes to the default Web application.

This section covers features and issues to consider before developing your applications. In all, the following topics are covered:

Code Template

Here is a code template for servlet development:

public class myServlet extends HttpServlet {
  
  public void init(ServletConfig config) {
  }

  public void destroy() {
  }

  public void doGet(HttpServletRequest request, HttpServletResponse)
                    throws ServletException, IOException {
  }

  public void doPost(HttpServletRequest request, HttpServletResponse)
                    throws ServletException, IOException {
  }

  public String getServletInfo() {
    return "Some information about the servlet.";
  }

Overriding the init(), destroy(), and getServletInfo() methods is optional. The simplest servlet just overrides either doGet() or doPost().

The reason for overriding the init() method would be to perform special actions that are required only once in the servlet lifetime, such as the following:

Servlet Loading and Lifecycle

Servlets have a predictable and manageable lifecycle:

Also see the next section, "Servlet Preloading".

Servlet Preloading

Typically, the servlet container instantiates and loads a servlet class when it is first requested. However, you can arrange the preloading of servlets through settings in the Web site XML file (such as default-web-site.xml or http-web-site.xml) and the web.xml file. Preloaded servlets are loaded and initialized when the OC4J server starts up, or when the Web module is deployed or redeployed.

Preloading requires the following steps:

  1. Specify the attribute setting load-on-startup="true" in the <web-app> subelement of the <web-site> element of the Web site XML file. See "The default-web-site.xml, http-web-site.xml, and Other Web Site XML Files" for information about Web site XML files.

  2. For any servlet you want to preload, there must be a <load-on-startup> subelement under the <servlet> element in the web.xml file for the Web module.

Table 2-1 explains the behavior of the <load-on-startup> element in web.xml.

Table 2-1 File web.xml <load-on-startup> Behavior
Value Range Behavior

Less than zero (<0)

For example:

<load-on-startup>-1</load-on-startup)

Servlet is not preloaded.

Greater than or equal to zero (>=0)

For example:

<load-on-startup>1</load-on-startup>

Servlet is preloaded. The order of its loading, with respect to other preloaded servlets in the same Web application, is according to the load-on-startup value, lowest number first. (For example, 0 is loaded before 1, which is loaded before 2.)

Empty element

For example:

<load-on-startup/>

The behavior is as if the load-on-startup value is Integer.MAX_VALUE, ensuring that the servlet is loaded after any servlets with load-on-startup values greater than or equal to zero.

Servlet Class Loading

Regarding class loading for servlets, there are two important considerations in OC4J:

Loading WAR File Classes Before System Classes in OC4J

The servlet 2.3 specification recommends, but does not require, loading "local classes"--classes in the WAR file--before system classes. By default, the OC4J servlet container does not load local classes first, but this is configurable through the <web-app-class-loader> element in global-web-application.xml or orion-web.xml. This element has two attributes, listed here.

Also see "Element Descriptions for global-web-application.xml and orion-web.xml".

Sharing Cached Java Objects Across OC4J Servlets

In order to take advantage of the distributed functionality of the Oracle9i Application Server Java Object Cache, or to share a cached object between servlets, some minor modification to an application deployment is necessary. Any user-defined objects that will be shared between servlets or distributed between JVMs must be loaded by the system class loader; however, by default, objects loaded by a servlet are loaded by the context class loader. Objects loaded by the context class loader are visible only to the servlets within the servlet context corresponding to that class loader. The object definition would not be available to other servlets or to the cache in another JVM. If an object is loaded by the system class loader, however, the object definition will be available to other servlets and to the cache on other JVMs.

With OC4J, the system classpath is derived from the manifest of the oc4j.jar file and any associated .jar files, including cache.jar. The classpath in the environment is ignored. To include a cached object in the classpath for OC4J, the .class file should either be copied to the following directory:

[Oracle_Home]/javacache/sharedobjects/classes

or it should be added to the following .jar file:

[Oracle_Home]/javacache/cachedobjects/share.jar

Both the classes directory and the share.jar file are included in the manifest for cache.jar, and are therefore included in the system classpath.

For information about the Oracle9i Application Server Java Object Cache, see the Oracle9iAS Containers for J2EE Services Guide.

Servlet Information Exchange

A servlet typically receives information from one or more sources, including the following:

The servlet adds information to the response object, and the container sends the response back to the client.

Servlet Threading

If there is an additional servlet request while a servlet is already running, servlet container behavior depends on whether the servlet uses a single-thread model or a multiple-thread model. In a single-thread case, the servlet container prevents multiple simultaneous service() calls from being dispatched to a single servlet instance--it may spawn multiple separate servlet instances instead. In a multiple-thread model, the container can make multiple simultaneous service() calls to a single servlet instance, using a separate thread for each call.

When a servlet can be invoked from more than one thread, you must ensure that the servlet code is thread-safe. One option, though not generally advisable, is to synchronize the servlet (specifically, the service() method). This can significantly reduce performance.

As an alternative, the servlet specification provides that a servlet can implement the javax.servlet.SingleThreadModel interface to guarantee synchronized access to the whole servlet. For stateful servlets, it is advisable to use this single-thread model.

Servlet Security Considerations

There are several considerations regarding the security of your Web application running in the OC4J servlet container:

OC4J Default Web Application and Key Directories

OC4J is installed with a default configuration that includes a default Web site and a default application. The default application includes a default Web application. Therefore, you can start OC4J immediately without any additional configuration.

For simplicity, this chapter assumes you are using an OC4J standalone environment and deploying to the default Web application. An OC4J standalone environment includes the following key directories.

In addition, there are the following key directories of the OC4J standalone default Web application:

Oracle9i JDeveloper Support for Servlet Development

Visual Java programming tools now typically support servlet coding. In particular, Oracle9i JDeveloper supports servlet development and includes the following features:

Servlet Invocation

A servlet or JSP page is invoked by the container when a request for the servlet arrives from a client. The client request might come from a Web browser or a Java client application, or from another servlet in the application using the request forwarding mechanism, or from a remote object on a server.

A servlet is requested through its URL mapping. The URL mapping for a servlet consists of two parts: the context path and the servlet path. The context path is that part of the URL from the first forward slash after the host name or port number, up to the servlet path. The servlet path continues from the slash at the end of the context path (if there is a context path) to the end of the URL string, or until a '?' or ';' that delimits the servlet path from the additional material such as query strings or rewritten parts of the URI. In a typical deployment scenario, the context path and servlet path are determined through settings in a standard web.xml file.

The remainder of this section covers the following topics, including some special OC4J features for invoking a servlet by class name in a development scenario:

Servlet Invocation by Class Name During OC4J Development

For a development or testing scenario in OC4J, there is a mechanism for invoking a servlet by class name. This might simplify the URL for invocation. The presumption here is that you would use this in an OC4J standalone environment while developing your application.

The servlet-webdir attribute in the <orion-web-app> element of the global-web-application.xml file or orion-web.xml file defines a special URL component. Anything following this URL component is assumed to be a servlet class name, including applicable package information, within the appropriate servlet context. By default in OC4J, this setting is "/servlet".

The following URL shows how to invoke a servlet called SessionServlet, with explanations following. In this example, assume SessionServlet is in package foo.bar, and executes in the OC4J default Web application. Also assume a context path of "/" (the default for the default Web application in OC4J standalone).

http://<host><:port>/servlet/foo.bar.SessionServlet

http://

The network protocol. Other protocols are ormi, ftp, https, and so on.

<host>

The network name of the server that the Web application is running on. If the Web client is on the same system as the application server, you can use localhost. Otherwise, use the host name (as defined in /etc/hosts on a UNIX system, for example).

<:port>

The port that the Web server listens on. (If you do not specify a port, most browsers assume port 80.) For AJP protocol through Oracle HTTP Server, the server port is defined in the port attribute of the <web-site> element in default-web-site.xml (or other OC4J Web site XML file, as appropriate). For HTTP protocol in OC4J standalone, the port is according to the port attribute of the <web-site> element in http-web-site.xml.

/servlet

This is according to the default servlet-webdir setting.

/foo.bar.SessionServlet

Because there is a servlet-webdir setting, this portion of the URL is simply the servlet package and class name.

This mechanism applies to any servlet context, however, and not just for the default Web application. If the context path is foo, for example, the URL to invoke by class name would be as follows:

http://<host><:port>/foo/servlet/foo.bar.SessionServlet


Important:

Allowing the invocation of servlets by class name presents a significant security risk; OC4J should not be configured to operate in this mode in a production environment. See "Servlet Security Considerations" for information.



Note:

See the Oracle9iAS Containers for J2EE User's Guide for information about defined ports and what listeners they are mapped to, and for information about how to alter these settings.


Servlet Invocation in an Oracle9iAS Production Environment

In a production environment, using the servlet-webdir attribute in global-web-application.xml or orion-web.xml is inadvisable for security reasons. Instead, standard servlet settings and mappings in the application web.xml file specify the context path and servlet path.

In web.xml, the <servlet-name> subelement of the <servlet> element defines a name for the servlet and relates it to a servlet class. The <servlet-mapping> subelement relates servlet names to path mappings. The servlet name as well as the mapping names are arbitrary--it is not necessary for the class that is invoked to have the same base name, or even a similar base name, to either the <servlet-name> or any of the <servlet-mapping> settings.

In an Oracle9iAS environment, the context path is "/j2ee" to use AJP protocol through the Oracle HTTP Server for an application that is deployed to the OC4J default Web application. Here is an example:

http://<host><:port>/j2ee/MyServlet

Use port 7777 for access through the Oracle HTTP Server with Oracle9iAS Web Cache enabled.

If you are not using the default Web application, specify the context path while deploying the application, in the Enterprise Manager page where you are prompted for "URL binding". See the Oracle9iAS Containers for J2EE User's Guide for more information. That document also has information about OC4J port settings and other default settings. For general information about Enterprise Manager, see the Oracle Enterprise Manager Administrator's Guide.


Notes:

  • This discussion assumes the Web application is bound to a Web site that uses AJP protocol, according to settings in the default-web-site.xml file.

  • For production applications, Oracle recommends that you always use the Oracle HTTP Server, as discussed in this section. See the Oracle HTTP Server Administration Guide for general information.


There is also a relevant element in the default-web-site.xml file (or other Web site XML file). The <frontend> subelement of the <web-site> element specifies a perceived front-end host and port of the Web site as seen by HTTP clients. When the site is behind something like a load balancer or firewall, the <frontend> specification is necessary to provide appropriate information to the Web application for functionality such as URL rewriting. Attributes are host, for the name of the front-end server (such as "www.acme.com"), and port, for the port number of the front-end server (such as "80"). Using this front-end information, the back-end server that is actually running the application knows to refer to www.acme.com instead of to itself in any URL rewriting. This way, subsequent requests properly come in through the front-end again, instead of trying to access the back-end directly.

Servlet Invocation in an OC4J Standalone Environment

The information about servlet configuration through web.xml in the previous section, "Servlet Invocation in an Oracle9iAS Production Environment", also applies in an OC4J standalone environment if you are not invoking by class name.

When testing with OC4J standalone, you can use port 8888 to access OC4J through its own Web listener. The default context path is "/". Here is an example:

http://<host>:8888/MyServlet


Note:

The OC4J standalone Web site uses HTTP protocol without going through the Oracle HTTP Server and AJP, and is configured according to settings in the http-web-site.xml file . These settings include the context path, for example (according to the root attribute of the <web-app> subelement under the <web-site> element).


Servlet Sessions

Servlet sessions were introduced in "Introduction to Servlet Sessions". This section provides details and examples, covering the following topics:

Session Tracking

This section provides an overview of servlet session tracking and features, then describes the OC4J implementation.

Overview of Session Tracking

The HTTP protocol is stateless by design. This is fine for stateless servlets that simply take a request, do a few computations, output some results, and then in effect go away. But many, if not most, server-side applications must keep some state information and maintain a dialogue with the client. The most common example of this is a shopping cart application. A client accesses the server several times from the same browser, and visits several Web pages. The client decides to buy some of the items offered for sale at the Web site, and clicks the BUY ITEM buttons. If each transaction were being served by a stateless server-side object, and the client provided no identification on each request, it would be impossible to maintain a filled shopping cart over several HTTP requests from the client. In this case, there would be no way to relate a client to a server session, so even writing stateless transaction data to persistent storage would not be a solution.

When a servlet creates an HTTP session object (through the request object getSession() method), the client interaction is considered to be stateful.

Session tracking involves identifying user sessions by ID numbers and tying requests to their session through use of the ID number. The typical mechanisms for this are cookies or URL rewriting.


Note:

Do not use HttpSession objects to store persistent information that must last beyond the normal duration of a session. You can store persistent data in a database if you need the protection, transactional safety, and backup that a database offers. Alternatively, you can save persistent information on a file system or in a remote object.


Cookies

A number of approaches have been used in attempting to add a measure of statefulness to the HTTP protocol. The most widely accepted is the use of cookies, used to transmit an identifier between server and client, in conjunction with stateful servlets that can maintain session objects. Session objects are simply dictionaries that store values (Java objects) together with their associated keys (Java strings).

Cookie usage is as follows:

  1. With the first response from a stateful servlet after a session is created, the server (container) sends a cookie with a session identifier back to the client, often along with a small amount of other useful information (all less than 4 KB). The container sends the cookie, named JSESSIONID, in the HTTP response header.

  2. Upon each subsequent request from the same Web client session, if the client supports cookies it sends the cookie back to the server as part of the request, and the cookie value is used by the server to look up session state information to pass to the servlet.

  3. With subsequent responses, the container sends the updated cookie back to the client.

The servlet code is not required to do anything to send a cookie; this is handled by the container. Sending cookies back to the server is handled automatically by the Web browser, unless the end-user disables cookies.

The container uses the cookie for session maintenance. A servlet can retrieve cookies using the getCookies() method of the HttpServletRequest object, and can examine cookie attributes using the accessor methods of the javax.servlet.http.Cookie objects.

URL Rewriting

An alternative to using cookies is URL rewriting, through the encodeURL() method of the response object. This is where the session ID is encoded into the URL path of a request. See "Session Servlet Example" for an example of URL rewriting.

The name of the path parameter is jsessionid, as in the following example:

http://<host><:port>/myapp/index.html?jsessionid=6789

Similarly to the functionality of cookies, the value of the rewritten URL is used by the server to look up session state information to pass to the servlet.

Although cookies are typically enabled, the only way for you to ensure session tracking is to use encodeURL() in your servlets, or encodeRedirectURL() for redirects.


Note:

In the OC4J implementation, calls to the encodeURL() and encodeRedirectURL() methods will result in no action if cookies are enabled.


Other Session Tracking Methods

Other techniques have been used in the past to relate client and server sessions. These include server hidden form fields and user authentication mechanisms to store additional information. Oracle does not recommend these techniques in OC4J applications, because they have many drawbacks, including performance penalties and loss of confidentiality.

Session Tracking in OC4J

For session-tracking in OC4J, the servlet container will first attempt to accomplish tracking through cookies. If cookies are disabled, session tracking can be maintained only by using the encodeURL() method of the response object, or the encodeRedirectURL() method for redirects. You must include the encodeURL() or encodeRedirectURL() calls in your servlet if cookies might be disabled.

The use of session cookies is disabled by the following setting in the global-web-application.xml or orion-web.xml file:

<session-tracking cookies="disabled" ... >
   ...
</session-tracking>

Cookies are enabled by default.


Notes:

  • OC4J does not support auto-encoding, where session IDs are automatically encoded into the URL by the servlet container. This is a non-standard and expensive process.

  • An encodeURL() or encodeRedirectURL() call will not encode the session ID into the URL if the cookie mechanism is found to be working properly.

  • The encodeURL() method replaces the servlet 2.0 encodeUrl() method (note capitalization), which is deprecated.


Session Cancellation

HttpSession objects persist for the duration of the server-side session. A session is either terminated explicitly by the servlet, or it "times out" after a certain period and is cancelled by the container.

Cancellation Through a Timeout

The default session timeout for the OC4J server is 20 minutes. You can change this for a specific application by setting the <session-timeout> subelement under the <session-config> element of web.xml. For example, to reduce the session timeout to five minutes, add the following lines to the application web.xml:

<session-config>
  <session-timeout>5</session-timeout>
</session-config>

The <session-timeout> element takes integer values. According to the servlet 2.3 specification, a value of 0 (zero) or less specifies the default behavior that a session never times out. For example:

<session-config>
  <session-timeout>-1</session-timeout>
</session-config>

Cancellation by the Servlet

A servlet explicitly cancels a session by invoking the invalidate() method on the session object. You must obtain a new session object by invoking the getSession() method of the HttpServletRequest object.

Session Servlet Example

The SessionServlet code below implements a servlet that establishes an HttpSession object and prints some interesting data held by the request and session objects.

SessionServlet Code

import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Date;

public class SessionServlet extends HttpServlet { 

  public void doGet (HttpServletRequest req, HttpServletResponse res)
        throws ServletException, IOException {

    // Get the session object. Create a new one if it doesn't exist.
    HttpSession session = req.getSession(true);

    res.setContentType("text/html");
    PrintWriter out = res.getWriter();

    out.println("<head><title> " + "SessionServlet Output " +
                "</title></head><body>");
    out.println("<h1> SessionServlet Output </h1>");

    // Set up a session hit counter. "sessionservlet.counter" is just the
    // conventional way to create a key for the value to be stored in the
    // session object "dictionary".
    Integer ival = 
      (Integer) session.getAttribute("sessionservlet.counter");
    if (ival == null) {
      ival = new Integer(1);
    }
    else {
      ival = new Integer(ival.intValue() + 1);
    }

    // Save the counter value.
    session.setAttribute("sessionservlet.counter", ival);

    // Report the counter value. 
    out.println(" You have hit this page <b>" + 
                ival + "</b> times.<p>");

    // This statement provides a target that the user can click on
    // to activate URL rewriting. It is not done by default.
    out.println("Click <a href=" + 
                res.encodeURL(HttpUtils.getRequestURL(req).toString()) + 
                ">here</a>");
    out.println(" to ensure that session tracking is working even " +
                "if cookies aren't supported.<br>");
    out.println("Note that by default URL rewriting is not enabled" +
                " due to its large overhead.");
    // Report data from request.
    out.println("<h3>Request and Session Data</h3>");
    out.println("Session ID in Request: " +
                req.getRequestedSessionId());
    out.println("<br>Session ID in Request is from a Cookie: " +
                req.isRequestedSessionIdFromCookie());
    out.println("<br>Session ID in Request is from the URL: " +
                req.isRequestedSessionIdFromURL());
    out.println("<br>Valid Session ID: " +
                req.isRequestedSessionIdValid());

    // Report data from the session object.
    out.println("<h3>Session Data</h3>");
    out.println("New Session: " + session.isNew());
    out.println("<br> Session ID: " + session.getId());
    out.println("<br> Creation Time: " + new Date(session.getCreationTime()));
    out.println("<br>Last Accessed Time: " +
                new Date(session.getLastAccessedTime()));

    out.println("</body>");
    out.close();
  }

  public String getServletInfo() {
    return "A simple session servlet";
  }
}

Deploying and Testing

Enter the preceding code into a text editor, and save it in the file SessionServlet.java in the OC4J default Web application /WEB-INF/classes directory. If you use the setting development="true" in the <orion-web-app> element of the global-web-application.xml file, the servlet can be recompiled and redeployed automatically the next time it is invoked. You may also have to set the source-directory attribute appropriately. See "Element Descriptions for global-web-application.xml and orion-web.xml" for more information about these attributes.

Figure 2-1 shows the output of this servlet when it is invoked the second time in a session by a Web browser that has cookies enabled. Experiment with different Web browser settings--for example, by disabling cookies--then select the HREF that causes URL rewriting.

Figure 2-1 Session Servlet Display

Text description of sessserv.gif follows.

Text description of the illustration sessserv.gif

Session Replication

The session object of a stateful servlet can be replicated to other OC4J servers in a load-balanced cluster island. If the server handling a request to a servlet should fail, the request can "failover" to another JVM on another server in the cluster island. The session state will still be available. The Web application must be marked as distributable in the web.xml file, by use of the standard <distributable> element.

Objects that are stored by a servlet in the HttpSession object are replicated, and must be serializable or remoteable for replication to work properly.

Note that a slight but noticeable delay occurs when an application is replicated to other servers in a load-balanced cluster island. It is, therefore, possible that the servlet could have been replicated by the time a failure occurred in the original server, but that the session information had not yet been replicated.

Use of JDBC in Servlets

A servlet can access a database using a JDBC driver. The recommended way to use JDBC is by using an OC4J data source to get the database connection. See Oracle9iAS Containers for J2EE Services Guide for information about OC4J data sources. For more information about JDBC, see the Oracle9i JDBC Developer's Guide and Reference.

Database Query Servlet

Part of the power of servlets comes from their ability to retrieve data from a database. A servlet can generate dynamic HTML by getting information from a database and sending it back to the client. A servlet can also update a database, based on information passed to it in the HTTP request.

The example in this section shows a servlet that gets some information from the user through an HTML form and passes the information to a servlet. The servlet completes and executes a SQL statement, querying the sample HR schema to get information based on the request data.

A servlet can get information from the client in many ways. This example reads a query string from the HTTP request.

HTML Form

The Web browser accesses a form in a page that is served through the Web listener. First, copy the following text into a file, naming the file EmpInfo.html.

<HTML>
<HEAD>
<TITLE>Get Employee Information</TITLE>
</HEAD>

<BODY>
<FORM METHOD=GET ACTION="/servlet/GetEmpInfo">
The query is<br>
SELECT LAST_NAME, EMPLOYEE_ID FROM EMPLOYEES WHERE LAST NAME LIKE ?.<p>

Enter the WHERE clause ? parameter (use % for wildcards).<br>
Example: 'S%':<br>
<INPUT TYPE=TEXT NAME="queryVal">
<P>
<INPUT TYPE=SUBMIT VALUE="Send Info">
</FORM>

</BODY>
</HTML>

Then save this file in the root directory of the OC4J default Web application.

Servlet Code: GetEmpInfo

The servlet that the preceding HTML page calls takes the input from a query string. The input is the completion of the WHERE clause in the SELECT statement. The servlet then appends this input to complete the database query. Most of the code in this servlet consists of the JDBC statements required to connect to the data server and retrieve the query rows.

This servlet makes use of the init() method to do a one-time lookup of a data source, using JNDI. The data source lookup assumes a data source such as the following has been defined in the data-sources.xml file in the OC4J configuration files directory:

<data-source
   class="com.evermind.sql.DriverManagerDataSource"
   name="OracleDS"
   location="jdbc/OracleCoreDS"
   xa-location="jdbc/xa/OracleXADS"
   ejb-location="jdbc/OracleDS"
   connection-driver="oracle.jdbc.driver.OracleDriver"
   username="scott"
   password="tiger"
   url="jdbc:oracle:thin:@localhost:5521:oracle"
   inactivity-timeout="30" 
/>

In Oracle9iAS release 2, it is advisable to use only the ejb-location JNDI name in the JNDI lookup for a data source. See the Oracle9iAS Containers for J2EE Services Guide for more information about data sources.

Here is the servlet code:

import java.io.*;
import java.sql.*;
import javax.servlet.*;
import javax.servlet.http.*;
// These packages are needed for the JNDI lookup.
import javax.naming.*;
// These packages support SQL operations and Oracle JDBC drivers.
import javax.sql.*;
import oracle.jdbc.*;

public class GetEmpInfo extends HttpServlet {

  DataSource ds = null;

  public void init() throws ServletException {
    try {
      InitialContext ic = new InitialContext();
      ds = (DataSource) ic.lookup("java:comp/env/jdbc/OracleDS");
    }
    catch (NamingException ne) {
      throw new ServletException(ne);
    }
  }


  public void doGet (HttpServletRequest req, HttpServletResponse resp)
                  throws ServletException, IOException {

    String queryVal = req.getParameter("queryVal");
    String query =
      "select last_name, employee_id from employees " +
      "where last_name like " + queryVal;

    resp.setContentType("text/html");

    PrintWriter out = resp.getWriter();
    out.println("<html>");
    out.println("<head><title>GetEmpInfo</title></head>");
    out.println("<body>");

    try {
      Connection conn = ds.getConnection();
      Statement stmt = conn.createStatement();
      ResultSet rs = stmt.executeQuery(query);

      out.println("<table border=1 width=50%>");
      out.println("<tr><th width=75%>Last Name</th>" + 
                  "<th width=25%>Employee ID</th></tr>");

      for (int count = 0; ; count++ ) {
        if (rs.next()) {
          out.println("<tr><td>" + rs.getString(1) + "</td><td>" +
             rs.getInt(2) + "</td></tr>");
        }
        else {
          out.println("</table><h3>" + count + " rows retrieved</h3>");
          break;
        }
      }
      conn.close();
      rs.close();
      stmt.close();
    }
    catch (SQLException se) {
      se.printStackTrace(out);
    }

    out.println("</body></html>");
  }

  public void destroy() {
  }
}

Deployment and Testing of the Database Query Servlet

To deploy this example, save the HTML file in the document root of the OC4J default Web application, and save the Java servlet in the /WEB-INF/classes directory of the default Web application. The GetEmpInfo.java file is automatically compiled when the servlet is invoked by the form.

To test the example directly through the OC4J listener, such as in OC4J standalone, invoke the EmpInfo.html page from a Web browser as follows:

http://<host>:8888/EmpInfo.html

This assumes "/" is the context path of the OC4J standalone default Web application.

Complete the form and click Submit Query.


Note:

For general information about invoking servlets in OC4J, see "Servlet Invocation".


When you invoke EmpInfo.html, you will see a browser window that looks something like Figure 2-2.

Figure 2-2 Employee Information Query

Text description of empinfo.gif follows.

Text description of the illustration empinfo.gif

Entering 'S%' in the form and pressing Submit Query calls the GetEmpInfo servlet, and the results look something like Figure 2-3.

Figure 2-3 Employee Information Results

Text description of getemp.gif follows.

Text description of the illustration getemp.gif

EJB Calls from Servlets

A servlet or a JSP page can call an EJB to perform additional processing. A typical application design often uses servlets as a front-end to do the initial processing of client requests, with EJBs being called to perform the business logic that accesses or updates a database. Container-managed-persistence (CMP) entity beans, in particular, are well-suited for such tasks.

There are three general scenarios for servlet-EJB interactions:

Local EJB Lookup within the Same Application

This section presents an example of a single servlet, HelloServlet, that calls a single EJB, HelloBean, within the same application using a local lookup.

Here are the key steps of the servlet code:

  1. Imports the EJB package for access to the bean home and remote interfaces.

  2. Prints a message from the servlet.

  3. Creates an output string, with an error default.

  4. Uses JNDI to look up the EJB home interface.

  5. Creates the EJB remote object from the home.

  6. Invokes the helloWorld() method on the remote object, which returns a String object.

  7. Prints the message from the EJB.

Servlet Code: HelloServlet

package myServlet;

// Step 1: Import the EJB package.
import myEjb.*;
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.naming.*;                                // for JNDI

public class HelloServlet extends HttpServlet {
 
  public void doGet (HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {

    response.setContentType("text/html");
    PrintWriter out = response.getWriter();

    out.println("<html><head><title>Hello from Servlet</title></head>");
    // Step 2: Print a message from the servlet.
    out.println("<body><h1>Hello from hello servlet!</h1></body>");

    // Step 3: Create an output string, with an error default.
    String s = "If you see this message, the ejb was not invoked properly!!";
    // Step 4: Use JNDI to look up the EJB home interface.
    try {
      HelloHome hh = (HelloHome) 
                  (new InitialContext()).lookup("java:comp/env/ejb/HelloBean");
    
      // Step 5: Create the EJB remote IF.
      HelloRemote hr = hh.create();
      // Step 6: Invoke the helloWorld() method on the remote object.
      s = hr.helloWorld();
    } catch (Exception e) {
      e.printStackTrace(out);
    }
    // Step 7: Print the message from the EJB.
    out.println("<br>" + s);
    out.println("</html>");
  }
}

Figure 2-4 shows the output to a Web browser when you invoke the servlet. The output from the servlet is printed in H1 format at the top, then the output from the EJB is printed in text format below that.

Figure 2-4 Output from HelloServlet

Text description of dblhello.gif follows.

Text description of the illustration dblhello.gif

EJB Code: HelloBean Stateful Session Bean

The EJB, as shown here, implements a single method--helloWorld()--that returns a greeting to the caller. The home and remote EJB interface code is also shown below.

package myEjb;

import java.rmi.RemoteException;
import javax.ejb.*;

public class HelloBean implements SessionBean
{
  public String helloWorld () throws RemoteException {
    return "Hello from myEjb.HelloBean";
  }

  public void ejbCreate () throws RemoteException, CreateException {}
  public void ejbRemove () {}
  public void setSessionContext (SessionContext ctx) {}
  public void ejbActivate () {}
  public void ejbPassivate () {}
}

EJB Interface Code: Home and Remote Interfaces

Here is the code for the home interface:

package myEjb;

import java.rmi.RemoteException;
import javax.ejb.EJBHome;
import javax.ejb.CreateException;

public interface HelloHome extends EJBHome
{
  public HelloRemote create () throws RemoteException, CreateException;
}

Here is the code for the remote interface:

package myEjb;

import java.rmi.RemoteException;
import javax.ejb.EJBObject;

public interface HelloRemote extends EJBObject
{
  public String helloWorld () throws RemoteException;
}

Deployment of the Servlet-EJB Application

This section discusses the deployment steps for the Servlet-EJB sample application, including the Web archive, EJB archive, and application-level descriptor.

See Chapter 3, "Deployment and Configuration", for general information about deployment to OC4J.

Web Archive

To deploy this application, an EJB deployment descriptor (ejb-jar.xml) and a Web deployment descriptor (web.xml) are required. The contents of web.xml for this example are as follows:

<?xml version="1.0"?>
<!DOCTYPE WEB-APP PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 
2.2//EN" "http://java.sun.com/j2ee/dtds/web-app_2_2.dtd">

<web-app>
  <display-name>HelloServlet</display-name>
  <description> HelloServlet </description>
  <servlet>
    <servlet-name> ServletCallingEjb </servlet-name>
    <servlet-class> myServlet.HelloServlet </servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name> ServletCallingEjb </servlet-name>
    <url-pattern> /doubleHello </url-pattern>
  </servlet-mapping>
  <welcome-file-list>
    <welcome-file> index.html </welcome-file>
  </welcome-file-list>
  <ejb-ref>
    <ejb-ref-name>ejb/HelloBean</ejb-ref-name>
    <ejb-ref-type>Session</ejb-ref-type>
    <home>myEjb.HelloHome</home>
    <remote>myEjb.HelloRemote</remote>
  </ejb-ref>
</web-app>

Next, create the directory structure that is required for Web application deployment, and move the Web deployment descriptor (web.xml) and the compiled servlet class file into the structure. The web.xml file must be in a /WEB-INF directory, and the servlet class files (in their respective packages, as applicable) must be under the /WEB-INF/classes directory. Once you create the directory structure and populate the directories, create a WAR file to contain the files. From the Web root directory, create the WAR file as follows:

% jar cvf myapp-web.war *

When created, the WAR file should look like this:

% jar -tf myapp-web.war 
META-INF/
META-INF/MANIFEST.MF
WEB-INF/
WEB-INF/classes/
WEB-INF/classes/myServlet/
WEB-INF/classes/myServlet/HelloServlet.class
WEB-INF/web.xml

EJB Archive

The contents of ejb-jar.xml are as follows. Note that the <ejb-name> value here corresponds to the <ejb-ref-name> value in the web.xml file above.

<?xml version="1.0"?>
<!DOCTYPE ejb-jar PUBLIC "-//Sun Microsystems, Inc.//DTD Enterprise JavaBeans 
1.12//EN" "http://java.sun.com/j2ee/dtds/ejb-jar_1_1.dtd">
<ejb-jar>
  <enterprise-beans>
    <session>
      <description>Hello Bean</description>
      <ejb-name>ejb/HelloBean</ejb-name>
      <home>myEjb.HelloHome</home>
      <remote>myEjb.HelloRemote</remote>
      <ejb-class>myEjb.HelloBean</ejb-class>
      <session-type>Stateful</session-type>
      <transaction-type>Container</transaction-type>
    </session>
  </enterprise-beans>
  <assembly-descriptor>
  </assembly-descriptor>
</ejb-jar>

Create a JAR file to hold the EJB components. The JAR file should look like this:

% jar tf myapp-ejb.jar 
META-INF/
META-INF/MANIFEST.MF
myEjb/
META-INF/ejb-jar.xml
myEjb/HelloBean.class
myEjb/HelloHome.class
myEjb/HelloRemote.class

Application-Level Descriptor

To deploy the application, create an application deployment descriptor--application.xml. This file describes the modules in the application.

<?xml version="1.0"?>
<!DOCTYPE application PUBLIC "-//Sun Microsystems, Inc.//DTD J2EE Application 
1.2//EN" "http://java.sun.com/j2ee/dtds/application_1_2.dtd">

<application>
  <display-name>Servlet_calling_ejb_example</display-name>
  <module>
    <web>
      <web-uri>myapp-web.war</web-uri>
      <context-root>/foo</context-root>
    </web>
  </module>
  <module>
    <ejb>myapp-ejb.jar</ejb>
  </module>
</application>

However, presuming you are developing in an OC4J standalone environment (where OC4J runs apart from Oracle9iAS), the <context-root> element is ignored. You must specify the context root through appropriate entries in http-web-site.xml or the relevant Web site XML file.

(In an Oracle9iAS production environment, for a new context root--such as /foo in this example--to route properly to OC4J through Oracle HTTP Server, there must be an appropriate Oc4jMount command in the mod_oc4j.conf file. Assuming you use Enterprise Manager to deploy the application, this is handled automatically. This discussion assumes the Web application is bound to a Web site that uses AJP protocol, according to settings in default-web-site.xml or the relevant Web site XML file.)

Finally, create an EAR file to hold the application components. The EAR file should look like this:

% jar tf myapp.ear
META-INF/
META-INF/MANIFEST.MF
myapp-ejb.jar
myapp-web.war
META-INF/application.xml

Deployment Configuration

To perform the application deployment for testing purposes, add the following entry to the server.xml file in the OC4J configuration files directory (specifying the appropriate path information).

   <application
      name="myapp" 
      path="<your_path_to>/lib/myapp.ear"
      auto-start="true"
   />

In an OC4J standalone development environment, you can accomplish this through the admin.jar tool. That tool is documented in the Oracle9iAS Containers for J2EE User's Guide, OC4J standalone version.

Then bind the Web module to a Web site. You will need the following entry in the Web site XML file in the OC4J configuration files directory:

   <web-app 
      application="myapp"
      name="myapp-web"
      root="/myapp"
   />


Notes:


Remote EJB Lookup within the Same Application

To perform a remote EJB lookup in OC4J, enable the EJB remote flag. This is an attribute in the <ejb-module> subelement of an <orion-application> element in the orion-application.xml file for the application to which the calling servlet belongs. (The default setting is remote="false".) Here is an example of enabling this flag:

<orion-application ... >
   <ejb-module remote="true" ... />

   ...

</orion-application>

No changes are necessary to the servlet code. Recall the local EJB lookup from "Servlet Code: HelloServlet":

HelloHome hh = (HelloHome) 
               (new InitialContext()).lookup("java:comp/env/ejb/HelloBean");

Given a remote="true" setting, this code would result in a remote lookup of ejb/HelloBean. Where the lookup is performed is according to how EJB clustering is configured in the application rmi.xml file.

Remote servers are configured in rmi.xml in <server> elements, through the host, port, user, and password attributes as appropriate. If multiple servers are configured, OC4J will search all of them, as necessary, for the intended EJB.

See the Oracle9iAS Containers for J2EE Services Guide for information about rmi.xml.

EJB Lookup Outside the Application

This section discusses servlet coding steps to look up an EJB from a different application (deployed to a different OC4J instance). This functionality uses RMI protocol, and you must specify an appropriate initial context factory for the lookup. As of the OC4J 9.0.3 implementation, you can use RMI over either ORMI or IIOP to look up an EJB that is in a different application.

The remote flag discussed in the preceding section, "Remote EJB Lookup within the Same Application", is not relevant--the lookup is according to the ORMI or IIOP URL. If the host and port are the same as for the calling servlet, then the lookup is local; otherwise, the lookup is remote.

EJB Lookup Outside the Application over ORMI

Here are the servlet coding steps for EJB lookup outside the application (to a different OC4J instance) using RMI over ORMI. Assume that the EJB remote and home interfaces are packaged in the application WAR file.

  1. Populate the environment entries for the lookup. These are static fields of the javax.naming.Context interface, which is implemented by the javax.naming.InitialContext class.

    ...
    Hashtable env = new Hashtable(); 
    env.put(Context.INITIAL_CONTEXT_FACTORY,
                    "com.evermind.server.rmi.RMIInitialContextFactory"); 
    env.put(Context.SECURITY_PRINCIPAL, "admin"); 
    env.put(Context.SECURITY_CREDENTIALS, "debu"); 
    env.put(Context.PROVIDER_URL, "ormi://renmis/ejbsamples");
    ...
    
    
    • The INITIAL_CONTEXT_FACTORY setting specifies the initial context factory to use. This example specifies the OC4J initial context factory for ORMI.

    • The SECURITY_PRINCIPAL setting specifies the identity of the principal for authenticating the caller to the service.

    • The SECURITY_CREDENTIALS setting specifies the credentials of the principal for authenticating the caller to the service.

    • The PROVIDER_URL setting specifies the URL for the lookup.


Notes:

  • If you omit the host and port in the ORMI (or IIOP) URL, the host is assumed to be localhost and a local lookup is performed.

  • In situations such as in this example, where an ORMI or IIOP initial context factory is specified, EJB references declared in web.xml cannot be looked up.

  • If no initial context factory is specified, then ApplicationInitialContextFactory is used by default. This is appropriate when you want to use the context of the application for lookup. EJB references are looked up in web.xml in this case.


  1. Look up the remote EJB and create the bean instance.

    ...
    Context ctx = new InitialContext(env); 
    Object obj = ctx.lookup("MyCart"); 
    home = (CartHome)PortableRemoteObject.narrow(obj,CartHome.class); 
    cart = (Cart)PortableRemoteObject.narrow(home.create(), Cart.class);
    ...
    
    

The EJB must be defined with the name MyCart in the ejb-jar.xml file of the application where it is being looked up.

EJB Lookup Outside the Application over IIOP

To use RMI over IIOP instead of over ORMI, you must specify the OC4J IIOP initial context factory, IIOPInitialContextFactory, as in the following example:

env.put(Context.INITIAL_CONTEXT_FACTORY,
                "com.oracle.iiop.server.IIOPInitialContextFactory");

In this case, any principal (user name) and credentials (password) specified through the environment entries are not used. Instead, assuming Common Secure Interoperability version 2 (CSIv2) is enabled, the available credentials are taken from the current "subject" (a standard JAAS concept) as follows:

You would also have to specify a PROVIDER_URL setting that is appropriate for IIOP, using a corbaname URL scheme. Given that IIOP is the default when using a corbaname, the following PROVIDER_URL setting for IIOP would be equivalent to the PROVIDER_URL setting given earlier for ORMI (ormi://renmis/ejbsamples):

corbaname::localhost:port#/renmis/ejbsamples

(The "#" is part of the syntax.)

See the Oracle9iAS Containers for J2EE Services Guide for additional information about IIOP and related syntax.


Note:

CSIv2 is required by J2EE 1.3 and used by IIOP.


EJB 2.0 Local Interfaces

In the EJB 1.1 specification, an EJB (except for message-driven beans) has a remote interface that extends javax.ejb.EJBObject and a home interface that extends javax.ejb.EJBHome. In this model, all EJBs are defined as remote objects, adding unnecessary overhead to EJB calls in situations where the EJBs are actually local (packaged in the same application, for example).

The EJB 2.0 specification adds support for local connections through local interfaces. An EJB is classified as local if it implements the local versions of the remote and home interfaces--javax.ejb.EJBLocalObject and javax.ejb.EJBLocalHome, respectively.

For a complete example of the use of local interfaces, see the OC4J How-To document at the following location:

http://otn.oracle.com/tech/java/oc4j/htdocs/how-to-ejb-local-interfaces.html

For additional information, refer to the Oracle9iAS Containers for J2EE Enterprise JavaBeans Developer's Guide and Reference.


Go to previous page Go to next page
Oracle
Copyright © 2002 Oracle Corporation.

All Rights Reserved.
Go To Core Documentation
Core
Go To Platform Documentation
Platform
Go To Table Of Contents
Contents
Go To Index
Index