[ Team LiB ] Previous Section Next Section

16.7 Dealing with Scripting Syntax Errors

When you use scripting elements you must be prepared to deal with a new class of syntax errors. The scripting code is inserted into the servlet code, generated based on the JSP page in the translation-phase, more or less as is. A syntax error in a scripting element may therefore result in an error the JSP container can't report in a sensible way.

Directives and action elements don't have this problem. The container reads the JSP page and generates servlet code by replacing all JSP directives and action elements with code that produces the appropriate result. To do this, it needs to analyze these types of elements in detail. If there's a syntax error in a directive or action element, it can easily tell which element is incorrect (as you saw in Chapter 9). A syntax error in a scripting element, on the other hand, isn't discovered when the JSP page is read, but instead when the generated servlet is compiled with a Java compiler. The compiler reports an error in terms of its location in the generated servlet code (as opposed to the location in the JSP page), with messages that don't always make sense to a JSP page author.

Before we look at some real error examples, let's briefly look at how the scripting code is embedded in the generated servlet to really understand the problem. Example 16-5 shows a simple JSP page that uses all three scripting element types.

Example 16-5. JSP page with all scripting element types (allinone.jsp)
<%@ page language="java" contentType="text/html" %>
<%@ page import="java.util.Date" %>
<%!
  private String getGreeting(  ) {
    Date now = new Date(  );
    String greeting = null;
    if (now.getHours(  ) < 12) {
      greeting = "Good morning";
    
}
else if (now.getHours( ) < 18) { greeting = "Good day";
}
else { greeting = "Good evening";
}
return greeting;
}
%>
<html> <head> <title>All Scripting Elements</title> </head> <body bgcolor="white"> <%= getGreeting( ) %> <% if (request.getParameter("name") == null) { %> stranger! <% } else { %> partner! <% } %> How are you? </body> </html>

In this page, an import attribute imports the java.util.Date class. This class is used in a declaration element that defines a method named getGreeting( ). The method returns a String with an appropriate greeting depending on the time of day. An expression element invokes the method and adds the result to the response. Finally, scriptlet elements add either "stranger!" or "partner!" depending on if a request parameter is received or not. This may not make much sense, but it demonstrates the use of all scripting types.

Example 16-6 shows the servlet code the container may create based on this page.

Example 16-6. Servlet generated from JSP page
package org.apache.jsp;
  
import java.util.Date;
import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.jsp.*;
import org.apache.jasper.runtime.*;
  
public class allinone$jsp extends HttpJspBase {
  
