Oracle9iAS Containers for J2EE Servlet Developer's Guide Release 2 (9.0.3) Part Number A97680-01 |
|
This chapter provides basic information for developing servlets for OC4J and the Oracle9i Application Server, covering the following topics:
The assumption in this chapter is that you are in an OC4J standalone development environment. For considerations in configuring and deploying a production application with Oracle Enterprise Manager in an Oracle9iAS environment, see "OC4J Deployment and Configuration with Oracle9iAS and Enterprise Manager".
Note:
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:
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:
log()
method of the ServletContext
object
Servlets have a predictable and manageable lifecycle:
web.xml
. These can include initialization parameters.
service()
method of the generic servlet, which then delegates the request to doGet()
or doPost()
(or another overridden request-handling method), depending on the information in the request headers.
java.io.PrintWriter
or javax.servlet.ServletOutputStream
object.
destroy()
method before the servlet is unloaded.
Also see the next section, "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:
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.
<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
.
Regarding class loading for servlets, there are two important considerations 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.
search-local-classes-first
: Set this to "true
" to search and load WAR file classes before system classes. The default is "false
".
include-war-manifest-class-path
: Set this to "false
" to not include the class path specified in the WAR file manifest Class-Path
attribute when searching and loading classes from the WAR file (regardless of the search-local-classes-first
setting). The default is "true
".
Also see "Element Descriptions for global-web-application.xml and orion-web.xml".
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.
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.
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.
There are several considerations regarding the security of your Web application running in the OC4J servlet container:
<security-role>
element of the web.xml
deployment descriptor. For general information, refer to the Sun Microsystems Java Servlet Specification, Version 2.3. OC4J also offers related support through the global-web-application.xml
file <security-role-mapping>
element. See "The global-web-application.xml and orion-web.xml Files".
In Oracle9iAS release 2, the OC4J servlet container does not directly support SSL and HTTPS. This affects some default-web-site.xml
elements and attributes, specifically:
<web-site>
element secure
and protocol
attributes--Some settings are unsupported or not recommended.
<web-site>
element <ssl-config>
subelement--This is unsupported.
See "The default-web-site.xml, http-web-site.xml, and Other Web Site XML Files" for information about these elements and attributes.
Invoking by class name bypasses security constraints specified in the web.xml
file, and should be considered only in a development environment. It is also true that when a servlet is invoked by class, any exception it throws may reveal the physical path of the servlet location, which is highly undesirable.
To resolve this problem, particularly in a production environment, configure the application so that the servlet-webdir
attribute of the <orion-web-app>
element of the global-web-application.xml
file does not start with a slash ("/
"). This effectively disables the dynamic servlet class name lookup. Note that the default setting, "/servlet
", does start with a slash.
The following configuration, for example, would remedy the situation:
<orion-web-app ... servlet-webdir="null" ... > ... </orion-web-app>
java.security.SecureRandom
functionality to generate random session ID numbers.
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.
[Oracle_Home]/j2ee/home
This will be referred to as j2ee/home
in the remainder of this document.
j2ee/home/config
In addition, there are the following key directories of the OC4J standalone default Web application:
j2ee/home/default-web-app
j2ee/home/default-web-app/WEB-INF/classes
In an Oracle9iAS environment, directories are configurable using Enterprise Manager. See the Oracle9iAS Containers for J2EE User's Guide.
Note:
Visual Java programming tools now typically support servlet coding. In particular, Oracle9i JDeveloper supports servlet development and includes the following features:
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:
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
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. |
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:
|
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.
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
Servlet sessions were introduced in "Introduction to Servlet Sessions". This section provides details and examples, covering the following topics:
This section provides an overview of servlet session tracking and features, then describes the OC4J implementation.
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.
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:
JSESSIONID
, in the HTTP response header.
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.
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.
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.
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.
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.
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>
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.
The SessionServlet
code below implements a servlet that establishes an HttpSession
object and prints some interesting data held by the request and session objects.
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"; } }
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.
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.
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.
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.
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.
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() { } }
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.
Entering 'S%'
in the form and pressing Submit Query calls the GetEmpInfo
servlet, and the results look something like Figure 2-3.
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:
<ejb-ref>
element in the web.xml
file. See "Local EJB Lookup within the Same Application" below, which includes a detailed example.
Notes:
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:
helloWorld()
method on the remote object, which returns a String
object.
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.
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 () {} }
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; }
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.
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
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
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
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:
|
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
.
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.
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.
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"); ...
INITIAL_CONTEXT_FACTORY
setting specifies the initial context factory to use. This example specifies the OC4J initial context factory for ORMI.
SECURITY_PRINCIPAL
setting specifies the identity of the principal for authenticating the caller to the service.
SECURITY_CREDENTIALS
setting specifies the credentials of the principal for authenticating the caller to the service.
PROVIDER_URL
setting specifies the URL for the lookup.
... 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.
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.
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.
|
Copyright © 2002 Oracle Corporation. All Rights Reserved. |
|