Java Servlet Programming, 2nd Edition > 18. JavaServer Pages > 18.4 Directives |
18.4 DirectivesA JSP 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, control its output buffering and session management, extend a different superclass, and give special handling to errors. A directive can even specify the use of a non-Java scripting language. The directive syntax requires a directive name along with an attribute name/value pair, all surrounded by <%@ %> tags. The quotes around the attribute value are mandatory: <%@ directiveName attribName="attribValue" %> The page directive allows the JSP to control the generated servlet through the setting of special attributes, as listed here:
18.4.1 Using DirectivesExample 18-4 shows a JSP page named errorMaker.jsp that uses several directives. First it sets the page directive session attribute to false because the page doesn't use the session object and there's no need for the server to create a session needlessly. Then it sets the errorPage attribute to /errorTaker.jsp so if the page throws an uncaught exception the errorTaker.jsp page will handle the display of the error message. The body of the page is simple. It throws an exception to trigger the errorPage behavior. (We'll talk about why it uses the if check a little later.) Example 18-4. Born to Be Bad<%-- errorMaker.jsp --%> <%@ page session="false" %> <%-- Don't send needless cookies --%> <%@ page errorPage="/errorTaker.jsp" %> <%-- General error handling page --%> <%-- All we're good for is throwing an exception --%> <% if (System.currentTimeMillis() > 0) { throw new Exception("oops"); } %> The errorTaker.jsp page is shown in Example 18-5. It sets the page directive isErrorPage attribute to true to indicate this page handles errors, and it uses the import attribute to import the com.oreilly.servlet classes. The JSP uses a scriptlet to set the status code of the response to 500, then it writes the body of the page with the exception class name, a stack trace, and a message suggesting the user contact the webmaster to report the problem. Example 18-5. And Your Problem Is...<%-- errorTaker.jsp --%> <%@ page isErrorPage="true" %> <%@ page import="com.oreilly.servlet.*" %> <% response.setStatus(500); %> <HTML> <HEAD><TITLE>Error: <%= exception.getClass().getName() %></TITLE></HEAD> <BODY> <H1> <%= exception.getClass().getName() %> </H1> We encountered an error while executing your page: <PRE> <%= ServletUtils.getStackTraceAsString(exception) %> </PRE> <% String name = request.getServerName(); %> Please contact <A HREF="mailto:webmaster@<%= name %>">webmaster@<%= name %></A> to report the problem. </BODY> </HTML> On any request to errorMaker.jsp, an exception is thrown, control is transferred to errorTaker.jsp, and errorTaker.jsp displays a nice error-reporting page. See Figure 18-3. Figure 18-3. A customizable error pageSo why did we have the System.currentTimeMillis( ) call in errorMaker.jsp ? That stems from the fact that JSP pages are required to preserve all whitespace from the body text of the document. This allows JSP to be used to create not only HTML but also XML, where whitespace can be very important. Unfortunately, preserving all whitespace means that the simple scriptlet: <% throw new Exception("oops"); %> must generate code to print a new line after the throw code that can never be reached! Compiling this scriptlet on Tomcat generates this error message: org.apache.jasper.JasperException: Unable to compile class for JSPC:\engines\jakarta-tomcat\work\localhost_8080%2Fjsp\ _0002ferrorMaker_0002ejsperrorMaker_jsp_3.java:75: Statement not reached. out.write("\r\n"); ^ By adding the check as to whether System.currentTimeMillis( ) is greater than zero, the compiler assumes there's a possibility the exception won't be thrown and thus there's no compile error. Similar problems happen with return and throw statements inside scriptlets. In fact, there are many such "gotchas" when using scriptlets with JSP. If you accidentally write a scriptlet instead of an expression (by forgetting the equals sign), declare a static variable inside a scriptlet (where statics aren't allowed), forget a semicolon (they're not needed in expressions but are needed in scriptlets), or write anything but perfect Java code, you're likely to get a confusing error message because the compiler is acting on the generated Java code, not on the JSP file. To demonstrate the problem, picture if <%= name %> were replaced by <% name %> in errorTaker.jsp. Tomcat generates this error: org.apache.jasper.JasperException: Unable to compile class for JSPC:\engines\jakarta-tomcat\work\localhost_8080%2Fjsp\ _0002ferrorTaker_0002ejsperrorTaker_jsp_6.java:91: Class name not found. name ^ Debugging an error like this often requires a programmer to look at the generated code to reconstruct what caused the error. 18.4.2 Avoid Java Code in JSP PagesScriptlets, expressions, and declarations allow the placement of Java code within a JSP page. They are powerful tools, yet we discourage their use. Even in the hands of a skilled programmer, placing code in the page is "secondhand" programming. You're writing Java code but not directly, and when errors occur the extra level of indirection makes the problem hard to diagnose. When put into the hands on nonprogrammers, the situation becomes even worse. The nonprogrammers must learn some amount of Java, a language not designed for scripting and not intended for nonprogrammers. A language like JavaScript simpler, more forgiving, and better known by web designers could work better than Java, but JavaScript support is nonstandard and not widely implemented. And finally, it's a matter of design. Having designers and developers working on the same file creates a bottleneck and extra opportunities for error. The content and presentation should be separated. To enable this and to move Java code out of the page, JSP allows the use of JavaBeans and custom tag libraries. We'll look at JavaBeans next.
|