JSP scripting elements allow you to use Java programming language statements in your JSP pages. Scripting elements are typically used to create and access objects, define methods, and manage the flow of control. Many tasks that require the use of scripts can be eliminated by using custom tag libraries, in particular the JSP Standard Tag Library. Because one of the goals of JSP technology is to separate static data from the code needed to dynamically generate content, very sparing use of JSP scripting is recommended. Nevertheless, there may be some circumstances that require its use.
There are three ways to create and use objects in scripting elements:
Instance and class variables of the JSP page’s servlet class are created in declarations and accessed in scriptlets and expressions.
Local variables of the JSP page’s servlet class are created and used in scriptlets and expressions.
Attributes of scope objects (see Using Scope Objects) are created and used in scriptlets and expressions.
This chapter briefly describes the syntax and usage of JSP scripting elements.
This chapter illustrates JSP scripting elements using webclient, a version of the hello1 example introduced in Chapter 3, Getting Started with Web Applications that accesses a web service.
To deploy and run the webclient example using NetBeans IDE, follow these steps:
Build and deploy the JAX-WS web service MyHelloService described in Building, Packaging, and Deploying the Service.
In NetBeans IDE, select File->Open Project.
In the Open Project dialog, navigate to:
tut-install/javaeetutorial5/examples/jaxws/ |
Select the webclient folder.
Select the Open as Main Project check box and the Open Required Projects check box.
Click Open Project.
In the Projects tab, right-click the webclient project, and select Undeploy and Deploy.
To run the application, open the bookstore URL http://localhost:8080/webclient/greeting.
To deploy and run the webclient example using ant, follow these steps:
Build and deploy the JAX-WS web service MyHelloService described in Building, Packaging, and Deploying the Service.
In a terminal window, go to tut-install/javaeetutorial5/examples/jaxws/webclient/.
Run ant. This target will spawn any necessary compilations, will copy files to the tut-install/javaeetutorial5/examples/jaxws/webclient/build/ directory, will create a WAR file, and will copy it to the tut-install/javaeetutorial5/examples/jaxws/webclient/dist directory.
Start the Application Server.
To deploy the example using ant, run the following command:
ant deploy
To run the example, open your browser to http://localhost:8080/webclient/greeting.
To learn how to configure the example, refer to the deployment descriptor (the web.xml file), which includes the following configurations:
A display-name element that specifies the name that tools use to identify the application.
A set of servlet elements that identify the application’s JSP file.
A servlet-mapping element that defines the alias to the JSP file.
The example assumes that the Application Server runs on the default port, 8080. If you have changed the port, you must update the port number in the file tut-install/javaeetutorial5/examples/jaxws/webclient/response.jsp before building and running the example.
JSP technology allows a container to support any scripting language that can call Java objects. If you wish to use a scripting language other than the default, java, you must specify it in the language attribute of the page directive at the beginning of a JSP page:
<%@ page language="scripting-language" %>
Because scripting elements are converted to programming language statements in the JSP page’s servlet class, you must import any classes and packages used by a JSP page. If the page language is java, you import a class or package with the import attribute of the page directive:
<%@ page import="fully-qualified-classname, packagename.*" %>
The webclient JSP page response.jsp uses the following page directive to import the classes needed to access the service classes:
<%@ page import= "helloservice.endpoint.HelloService, helloservice.endpoint.Hello" %>
By default, scripting in JSP pages is valid. Because scripting can make pages difficult to maintain, some JSP page authors or page authoring groups may want to follow a methodology in which scripting elements are not allowed.
You can disable scripting for a group of JSP pages in an application in one of two ways:
Select the Ignore Java Script check box in the JSP Property Group node of the NetBeans IDE web.xml editor.
Set the scripting-invalid element of the application’s deployment descriptor to true. The scripting-invalid element is a child of the jsp-property-group element that defines properties for a set of JSP pages.
For information on how to define a group of JSP pages, see Setting Properties for Groups of JSP Pages. When scripting is invalid, it means that scriptlets, scripting expressions, and declarations will produce a translation error if present in any of the pages in the group. Table 9–1 summarizes the scripting settings and their meanings.
Table 9–1 Scripting Settings
JSP Configuration |
Scripting Encountered |
---|---|
Unspecified |
Valid |
false |
Valid |
true |
Translation Error |
A JSP declaration is used to declare variables and methods in a page’s scripting language. The syntax for a declaration is as follows:
<%! scripting-language-declaration %>
When the scripting language is the Java programming language, variables and methods in JSP declarations become declarations in the JSP page’s servlet class.
You can customize the initialization process to allow the JSP page to read persistent configuration data, initialize resources, and perform any other one-time activities; to do so, you override the jspInit method of the JspPage interface. You release resources using the jspDestroy method. The methods are defined using JSP declarations.
For example, an older version of the Duke’s Bookstore application retrieved the object that accesses the bookstore database from the context and stored a reference to the object in the variable bookDBAO in the jspInit method. The variable definition and the initialization and finalization methods jspInit and jspDestroy were defined in a declaration:
<%! private BookDBAO bookDBAO; public void jspInit() { bookDBAO = (BookDBAO)getServletContext().getAttribute("bookDB"); if (bookDBAO == null) System.out.println("Couldn’t get database."); } %>
When the JSP page was removed from service, the jspDestroy method released the BookDBAO variable.
<%! public void jspDestroy() { bookDBAO = null; } %>
A JSP scriptlet is used to contain any code fragment that is valid for the scripting language used in a page. The syntax for a scriptlet is as follows:
<% scripting-language-statements %>
When the scripting language is set to java, a scriptlet is transformed into a Java programming language statement fragment and is inserted into the service method of the JSP page’s servlet. A programming language variable created within a scriptlet is accessible from anywhere within the JSP page.
In the web service version of the hello1 application, greeting.jsp contains a scriptlet to retrieve the request parameter named username and test whether it is empty. If the if statement evaluates to true, the response page is included. Because the if statement opens a block, the HTML markup would be followed by a scriptlet that closes the block.
<% String username = request.getParameter("username"); if ( username != null && username.length() > 0 ) { %> <%@include file="response.jsp" %> <% } %>
A JSP expression is used to insert the value of a scripting language expression, converted into a string, into the data stream returned to the client. When the scripting language is the Java programming language, an expression is transformed into a statement that converts the value of the expression into a String object and inserts it into the implicit out object.
The syntax for an expression is as follows:
<%= scripting-language-expression %>
Note that a semicolon is not allowed within a JSP expression, even if the same expression has a semicolon when you use it within a scriptlet.
In the web service version of the hello1 application, response.jsp contains the following scriptlet, which gets the proxy that implements the service endpoint interface. It then invokes the sayHello method on the proxy, passing the user name retrieved from a request parameter:
<% String resp = null; try { Hello hello = new HelloService().getHelloPort(); resp = hello.sayHello(request.getParameter("username")); } catch (Exception ex) { resp = ex.toString(); } %>
A scripting expression is then used to insert the value of resp into the output stream:
<h2><font color="black"><%= resp %>!</font></h2>
Tags that accept scripting elements in attribute values or in the body cannot be programmed as simple tags; they must be implemented as classic tags. The following sections describe the TLD elements and JSP tag extension API specific to classic tag handlers. All other TLD elements are the same as for simple tags.
You specify the character of a classic tag’s body content using the body-content element:
<body-content>empty | JSP | tagdependent</body-content>
You must declare the body content of tags that do not have a body as empty. For tags that have a body, there are two options. Body content containing custom and core tags, scripting elements, and HTML text is categorized as JSP. All other types of body content (for example, SQL statements passed to the query tag) are labeled tagdependent.
The classes and interfaces used to implement classic tag handlers are contained in the javax.servlet.jsp.tagext package. Classic tag handlers implement either the Tag, the IterationTag, or the BodyTag interface. Interfaces can be used to take an existing Java object and make it a tag handler. For newly created classic tag handlers, you can use the TagSupport and BodyTagSupport classes as base classes. These classes and interfaces are contained in the javax.servlet.jsp.tagext package.
Tag handler methods defined by the Tag and BodyTag interfaces are called by the JSP page’s servlet at various points during the evaluation of the tag. When the start element of a custom tag is encountered, the JSP page’s servlet calls methods to initialize the appropriate handler and then invokes the handler’s doStartTag method. When the end element of a custom tag is encountered, the handler’s doEndTag method is invoked for all but simple tags. Additional methods are invoked in between when a tag handler needs to manipulate the body of the tag. For further information, see Tags with Bodies. To provide a tag handler implementation, you must implement the methods, summarized in Table 9–2, that are invoked at various stages of processing the tag.
Table 9–2 Tag Handler Methods
Tag Type |
Interface |
Methods |
---|---|---|
Basic |
Tag |
doStartTag, doEndTag |
Attributes |
Tag |
doStartTag, doEndTag, setAttribute1,...,N, release |
Body |
Tag |
doStartTag, doEndTag, release |
Body, iterative evaluation |
IterationTag |
doStartTag, doAfterBody, doEndTag, release |
Body, manipulation |
BodyTag |
doStartTag, doEndTag, release, doInitBody, doAfterBody |
A tag handler has access to an API that allows it to communicate with the JSP page. The entry points to the API are two objects: the JSP context (javax.servlet.jsp.JspContext) for simple tag handlers and the page context (javax.servlet.jsp.PageContext) for classic tag handlers. JspContext provides access to implicit objects. PageContext extends JspContext with HTTP-specific behavior. A tag handler can retrieve all the other implicit objects (request, session, and application) that are accessible from a JSP page through these objects. In addition, implicit objects can have named attributes associated with them. Such attributes are accessed using [set|get]Attribute methods.
If the tag is nested, a tag handler also has access to the handler (called the parent) associated with the enclosing tag.
The Tag interface defines the basic protocol between a tag handler and a JSP page’s servlet. It defines the life cycle and the methods to be invoked when the start and end tags are encountered.
The JSP page’s servlet invokes the setPageContext, setParent, and attribute-setting methods before calling doStartTag. The JSP page’s servlet also guarantees that release will be invoked on the tag handler before the end of the page.
Here is a typical tag handler method invocation sequence:
ATag t = new ATag(); t.setPageContext(...); t.setParent(...); t.setAttribute1(value1); t.setAttribute2(value2); t.doStartTag(); t.doEndTag(); t.release();
The BodyTag interface extends Tag by defining additional methods that let a tag handler access its body. The interface provides three new methods:
setBodyContent: Creates body content and adds to the tag handler
doInitBody: Called before evaluation of the tag body
doAfterBody: Called after evaluation of the tag body
A typical invocation sequence is as follows:
t.doStartTag(); out = pageContext.pushBody(); t.setBodyContent(out); // perform any initialization needed after body content is set t.doInitBody(); t.doAfterBody(); // while doAfterBody returns EVAL_BODY_AGAIN we // iterate body evaluation ... t.doAfterBody(); t.doEndTag(); out = pageContext.popBody(); t.release();
A tag handler for a tag with a body is implemented differently depending on whether or not the tag handler needs to manipulate the body. A tag handler manipulates the body when it reads or modifies the contents of the body.
If the tag handler does not need to manipulate the body, the tag handler should implement the Tag interface. If the tag handler implements the Tag interface and the body of the tag needs to be evaluated, the doStartTag method must return EVAL_BODY_INCLUDE; otherwise it should return SKIP_BODY.
If a tag handler needs to iteratively evaluate the body, it should implement the IterationTag interface. The tag handler should return EVAL_BODY_AGAIN from the doAfterBody method if it determines that the body needs to be evaluated again.
If the tag handler needs to manipulate the body, the tag handler must implement BodyTag (or must be derived from BodyTagSupport).
When a tag handler implements the BodyTag interface, it must implement the doInitBody and the doAfterBody methods. These methods manipulate body content passed to the tag handler by the JSP page’s servlet.
A BodyContent object supports several methods to read and write its contents. A tag handler can use the body content’s getString or getReader method to extract information from the body, and the writeOut(out) method to write the body contents to an out stream. The writer supplied to the writeOut method is obtained using the tag handler’s getPreviousOut method. This method is used to ensure that a tag handler’s results are available to an enclosing tag handler.
If the body of the tag needs to be evaluated, the doStartTag method must return EVAL_BODY_BUFFERED; otherwise, it should return SKIP_BODY.
The doInitBody method is called after the body content is set but before it is evaluated. You generally use this method to perform any initialization that depends on the body content.
The doAfterBody method is called after the body content is evaluated. doAfterBody must return an indication of whether to continue evaluating the body. Thus, if the body should be evaluated again, as would be the case if you were implementing an iteration tag, doAfterBody should return EVAL_BODY_AGAIN; otherwise, doAfterBody should return SKIP_BODY.
The following example reads the content of the body (which contains an SQL query) and passes it to an object that executes the query. Because the body does not need to be reevaluated, doAfterBody returns SKIP_BODY.
public class QueryTag extends BodyTagSupport { public int doAfterBody() throws JspTagException { BodyContent bc = getBodyContent(); // get the bc as string String query = bc.getString(); // clean up bc.clearBody(); try { Statement stmt = connection.createStatement(); result = stmt.executeQuery(query); } catch (SQLException e) { throw new JspTagException("QueryTag: " + e.getMessage()); } return SKIP_BODY; } }
A tag handler should reset its state and release any private resources in the release method.
Tags cooperate by sharing objects. JSP technology supports two styles of object sharing.
The first style requires that a shared object be named and stored in the page context (one of the implicit objects accessible to JSP pages as well as tag handlers). To access objects created and named by another tag, a tag handler uses the pageContext.getAttribute(name,scope) method.
In the second style of object sharing, an object created by the enclosing tag handler of a group of nested tags is available to all inner tag handlers. This form of object sharing has the advantage that it uses a private namespace for the objects, thus reducing the potential for naming conflicts.
To access an object created by an enclosing tag, a tag handler must first obtain its enclosing tag using the static method TagSupport.findAncestorWithClass(from,class) or the TagSupport.getParent method. The former method should be used when a specific nesting of tag handlers cannot be guaranteed. After the ancestor has been retrieved, a tag handler can access any statically or dynamically created objects. Statically created objects are members of the parent. Private objects can also be created dynamically. Such objects can be stored in a tag handler using the setValue method and can be retrieved using the getValue method.
The following example illustrates a tag handler that supports both the named approach and the private object approach to sharing objects. In the example, the handler for a query tag checks whether an attribute named connectionId has been set. If the connection attribute has been set, the handler retrieves the connection object from the page context. Otherwise, the tag handler first retrieves the tag handler for the enclosing tag and then retrieves the connection object from that handler.
public class QueryTag extends BodyTagSupport { public int doStartTag() throws JspException { String cid = getConnectionId(); Connection connection; if (cid != null) { // there is a connection id, use it connection =(Connection)pageContext. getAttribute(cid); } else { ConnectionTag ancestorTag = (ConnectionTag)findAncestorWithClass(this, ConnectionTag.class); if (ancestorTag == null) { throw new JspTagException("A query without a connection attribute must be nested within a connection tag."); } connection = ancestorTag.getConnection(); ... } } }
The query tag implemented by this tag handler can be used in either of the following ways:
<tt:connection cid="con01" ... > ... </tt:connection> <tt:query id="balances" connectionId="con01"> SELECT account, balance FROM acct_table where customer_number = ? <tt:param value="${requestScope.custNumber}" /> </tt:query> <tt:connection ... > <tt:query cid="balances"> SELECT account, balance FROM acct_table where customer_number = ? <tt:param value="${requestScope.custNumber}" /> </tt:query> </tt:connection>
The TLD for the tag handler use the following declaration to indicate that the connectionId attribute is optional:
<tag> ... <attribute> <name>connectionId</name> <required>false</required> </attribute> </tag>
The mechanisms for defining variables in classic tags are similar to those described in Chapter 8, Custom Tags in JSP Pages. You must declare the variable in a variable element of the TLD or in a tag extra info class. Use PageContext().setAttribute(name,value) or PageContext.setAttribute(name,value,scope) methods in the tag handler to create or update an association between a name that is accessible in the page context and the object that is the value of the variable. For classic tag handlers, Table 9–3 illustrates how the availability of a variable affects when you may want to set or update the variable’s value.
Table 9–3 Variable Availability
Value |
Availability |
In Methods |
---|---|---|
NESTED |
Between the start tag and the end tag |
doStartTag, doInitBody, and doAfterBody |
AT_BEGIN |
From the start tag until the end of the page |
doStartTag, doInitBody, doAfterBody, and doEndTag |
AT_END |
After the end tag until the end of the page |
doEndTag |
A variable defined by a custom tag can also be accessed in a scripting expression. For example, the web service described in the preceding section can be encapsulated in a custom tag that returns the response in a variable named by the var attribute, and then var can be accessed in a scripting expression as follows:
<ws:hello var="response" name="<%=request.getParameter("username")%>" /> <h2><font color="black"><%= response %>!</font></h2>
Remember that in situations where scripting is not allowed (in a tag body where the body-content is declared as scriptless and in a page where scripting is specified to be invalid), you wouldn’t be able to access the variable in a scriptlet or an expression. Instead, you would have to use the JSP expression language to access the variable.