          private String getGreeting(  ) {
            Date now = new Date(  );
            String greeting = null;
            if (now.getHours(  ) < 12) {
              greeting = "Good morning";
            
}
else if (now.getHours( ) < 18) { greeting = "Good day"; } else { greeting = "Good evening"; } return greeting; }
public void _jspService(HttpServletRequest request, HttpServletResponse response) throws java.io.IOException, ServletException { JspFactory _jspxFactory = null; PageContext pageContext = null; HttpSession session = null; ServletContext application = null; ServletConfig config = null; JspWriter out = null; Object page = this; String _value = null; try { _jspxFactory = JspFactory.getDefaultFactory( ); response.setContentType("text/html"); pageContext = _jspxFactory.getPageContext(this, request, response, "", true, 8192, true); application = pageContext.getServletContext( ); config = pageContext.getServletConfig( ); session = pageContext.getSession( ); out = pageContext.getOut( ); out.write("\r\n"); out.write("\r\n"); out.write("\r\n<html>\r\n <head>\r\n); out.write(" <title>All Scripting Elements</title>\r\n); out.write("</head>\r\n <body bgcolor=\"white\">\r\n"); out.print( getGreeting( ) ); out.write("\r\n "); if (request.getParameter("name") == null) { out.write("\r\n stranger!\r\n "); } else { out.write("\r\n partner!\r\n "); } out.write("\r\n How are you?\r\n); out.write(" </body>\r\n</html>\r\n"); } catch (Throwable t) { if (out != null && out.getBufferSize( ) != 0) out.clearBuffer( ); if (pageContext != null) pageContext.handlePageException(t); } finally { if (_jspxFactory != null) _jspxFactory.releasePageContext(pageContext); } } }

The generated servlet in Example 16-6 looks a lot more complex than a hand-coded version would. That's because all implicit objects and a number of internal support objects must always be initialized (a hand-coded version doesn't need this generic initialization). The method for processing the request is named _jspService( ), invoked by the service( ) method in the base class. These details aren't important, so let's instead see what happens to the import attribute and all scripting elements.

The import attribute results in a Java import statement, as expected. The declaration element is inserted as is, at the top level of the class, outside the _jspService( ) method. This means that all variables declared in a JSP declaration element end up as instance variables, as opposed to local variables, and that methods in a declaration element don't have access to the JSP implicit variables. If a method needs an implicit variable value, it must be passed as an argument to the method.

The expression element is also inserted as is but wrapped in an out.write( ) call. This is why you mustn't use a semicolon at the end of a JSP expression; it would cause a syntax error when the expression is used as an out.write( ) argument.

Finally, the scripting elements: because they are mixed with other code in the generated servlet, they have the highest potential to cause problems. We will look at some specific examples later, but note how out.write( ) calls are inserted for all template text in between the scriptlet code. In a more complex page, such as one that has an action element enclosed by scriptlet code fragments, the code gets a lot more complex, and the chance for strange side effects increases.

16.7.1 Scripting Syntax Error Examples

Let's look at some examples of problems you need to deal with if you use scripting elements.

Example 16-7 shows a modified version of the page used earlier to illustrate how scripting elements end up in the generated servlet. It has two errors: a semicolon is incorrectly used in the expression, and the closing bracket for the else block in the last scriptlet is missing.

Example 16-7. Invalid semicolon use and missing end bracket (error1.jsp)
<%@ page language="java" contentType="text/html" %>
<%@ page import="java.util.Date" %>
<%!
  private String getGreeting(  ) {
    Date now = new Date(  );
    String greeting = null;
    if (now.getHours(  ) < 12) {
      greeting = "Good morning";
    }
    else if (now.getHours(  ) < 18) {
      greeting = "Good day";
    }
    else {
      greeting = "Good evening";
    }
    return greeting;
  }
%>
<html>
  <head>
    <title>Invalid semicolon use and missing end bracket</title>
  </head>
  <body bgcolor="white">
    <%= getGreeting(  ); %>
    <% if (request.getParameter("name") == null) { %>
      stranger!
    <% } else { %>
      partner!
  
    How are you?
  </body>
</html>

This is the error description Tomcat sends to the browser (with some line breaks added to make it fit the page):

org.apache.jasper.JasperException: Unable to compile class for JSP
  
An error occurred at line: 24 in the jsp file: /ch16/error1.jsp
  
Generated servlet error:
D:\jakarta-tomcat-5.0\work\localhost\ora\ch16\error1_jsp.java:67: ')' expected.
                out.write(String.valueOf(getGreeting(  ); ));
                                                      ^
  
An error occurred at line: 24 in the jsp file: /ch16/error1.jsp
  
Generated servlet error:
D:\jakarta-tomcat-5.0\work\localhost\ora\ch16\error1_jsp.java:67: Illegal start
Of expression
                out.write(String.valueOf(getGreeting(  ); ));
                                                        ^
  
An error occurred at line: -1 in the jsp file: null
  
Generated servlet error:
D:\jakarta-tomcat-5.0\work\localhost\ora\ch16\error1_jsp.java:75:
'catch' without 'try'.
        } catch (Throwable t) {
          ^
  
D:\jakarta-tomcat-5.0\work\localhost\ora\ch16\error1_jsp.java:47: 
'try' without 'catch' or 'finally'.
}
^
  
D:\jakarta-tomcat-5.0\work\localhost\ora\ch16\error1_jsp.java:86: 
'}' expected.
}
 ^
5 errors

The first two error messages are for the invalid semicolon in the expression. Because it includes the expression code, it's fairly easy to understand. At the beginning of the error report, there's a reference to the JSP page file and the line in the page where the first error occurred, but all the other line numbers refer to the servlet code, not the JSP page.

The messages for the missing brace probably don't make much sense to you. The error messages refer to invalid use of catch and try, which doesn't seem to match any code in the JSP page scriptlets. That's because the code with the missing brace is inserted into the block of code generated to output template text, invoke actions, and so forth, as discussed earlier, so the compiler gets confused about what the real problem is.

How can you find the real problem when you get this type of message? If you're a Java programmer, you can look at the generated servlet source file and try to figure out what's really wrong. Most JSP containers can be configured so that the generated source code is saved for you to look at. For Tomcat it's the default, and the name of the file is shown in the error message.

But if you're not a programmer, the only thing you can do is to study all scriptlets in your JSP page carefully and try to figure out what's wrong. That's not always easy, and it's a good reason to avoid scripting elements in your JSP pages. When you have to use scripting, use only extremely simple code and be very careful with the syntax.

Let's look at some other common syntax errors so you at least know the types of messages to expect. Example 16-8 illustrates a typical mistake.

Example 16-8. Scriptlet instead of expression (error2.jsp)
<%@ page language="java" contentType="text/html" %>
<%@ page import="java.util.Date" %>
<html>
  <head>
    <title>Scriptlet instead of expression</title>
  </head>
  <body bgcolor="white">
    Howdy
    <% if (request.getParameter("name") == null) { %>
      stranger!
    <% } else { %>
      partner!
    <% } %>
    It's <% new Date().toString(  ) %> and all is well.
  </body>
</html>

This is simply a case where the opening tag for a JSP expression (<%=) has mistakenly been written as the opening tag for a JSP scriptlet (<%). It looks like an innocent error, but the error message isn't giving you much help to find it:

org.apache.jasper.JasperException: Unable to compile class for JSP
  
An error occurred at line: 14 in the jsp file: /ch16/error2.jsp
  
Generated servlet error:
D:\jakarta-tomcat-5.0\work\localhost\ora\ch16\error2_jsp.java:56: ';' expected
                 new Date().toString(  ) 
                                      ^
  
1 error

Again, the scripting code and the generated code clash, resulting in a message that's hard to understand. But at least you can recognize the code from the JSP page and try to see what's really wrong.

Another common mistake has to do with how the Java compiler deals with classes in the unnamed package. Consider the page in Example 16-9 that uses a class named GreetingBean that doesn't belong to a specific package.

Example 16-9. Using a class from the unnamed package (error3.jsp)
<%@ page language="java" contentType="text/html" %>
<html>
  <head>
    <title>Using a class from the unnamed package</title>
  </head>
  <body bgcolor="white">
    <jsp:useBean id="greeting" class="GreetingBean" />
    <%= greeting.getGreeting(  ) %>
  </body>
</html>

This results in an error report like this, even if the class file for the bean is located where it should (either in WEB-INF/classes or in a JAR file in WEB-INF/lib):

org.apache.jasper.JasperException: Unable to compile class for JSP
  
An error occurred at line: 7 in the jsp file: /ch16/error3.jsp
  
Generated servlet error:
D:\jakarta-tomcat-5.0\work\localhost\ora\ch16\error3_jsp.java:48: 
cannot resolve symbol
symbol: class GreetingBean
location: class org.apache.jsp.error3_jsp
                GreetingBean greeting = null;
                ^
  
An error occurred at line: 7 in the jsp file: /ch16/error3.jsp
  
Generated servlet error:
D:\jakarta-tomcat-5.0\work\localhost\ora\ch16\error3_jsp.java:50: 
cannot resolve symbol
symbol: class GreetingBean
location: class org.apache.jsp.error3_jsp
                greeting = (GreetingBean) pageContext.getAttribute(...)
                            ^

An error occurred at line: 7 in the jsp file: /ch16/error3.jsp
  
Generated servlet error:
D:\jakarta-tomcat-5.0\work\localhost\ora\ch16\error3_jsp.java:53: 
cannot resolve symbol
symbol: class GreetingBean
location: class org.apache.jsp.error3_jsp
               greeting = (GreetingBean) java.beans.Beans.instantiate(...);
                           ^
3 errors

This is a problem I touched on earlier. Note that the error messages say that the symbol GreetingBean can not be found in the class org.apache.jsp.error3_jsp. The package prefix happens to be the package name Tomcat uses for the generated servlets, and other containers use different names. The important thing is that the Java compiler assumes that an unqualified class name refers to a class in the same package as the class it compiles, unless it's been imported with an import statement. Since classes from the unnamed package cannot be imported into a class that belongs to a package, the only recourse is to place the GreetingBean class in a package and use the fully qualified class name in the class attribute of the <jsp:useBean> action. If you need to refer to the class in scripting code, you may also want to add a page directive with an import attribute to the JSP page:

<%@ page language="java" contentType="text/html" %>
<%@ page import="com.mycompany.GreetingBean" %>
<html>
  <head>
    <title>Using a class from the unnamed package</title>
  </head>
  <body bgcolor="white">
    <jsp:useBean id="greeting" class="com.mycompany.GreetingBean" />
    <%= greeting.getGreeting(  ) %>
  </body>
</html>

The misleading and confusing error messages reported for scripting syntax errors are, in my opinion, a big problem and one that's hard to solve completely, even with better JSP container implementations and tools. It can be minimized, but it's always hard for a container to pinpoint the real problem when scripting code is mixed with other generated code. My only advice at this point is (again) to avoid scripting code as much as possible.

    [ Team LiB ] Previous Section Next Section