Thursday, February 10, 2011

#{...} is not allowed in template text

Shortly after starting my adventure with JSP 2.0 I came across the following runtime exception:

org.apache.jasper.JasperException: /index.jsp(13,27) #{...} is not allowed in template text
 org.apache.jasper.compiler.DefaultErrorHandler.jspError(DefaultErrorHandler.java:40)
 org.apache.jasper.compiler.ErrorDispatcher.dispatch(ErrorDispatcher.java:407)
 org.apache.jasper.compiler.ErrorDispatcher.jspError(ErrorDispatcher.java:102)
 ...
 javax.faces.webapp.FacesServlet.service(FacesServlet.java:313)

My first question was: WTF? All my libs are where they should be, no compilation time errs/warnings, syntax seems to be OK as well. OK, I don't have the jsf/core & jsf/html taglibs imported, but I'm using JSF 2.0, so I don't need the <@ taglib uri="..." prefix="..."/> any more, right? Not exactly.
Quick introduction: as of JSF 2.0 Oracle introduces new technology called Facelets. It's a page declaration language used to build views & templates for JSF-based web applications. Facelets are usually created with conformance to XHTML Transitional DTD, thus the default file extension is *.xhtml. Now, how does this apply to the aforementioned error? One of the advantages of Facelets is that they use XML namespace declaration to import tag libraries. Similar mechanism is used in JSP pages with XML syntax. Example:

Facelets (XHTML) - index.xhtml
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
 xmlns="http://www.w3.org/1999/xhtml"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:h="http://java.sun.com/jsf/html">
 <h:head>
  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
  <title>Facelet (JSF 2.0)</title>
 </h:head>
 <h:body>
  Name: <h:outputText value="#{personDetails.name}"/>
 </h:body>
</html>

In the above example, both namespace declarations in <html> tag instruct the container that the page is using standard JSF tag libraries http://java.sun.com/jsf/core & http://java.sun.com/jsf/html, and so they should be automatically imported. Another advantage of the Facelets is that they define their own component tree, so there's no more need to wrap your JSF code into <f:view></f:view> tags, which was mandatory in previous versions of the framework.
So, why did I get the error? Because my file was a *.jsp file (i.e. JSP page) and not an *.xhtml file (default for Facelets), and the container kindly ignored my xmlns declarations.

What about the old JSP pages? We need to import manually all required taglibs. Some examples below (note the *.jsp file extension):

JSP (XHTML, JSP syntax) - index.jsp
<?xml version="1.0" encoding="ISO-8859-1" ?>
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
<%@ taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@ taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html
 xmlns="http://www.w3.org/1999/xhtml"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:h="http://java.sun.com/jsf/html">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
  <title>Insert title here</title>
 </head>
 <body>
  <f:view>
   Name: <h:outputText value="#{personDetails.name}"/>
  </f:view>
 </body>
</html>
JSP (XHTML, XML syntax) - index.jsp
<?xml version="1.0" encoding="ISO-8859-1" ?>
<jsp:root
 xmlns:jsp="http://java.sun.com/JSP/Page"
 xmlns:f="http://java.sun.com/jsf/core"
 xmlns:h="http://java.sun.com/jsf/html" version="2.0">
<html xmlns="http://www.w3.org/1999/xhtml">
 <head>
  <meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1" />
  <title>JSP (XHTML, XML syntax)</title>
 </head>
 <body>
  <f:view>
   Name: <h:outputText value="#{personDetails.name}"/>
  </f:view>
 </body>
</html>

A little explanation to the last snippet. As mentioned before, JSP pages with an XML syntax use the xmlns namespace declarations of the <jsp:root> tag while importing required tag libraries. What differs from Facelets, is that we still need to specify scope of our JSF code by declaring <f:view></f:view> tags in our JSP file.