2.6. JavaServer PagesJust as this book was going to press, Sun announced a new way to use servlets, called JavaServer Pages (commonly, but not officially, referred to as JSP). JSP's functionality and syntax bear a remarkable resemblance to Active Server Pages (ASP). JSP operates in many ways like server-side includes. The main difference is that instead of embedding a <SERVLET> tag in an HTML page, JSP embeds actual snippets of servlet code. It's an attempt by Sun to separate content from presentation, more convenient than server-side includes for pages that have chunks of dynamic content intermingled with static content in several different places. Just like server-side includes and servlet chaining, JSP doesn't require any changes to the Servlet API. But it does require special support in your web server. This support is not included in the Java Web Server 1.1.1 (the unofficially considered reference servlet engine against which this book is written), but it's expected to be introduced in the next version of the Java Web Server, probably 1.2, and in other servlet engines as they keep pace. Note that the following tutorial is based on the JavaServer Pages draft specification, version 0.91. You may notice small changes in the final specification. 2.6.1. Using JavaServer PagesAt its most basic, JSP allows for the direct insertion of servlet code into an otherwise static HTML file. Each block of servlet code (called a scriptlet) is surrounded by a leading <% tag and a closing %> tag.[8] For convenience, a scriptlet can use four predefined variables:
Example 2-6 shows a simple JSP page that says "Hello" in a manner similar to Example 2-2, though with a lot less code. It makes use of the predefined request and out variables. If you have a server that supports JavaServer Pages and want to test this page, you should place the file under the server's document root (probably server_root/public_html) and save it with a special extension. By default, this extension for JSP pages is .jsp. Assuming you have saved the page as hello1.jsp, you can then access it at the URL http://server:port/hello1.jsp. A screen shot is shown in Figure 2-11. Example 2-6. Saying Hello with JSP<HTML> <HEAD><TITLE>Hello</TITLE></HEAD> <BODY> <H1> <% if (request.getParameter("name") == null) { out.println("Hello World"); } else { out.println("Hello, " + request.getParameter("name")); } %> </H1> </BODY></HTML> Figure 2-11. Saying Hello using JavaServer Pages2.6.2. Behind the ScenesHow does JSP work? Behind the scenes, the server automatically creates, compiles, loads, and runs a special servlet to generate the page's content, as shown in Figure 2-12. You can think of this special servlet as a background, workhorse servlet. The static portions of the HTML page are generated by the workhorse servlet using the equivalent of out.println() calls, while the dynamic portions are included directly. For example, the servlet shown in Example 2-7 might be the background workhorse for hello1.jsp.[9]
Figure 2-12. Generating JavaServer PagesExample 2-7. The workhorse servlet for hello1.jspimport java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class _hello1_xjsp extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); BufferedReader in = request.getReader(); out.println("<HTML>"); out.println("<HEAD><TITLE>Hello</TITLE></HEAD>"); out.println("<BODY>"); out.println("<H1>"); if (request.getParameter("name") == null) { out.println("Hello World"); } else { out.println("Hello, " + request.getParameter("name")); } out.println("</H1>"); out.println("</BODY></HTML>"); } } The first time you access a JSP page, you may notice that it takes a short time to respond. This is the time necessary for the server to create and compile the background servlet. Subsequent requests should be as fast as ever because the server can reuse the servlet. The one exception is when the .jsp file changes, in which case the server notices and recompiles a new background servlet. If there's ever an error in compiling, you can expect the server to somehow report the problem, usually in the page returned to the client. 2.6.3. Expressions and DirectivesIn addition to scriptlets, JavaServer Pages allow the use of expressions and directives . A JSP expression begins with <%= and ends with %>. Any Java expression between the two tags is evaluated, the result is converted to a String, and the text is included directly in the page. This technique eliminates the clutter of an out.println() call. For example, <%= foo %> includes the value of the foo variable. A JSP directive begins with <%@ and ends with %>. A directive allows a JSP page to control certain aspects of its workhorse servlet. Directives can be used to have the workhorse servlet set its content type, import a package, extend a different superclass, implement an interface, and handle either GET or POST requests. A directive can even specify the use of a non-Java scripting language. In between the directive tags certain key variables can be assigned values using the following syntax: <%@ varname = "value" %> Here are the six variables you can set:
Example 2-8 shows a revised version of the Hello page that uses JSP expressions and directives. It uses a method directive to indicate it should handle POST requests, and it uses an expression to simplify its display of the name parameter. Example 2-8. Saying Hello using JSP expressions and directives<%@ method = "doPost" %> <HTML> <HEAD><TITLE>Hello</TITLE></HEAD> <BODY> <H1> <% if (request.getParameter("name") == null) { %> Hello World <% } else { %> Hello, <%= request.getParameter("name") %> <% } %> </H1> </BODY></HTML> The background workhorse servlet for this JSP page should look nearly identical to Example 2-7, with the only difference that this servlet implements doPost() instead of service(). 2.6.4. DeclarationsSometimes it's necessary for a JSP page to define methods and nonlocal variables in its workhorse servlet. For this there is a construct called a JSP declaration. A declaration begins with a <SCRIPT RUNAT="server"> tag and ends with a </SCRIPT> tag. In between the tags, you can include any servlet code that should be placed outside the servlet's service method. Example 2-9 demonstrates this with a JSP page that uses a declaration to define the getName() method. Example 2-9. Saying Hello using a JSP declaration<HTML> <HEAD><TITLE>Hello</TITLE></HEAD> <BODY> <H1> Hello, <%= getName(request) %> </H1> </BODY> </HTML> <SCRIPT RUNAT="server"> private static final String DEFAULT_NAME = "World"; private String getName(HttpServletRequest req) { String name = req.getParameter("name"); if (name == null) return DEFAULT_NAME; else return name; } </SCRIPT> The background servlet created to generate this page might look like the servlet shown in Example 2-10. Example 2-10. The workhorse servlet for a JSP page with a declarationimport java.io.*; import javax.servlet.*; import javax.servlet.http.*; public class _hello3_xjsp extends HttpServlet { public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); BufferedReader in = request.getReader(); out.println("<HTML>"); out.println("<HEAD><TITLE>Hello</TITLE></HEAD>"); out.println("<BODY>"); out.println("<H1>"); out.println("Hello, " + getName(request)); out.println("</H1>"); out.println("</BODY></HTML>"); } private static final String DEFAULT_NAME = "World"; private String getName(HttpServletRequest req) { String name = req.getParameter("name"); if (name == null) return DEFAULT_NAME; else return name; } } 2.6.5. JavaServer Pages and JavaBeansOne of the most interesting and powerful ways to use JavaServer Pages is in cooperation with JavaBeans components. JavaBeans are reusable Java classes whose methods and variables follow specific naming conventions to give them added abilities. They can be embedded directly in a JSP page using <BEAN> tags. A JavaBean component can perform a well-defined task (execute database queries, connect to a mail server, maintain information about the client, etc.) and make its resulting information available to the JSP page through simple accessor methods.[10]
The difference between a JavaBeans component embedded in a JSP page and a normal third-party class used by the generated servlet is that the web server can give JavaBeans special treatment. For example, a server can automatically set a bean's properties (instance variables) using the parameter values in the client's request. In other words, if the request includes a name parameter and the server detects through introspection (a technique in which the methods and variables of a Java class can be programatically determined at runtime) that the bean has a name property and a setName(String name) method, the server can automatically call setName() with the value of the name parameter. There's no need for getParameter() . A bean can also have its scope managed automatically by the server. A bean can be assigned to a specific request (where it is used once and destroyed or recycled) or to a client session (where it's automatically made available every time the same client reconnects). Sessions and session tracking are covered in depth in Chapter 7, "Session Tracking". A bean can even be implemented as a servlet! If the server detects that a bean implements the javax.servlet.Servlet interface (either directly or by extending GenericServlet or HttpServlet), it will call the bean's service() method once for each request and the bean's init() method when the bean is first created. The utility of this functionality is debatable, but it can be used by beans that need to prepare somehow before handling requests. Beans are embedded in a JSP page using the <BEAN> tag. It has the following syntax: <BEAN NAME="lookup name" VARNAME="alternate variable name" TYPE="class or interface name" INTROSPECT="{yes|no}" BEANNAME="file name" CREATE="{yes|no}" SCOPE="{request|session}"> <PARAM property1=value1 property2=value2> </BEAN> You can set the following attributes of the <BEAN> tag:
Parameters can be passed to a bean as a list using a <PARAM> tags placed between the opening <BEAN> tag and the closing </BEAN> tag. The parameter values are used to set the bean's properties using introspection. Example 2-11 demonstrates the use of a JavaBeans component with a JSP page; it says Hello with the help of a HelloBean. Example 2-11. Saying Hello using a JavaBean<%@ import = "HelloBean" %> <BEAN NAME="hello" TYPE="HelloBean" INTROSPECT="yes" CREATE="yes" SCOPE="request"> </BEAN> <HTML> <HEAD><TITLE>Hello</TITLE></HEAD> <BODY> <H1> Hello, <%= hello.getName() %> </H1> </BODY> </HTML> As you can see, using a JavaBeans component with JavaServer Pages greatly reduces the amount of code necessary in the page. This allows a clean separation of content (the functionality the bean provides) from presentation (the HTML structure of the page). By using a well-defined API to interact with the bean, even nonprogrammers can write JSP pages. The code for HelloBean is shown in Example 2-12. Its class file should be placed in the server's classpath (something like server_root/classes, although for the Java Web Server you need to first create this directory). Example 2-12. The HelloBean classpublic class HelloBean { private String name = "World"; public void setName(String name) { this.name = name; } public String getName() { return name; } } This is about as simple a bean as you'll ever see. It has a single name property that is set using setName() and retrieved using getName(). The default value of name is "World", but when a request comes in that includes a NAME parameter, the property is set automatically by the server with a call to setName(). To test the mechanism, try browsing to http://server:port/hellobean.jsp. You should see something similar to the screen shot in Figure 2-13. Figure 2-13. Saying Hello using JavaServer pages in cooperation with a JavaBeans componentCopyright © 2001 O'Reilly & Associates. All rights reserved. |
|