Oracle9iAS Containers for J2EE User's Guide Release 2 (9.0.2) Part Number A95880-01 |
|
This chapter introduces Java servlets and the Oracle9iAS Containers for J2EE (OC4J). Read this chapter if you are not familiar with servlets or if you want to refresh your knowledge of servlets. For more extensive information about servlets, see the Oracle9iAS Containers for J2EE Servlet Developer's Guide.
This chapter covers the following topics:
A servlet is a Java program that runs in a J2EE application server, such as OC4J, and receives and responds to HTTP requests from clients. Think of a servlet as the server-side counterpart to a Java applet. A servlet is one of the four application component types of a J2EE application. Others are applets and application client programs on the client side, and EJBs on the server side. Servlets are managed by the OC4J servlet container; EJBs are managed by the OC4J EJB container. These containers, together with the JavaServer Pages container, form the core of OC4J.
JavaServer Pages (JSP) is another server-side component type. JSP pages also involve the servlet container, because the JSP container itself is a servlet and is therefore executed by the servlet container. The JSP container translates JSP pages into page implementation classes, which are executed by the JSP container but function similarly to servlets. See Chapter 6, "JSP Primer" and the Oracle9iAS Containers for J2EE Support for JavaServer Pages Reference for more information about JSP pages.
Most servlets generate HTML text, which is then sent back to the client for display by the Web browser, or sent on to other components in the application. Servlets can also generate XML, to encapsulate data and send the data to the client or to other components.
A servlet differs from a Java application client in that is has no static main()
method. Therefore, a servlet must execute under the control of a servlet container, because it is the container that calls the methods of the servlet and provides services that the servlet might need when executing.
The servlet itself overrides the access methods (implemented in the GenericServlet
or the HttpServlet
classes) that it needs to process the request and return the response. For example, most servlets override the doGet()
and doPost()
methods (or both) of the HttpServlet
to process HTTP
GET
and POST
requests.
The servlet container provides the servlet easy access to properties of the HTTP request, such as its headers and parameters. In addition, a servlet can use other Java APIs such as JDBC to access a database, RMI to call remote objects, or JMS to perform asynchronous messaging, plus many other Java and J2EE services.
Because servlets are written in the Java programming language, they are supported on any platform that has a Java virtual machine and a Web server that supports servlets. You can use servlets on different platforms without recompiling and you can package servlets together with associated files such as graphics, sounds, and other data to make a complete Web application. This greatly simplifies application development.
It also means that your servlet-based application that was developed to run on another application server can be ported to OC4J with little effort. If your application was developed for an application server that complies with J2EE, then the porting effort is minimal.
Servlets outperform earlier ways of generating dynamic HTML, such as server-side includes or CGI scripts. Once a servlet is loaded into memory, it can run on a single lightweight thread; CGI scripts must be loaded in a different process for every request.
A servlet, along with optional servlet filters, relates to the servlet container and a client, such as a Web browser. When the Web listener is the Oracle HTTP Server, then the connection to the OC4J servlet container is through the mod_oc4j
module. See the Oracle HTTP Server Administration Guide for details.
A good way to learn about servlets and how to code them is to view some simple servlet examples. This section displays the code for two servlets and annotates the code with comments. For simplicity, numbered callouts are located beside sections of code and the corresponding descriptions for each number section appears below the code example.
Here is another "Hello World" demo. But it does serve to show the basic framework you use to write a servlet. This servlet just prints "Hi There!" back to the client.
import java.io.*; // 1. (first comment) import javax.servlet.*; import javax.servlet.http.*; public class HelloWorldServlet extends HttpServlet { // 2. public void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 3. resp.setContentType("text/html"); // 4. ServletOutputStream out = resp.getOutputStream(); // 5. out.println("<html>"); // 6. out.println("<head><title>Hello World</title></head>"); out.println("<body>"); out.println("<h1>Hi There!</h1>"); out.println("</body></html>"); // 7. } }
HttpServlet
class, which implements the methods that a servlet uses. You override the methods you need for your particular servlet implementation.
doGet()
method here overrides the one in the HttpServlet
class, which services HTTP GET
requests. Like almost all HttpServlet
methods, doGet()
takes request and response objects as parameters. In this example, no methods are called on the request object (req
), because this example requires no input (that is, request) data.
setContentType()
method on the response object to set the response content MIME type in the header. Here, it is text/html
, because that is what this servlet generates.
resp
) to get a writer that sends the output of the server back to the client. You could also get a PrintWriter
from the response object.
<h1>
) format.
body
and html
tags.
Save this servlet in a file called HelloWorldServlet.java
. Compile the servlet, using a Java 1.3.x compliant compiler:
% javac HelloWorldServlet.java
If you would like to try out this servlet in OC4J, just configure a web.xml
and archive these in a WAR file. Deploy the WAR file using the Deploy WAR File button on the OC4J Home Page. In the wizard, provide the URL servlet context as /j2ee/hello
. Thus, the WAR is deployed into the /j2ee/hello
servlet context. Having made sure that OC4J is up and running, you can invoke this servlet and see its output from a Web browser. Just enter the URL:
http://<apache_host>:<port>/j2ee/hello/servlet/HelloWorldServlet
The /servlet
part of the URI is an OC4J feature that starts up any servlet indicated, which in this case is the HelloWorldServlet
. Alternatively, you could have configured a context for the servlet in the application web.xml
. For example, the HelloWorldServlet
could be mapped to a URL, such as "world
", as follows:
<servlet-mapping> <servlet-name>HelloWorldServlet</servlet-name> <url-pattern>/world</url-pattern> </servlet-mapping>
Thus, you would invoke the servlet as follows:
http://<apache_host>:<port>/j2ee/hello/world
The <
apache_host
>
represents the name of the host that the OC4J server is running on. By default in Oracle9iAS, specify port 7777 for access through the Oracle HTTP Server with Oracle9iAS Web Cache enabled.
If your servlet exists within a package (or packages), you would include the packages in the <servlet-name>
definition. The following shows the <servlet-name>
definition for the HelloWorldServlet
that is included in the "my
" package. If this servlet is included in a nested group of packages, they are separated by a period.
<servlet-mapping> <servlet-name>my.HelloWorldServlet</servlet-name> <url-pattern>/world</url-pattern> </servlet-mapping>
The HttpServlet
methods, such as doGet()
and doPost()
, take two parameters: an HttpServletRequest
object and an HttpServletResponse
object. The servlet container passes these objects to the servlet and receives the response to pass on to the client, to the next servlet in a filter chain, or to another server object such as an EJB.
The request and response objects support methods that enable you to write efficient servlet code. In the preceding example, you saw that you can get a stream writer object from the response and use it to write statements to the response stream.
The HelloWorldServlet
example shows a minimal servlet--it really does not do very much. But the power of servlets comes from their ability to retrieve data from a database. A servlet can generate dynamic HTML: the servlet can get information from a database and send it back to the client.
Of course, a servlet can also update a database, based upon information passed to it in the HTTP request.
In the next example, a servlet gets some information from the client (the Web browser) and queries a database to get information based on the request data.
Although there are many ways that a servlet can get information from its client, this example uses a very common method: reading a query string from the HTTP request.
The Web browser accesses a form in a page that is served through the OC4J Web listener. First, enter the following text into a file. Next, name the file EmpInfo.html
.
<html> <head> <title>Query the Employees Table</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> </form> </body> </html>
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 complexity of this servlet comes from the JDBC code required to connect to the data server and retrieve the query rows. If you are not familiar with JDBC, see the Oracle9i JDBC Developer's Guide and Reference.
Here is the code for the servlet:
import javax.servlet.*; import javax.servlet.http.*; import javax.naming.*; //1. (see comments below) import javax.sql.*; // 2. import oracle.jdbc.*; public class GetEmpInfo extends HttpServlet { DataSource ds = null; Connection conn = null; public void init() throws ServletException { // 3. try { InitialContext ic = new InitialContext(); // 4. ds = (DataSource) ic.lookup("java:comp/env/jdbc/OracleDS"); // 5. conn = ds.getConnection(); // 6. } catch (SQLException se) { // 7. throw new ServletException(se); } catch (NamingException ne) { // 8. throw new ServletException(ne); } } public void doGet (HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { String queryVal = req.getParameter("queryVal"); // 9. String query = //10. "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 { Statement stmt = conn.createStatement(); //11. ResultSet rs = stmt.executeQuery(query); //12. for (int count = 0; ; count++ ) { //13. if (rs.next()) { out.println(rs.getString(1) + " " + rs.getInt(2) + "<br>"); } else { out.println("<h3>" + count + " rows retrieved</h3>"); break; } } rs.close(); //14. stmt.close(); } catch (SQLException se) { //15. se.printStackTrace(out); } out.println("</body></html>"); } public void destroy() { //16. try { conn.close(); } catch (SWLException ignored) { } } }
HttpServlet
init()
method to look up a data source and get a database connection using the data source.
OracleDS
. This assumes it has been configured in Enterprise Manager using the following element definitions:
<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" />
You can configure this data source either using the Advanced Properties or the Data Source links in the Administration section of either the OC4J Home Page or the application page.
In Oracle9iAS 9.0.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.
try...catch
sequence, to catch JNDI naming or SQL errors.
ServletException
.
ResultSet
object. This causes the statement to execute the query, and returns a result set, which may be empty, or else contains all the rows that satisfy the query.
getString()
and getInt()
methods of the result set instance. See the Oracle9i JDBC Developer's Guide and Reference for more information about the result set's getXXX()
methods.
destroy()
method closes the database connection.
When your browser invokes EmpInfo.html
, you should see a browser window that looks something like this:
Entering 'S%'
in the form, and pressing Submit Query calls the GetEmpInfo
servlet, and the results look like this:
The output from the GetEmpInfo
servlet is not very well formatted. But since the servlet generates HTML, there's no reason why you can't make the output a bit prettier. Just add an HTML table
statement before the Java for statement, and replace the out.println()
code in the for with some out.println()
calls that generate HTML table rows. Here is one way to do this:
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; } }
This simple modification generates better-looking output in a browser window, as shown here:
Servlets, and their JSP relatives, have come into widespread use for applications like shopping carts. For example, clients search for an item on a web site, then go to a page that describes the item more fully, and then might decide to buy the item, putting in their shopping basket. Finally, they check out, giving credit card details and a shipping address. To implement such a site, the server must be able to track the identity of clients as they migrate from page to page of the Web site.
Several mechanisms have been developed to do this, but the most widely-used is undoubtedly the cookie. A cookie is just a small piece of information, that includes the server session ID, that the server sends back to the client. The client (the Web browser, for example) then returns the cookie to the server on each new HTTP request. So a cookie provides a means to let a client synchronize with a server session to maintain stateful information while still using the stateless HTTP protocol.
In addition to cookies, for client to server communication, the OC4J servlet container supports the HttpSession
object, as described in the servlet specifications. An HTTP session object is scoped over the Web application only. This means that you cannot use session objects to share data between applications, or between different clients. To do these things, you should persist the data in a database or some other location.
The SessionServlet
code below implements a servlet that establishes an HttpSession
object, and uses that object to maintain a counter that records the number of times the session has been accessed. The servlet also prints a lot of information obtained both from the request and the session objects, to illustrate some of the capabilities of the HttpServletRequest
and the HttpSession
classes.
import java.io.*; import java.util.Enumeration; 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 { HttpSession session = req.getSession(true); // 1. res.setContentType("text/html"); PrintWriter out = res.getWriter(); out.println("<head><title> " + "SessionServlet Output " + "</title></head><body>"); out.println("<h1> SessionServlet Output </h1>"); Integer ival = (Integer) session.getAttribute("sessionservlet.counter"); // 2. if (ival==null) { ival = new Integer(1); } else { ival = new Integer(ival.intValue() + 1); } session.setAttribute("sessionservlet.counter", ival); // 3. out.println(" You have hit this page <b>" + ival + "</b> times.<p>"); // 4. out.println("Click <a href=" + res.encodeURL(HttpUtils.getRequestURL(req).toString()) + ">here</a>"); // 5. 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."); out.println("<h3>Request and Session Data</h3>"); // 6. 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()); out.println("<h3>Session Data</h3>"); // 7. 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() { //8. return "A simple session servlet"; } }
getSession(true)
method creates a new session if one hasn't already been created.
int
value. The name sessionservlet.counter
is an arbitrary key name for the attribute that is assigned by this servlet.
getServletInfo()
is a method that the container can call when it needs to supply information about what the servlet does. A servlet can override this GenericServlet
method to provide meaningful information for the container.
When you invoke the SessionServlet from a web browser, you will see something like the following:
You can use filters to process the requests that servlets receive, process the responses, or do both. For example, an application might need to provide special logging of certain kinds of requests for one or more servlets, or might need to encrypt the output (response objects) of a whole class of servlets.
Unlike servlets, filters do not generally create a response. You use filters to modify the requests or responses, or to perform some other action based on the requests or responses. These actions could include:
The javax.servlet.Filter
interface was added to the Servlet 2.3 specification to allow an application to perform these kinds of tasks. Several filters can be chained together to perform a series of tasks on requests or responses.
This example implements a simple filter that logs the amount of time (in milliseconds) required to process a servlet request. In this example, the filter is deployed to the default Web application, and a time log of each servlet or JSP invocation is written to the global-application.log
file in the j2ee/home/log
directory. To see the results of the filter, just examine this file in a separate window as servlet requests are being processed. On a UNIX-type system, you can use the command:
% tail -f j2ee/home
/log/global-application.log
The log filter implementation is commented, just like the previous examples.
import java.io.*; import java.util.*; import javax.servlet.*; import javax.servlet.http.*; public class LogFilter implements Filter { //1. FilterConfig config; ServletContext context; public void init(FilterConfig config) { //2. this.config = config; context = config.getServletContext(); //3. } public void destroy() { //4. context.log("Log Filter terminating."); } public //5. void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { long bef = System.currentTimeMillis(); chain.doFilter(req, res); //6. long aft = System.currentTimeMillis(); HttpServletRequest nreq = (HttpServletRequest) req; context.log("Request from " + nreq.getRequestURI() + ": " + (aft-bef)); } }
javax.servlet.Filter
interface: doFilter()
, init()
, and destroy()
.
init()
method at startup.
ServletContext
object from the configuration, to use writing the to the log file.
destroy()
method must be implemented. The container calls destroy()
before terminating the filter, so put any clean-up code, such as closing file handles, here.
doFilter()
takes request and response objects as parameters, and a FilterChain
object that lets the filter pass on the request and response objects (perhaps wrapped) to the next filter in the chain, or at the end of the chain, to the servlet or back to the container. The container calls filters before and after processing the target servlet.
config
object.
This filter is solitary (there is no chain), so the FilterChain
parameter is not used in the doFilter()
invocation.
After the servlet has finished, the filter computes the time required to process the servlet (in milliseconds), and writes the value out to the log file, along with the URI that invoked the servlet for which the filter applies.
Filters are configured in the deployment descriptor of a web application. Create a <filter>
tag in the web.xml
file, indicating a name for the filter and the name of the class that implements the filter. The filter in this example is intended to monitor all servlet requests for the application, so there must be a mapping to indicate that and to have it filter all requests: '/*'
.
Therefore, to deploy this filter in the default Web application, enter the following lines in web.xml
:
<web-app> ... <filter> <filter-name>log</filter-name> <filter-class>LogFilter</filter-class> </filter> <filter-mapping> <filter-name>log</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ... </web-app>
This sample shows the output that this filter generates. The PrimeSearcher
servlet was initialized by the container, and called a few times, then the server was shut down, but first the container called the filter destroy()
method. The lines that begin "Request from..." are the filter output.
8/1/01 8:50 AM defaultWebApp: 1.0.2.2 Stopped 8/1/01 8:50 AM defaultWebApp: PrimeSearcher: init 8/1/01 8:50 AM defaultWebApp: 1.0.2.2 Started 8/1/01 8:50 AM defaultWebApp: PrimeSearcher: init 8/1/01 8:50 AM defaultWebApp: Request from /servlet/PrimeSearcher: 1 8/1/01 10:10 AM defaultWebApp: Request from /servlet/PrimeSearcher: 1 8/2/01 5:56 AM defaultWebApp: Request from /servlet/PrimeSearcher: 2 8/2/01 2:12 PM defaultWebApp: Log Filter done. 8/2/01 2:12 PM defaultWebApp: 1.0.2.2 Stopped 8/2/01 2:12 PM Stopped (Shutdown executed by admin from 130.35.172.234 (dlsun1497))
For more information about filters, filter chains, and filter deployment, see the Oracle9iAS Containers for J2EE Servlet Developer's Guide.
Your first step in learning more about servlets should be to read the Oracle9iAS Containers for J2EE Servlet Developer's Guide. This guide tells you what you need to know to develop servlets and web-tier applications in the OC4J environment.
To get complete documentation on the servlet APIs, visit the Sun Microsystems Web site at:
http://java.sun.com/j2ee/docs.html
You can also find a great deal of tutorial information on servlets as well as other aspects of J2EE application development at this site.
Finally, there are several trade press books that will teach you how to develop servlets, and deploy them in J2EE-compatible applications. In particular, the books from O'Reilly & Associates (http://www.oreilly.com
) and Wrox (http://www.wrox.com
) are very useful.
|
Copyright © 2002 Oracle Corporation. All Rights Reserved. |
|