Advanced JavaServer Pages
David M. Geary Publisher: Prentice Hall PTR First Edition May 01, 2001 ISBN: 0-13-030704-1, 508 pages
To fully exploit the power of JavaServer Pages technology in Web application development, based on J2EE technology, you need to master the sophisticated server-side techniques that David Geary presents in Advanced JavaServer Pages. Advanced JavaServer Pages features detailed chapters on internationalization, authentication, JSP technology templates, design, and XML. It concludes with a comprehensive case study that ties together key topics developed in the book and demonstrates how to integrate advanced techniques based on JSP technology. This book is a must-have resource for every developer of Java technology creating server-side applications with JSP technology and servlets.
Advanced JavaServer Pages
Table of Contents Table of Contents ................................................................................................................. 1 Preface ................................................................................................................................... 1 What This Book Is About................................................................................................... 1 The Servlet and JSP APIs This Book Depends Upon ........................................................ 2 How This Book's Code Was Tested................................................................................... 2 This Book's Audience......................................................................................................... 2 How This Book Was Written ............................................................................................. 2 How To Use This Book...................................................................................................... 3 This Book's Custom Tag Libraries..................................................................................... 3 This Book's Code ............................................................................................................... 3 Conventions Used in This Book......................................................................................... 4 Acknowledgments................................................................................................................. 5 Chapter 1. CUSTOM TAG FUNDAMENTALS............................................................... 6 Using Custom Tags—The JSP File.................................................................................... 8 Defining Custom Tags—The TLD .................................................................................... 9 Implementing Custom Tags—Tag Handlers.................................................................... 10 Specifying the TLD in WEB-INF/web.xml ..................................................................... 12 and ........................................................................................................... 13 The Tag Life Cycle .......................................................................................................... 14 Thread Safety ................................................................................................................... 15 Tags with Attributes ......................................................................................................... 16 Accessing Page Information............................................................................................. 20 Error Handling.................................................................................................................. 22 The Tag Package .............................................................................................................. 23 Tags with Bodies .............................................................................................................. 26 Conclusion........................................................................................................................ 28 Chapter 2. CUSTOM TAG ADVANCED CONCEPTS................................................. 29 Body Tag Handlers........................................................................................................... 30 Iteration ............................................................................................................................ 32 Scripting Variables ........................................................................................................... 36 Body Content.................................................................................................................... 40 Nested Tags ...................................................................................................................... 50 Conclusion........................................................................................................................ 52 Chapter 3. HTML FORMS ............................................................................................... 53 Forms with Beans............................................................................................................. 53 Validation ......................................................................................................................... 60 A Form Framework .......................................................................................................... 67 Custom Tags..................................................................................................................... 80 Conclusion........................................................................................................................ 81 Chapter 4. TEMPLATES .................................................................................................. 83 Encapsulating Layout....................................................................................................... 84 Optional Content .............................................................................................................. 88 Role-based Content .......................................................................................................... 91 Defining Regions Separately............................................................................................ 92 Nesting Regions ............................................................................................................... 94 Extending Regions ........................................................................................................... 96 Combining Features ......................................................................................................... 98 Region Tag Implementations ......................................................................................... 100
Advanced JavaServer Pages
Conclusion...................................................................................................................... 111 Chapter 5. DESIGN ......................................................................................................... 112 Model 1 .......................................................................................................................... 112 Model 2: An MVC Approach......................................................................................... 114 A Model 2 Example ....................................................................................................... 115 Conclusion...................................................................................................................... 127 Chapter 6. A MODEL 2 FRAMEWORK...................................................................... 128 A Model 2 Framework ................................................................................................... 128 Refining the Design........................................................................................................ 136 Adding Use Cases .......................................................................................................... 141 The Importance of Custom Tags .................................................................................... 145 JSP Scripts...................................................................................................................... 146 Conclusion...................................................................................................................... 149 Chapter 7. EVENT HANDLING AND SENSITIVE FORM RESUBMISSIONS..... 151 Event Handling for a Model 2 Framework .................................................................... 151 Sensitive Form Resubmissions....................................................................................... 156 Conclusion...................................................................................................................... 169 Chapter 8. I18N ................................................................................................................ 170 Unicode .......................................................................................................................... 170 Charsets .......................................................................................................................... 172 Locales ........................................................................................................................... 174 Resource Bundles ........................................................................................................... 176 Multiple Resource Bundles ............................................................................................ 183 Formatting Locale-Sensitive Information ...................................................................... 185 Browser Language Preferences...................................................................................... 192 Custom Tags................................................................................................................... 195 Conclusion...................................................................................................................... 204 Chapter 9. SECURITY .................................................................................................... 205 Servlet Authentication.................................................................................................... 205 Basic Authentication ...................................................................................................... 209 Digest Authentication..................................................................................................... 211 Form-Based Authentication ........................................................................................... 212 SSL and Client Certificate Authentication..................................................................... 215 Customizing Authentication........................................................................................... 215 Web Application Security Elements .............................................................................. 220 Programmatic Authentication ........................................................................................ 221 Conclusion...................................................................................................................... 231 Chapter 10. DATABASES............................................................................................... 232 Database Creation .......................................................................................................... 233 Data Sources................................................................................................................... 235 Database Custom Tags ................................................................................................... 235 Connection Pooling ........................................................................................................ 247 Prepared Statements ....................................................................................................... 256 Transactions ................................................................................................................... 262 Scrolling Through Result Sets........................................................................................ 265 Conclusion...................................................................................................................... 269 Chapter 11. XML ............................................................................................................. 271 Generating XML ............................................................................................................ 272 Postprocessing XML ...................................................................................................... 278 Parsing XML .................................................................................................................. 279
Advanced JavaServer Pages
Transforming XML ........................................................................................................ 308 Using XPath ................................................................................................................... 316 Conclusion...................................................................................................................... 320 Chapter 12. A CASE STUDY.......................................................................................... 321 The Fruitstand ................................................................................................................ 322 The Model 2 Framework................................................................................................ 341 Internationalization......................................................................................................... 359 Authentication ................................................................................................................ 363 HTML Forms ................................................................................................................. 373 Sensitive Form Resubmissions....................................................................................... 379 SSL ................................................................................................................................. 380 XML and DOM.............................................................................................................. 381 Conclusion...................................................................................................................... 384 Appendix SERVLET FILTERS ..................................................................................... 385 A Servlet Filter Example................................................................................................ 386 Conclusion...................................................................................................................... 389
Advanced JavaServer Pages
Preface Shortly after the Swing volume of Graphic Java was published in March 1999, I became aware of the mass exodus from client-side Java to server-side Java. Because I make a living writing books, I took that exodus very seriously, so I began exploring server-side Java in search of a technology that would be appropriate for my next book. At first, I was enamored with XML, XSLT, and Java, and I spent a good deal of time experimenting with those technologies. But as exciting as those technologies are, it seemed to me that they were on the periphery of developing web applications, and I wanted something that was directly involved in the creation of web applications. Then I discovered servlets. To be honest, I wasn't too excited about servlets. Were software developers really going to create user interfaces by generating HTML with print statements from the guts of some servlet? I knew of at least one software developer that was not. Since 1984, I've had the good fortune to develop software by using a number of object-oriented languages and very cool user interface toolkits. I've developed applications in Smalltalk, Eiffel, and NeXTSTEP, and it seemed to me that developing applications with HTML— especially HTML manually generated from servlets—was akin to trading in a Ferrari for a Yugo. Then I discovered JSP. Although back in 1999 JSP was in its infancy, it was easy to see its potential. Here was a way to mix Java with HTML, which opened the door to all kinds of interesting possibilities. And in the Future Directions section of the JSP 1.0 specification, I saw something that really caught my eye: A portable tag extension mechanism is being considered for the JSP 1.1 specification. This mechanism permits the description of tags that can be used from any JSP page. Wow. With custom tags you could encapsulate Java code, which would essentially allow you to create custom components, in the form of tags, that could be used in conjunction with HTML. From then on, I knew that my next book would be about JSP. So I started to write an introductory JSP book, and I actually wrote the first chapter of that book before I realized two things. First, there was going to be a glut of introductory JSP books, and I did not want to compete against all of those books. Second, and most important, that first chapter was boring, and I hate to read boring books, let alone write them. So, I decided to write this book instead.
What This Book Is About As its name suggests, this book is an advanced treatment of JavaServer Pages. The central theme of this book is the design and implementation of flexible, extensible, and maintainable applications with beans, servlets, and JSP. This book begins where most introductory JSP books leave off, by showing you how to implement JSP custom tags. The ability to create custom tags is arguably JSP's greatest strength because it allows software developers and page authors to work in parallel with few dependencies. Subsequent chapters cover HTML forms, JSP templates, Model 1 and Model 2 architectures, a simple Model 2 framework, handling events, internationalization, security, databases, and XML. This book concludes with a comprehensive case study that shows how to use the techniques discussed in this book to develop a nontrivial web application.
1
Advanced JavaServer Pages
The Servlet and JSP APIs This Book Depends Upon The code in this book depends upon the Servlet 2.2 and JSP 1.1 specifications. Although the Servlet 2.3 and JSP 1.2 specifications were first released in draft form in November 2000, as this book went to press they were still in a state of flux. Because servlet filters are arguably the most important addition to the Servlet 2.3 specification, that topic is covered in “Servlet Filters”; however, you should be aware that the code in that appendix is very likely to change by the time you read this.
How This Book's Code Was Tested I tested all of the code in this book with Tomcat 3.2.1. If a code example from this book does not work correctly with Tomcat 3.2.1, such as the example in “Digest Authentication”, that fact is pointed out in the book's text. Because Tomcat is the reference implementation for the Servlet and JSP specifications, all of the code in this book should work with any servlet container that conforms to the Servlet 2.2 and JSP 1.1 (or higher) specifications. If an example from this book does not work with your servlet container, it is most likely a bug in that servlet container. I also tested all of the code in this book against Resin 1.2, which is an excellent servlet container available from http://www.caucho.com/. As a general rule, it is beneficial to test your code against more than one servlet container to ensure correctness and portability.
This Book's Audience This book was written for Java developers with a basic understanding of servlets and JSP. For most Java developers, this should be their second book that covers servlets and JSP. If you are new to servlets and JSP, I recommend the following books for your first book on those topics: • • •
Core Servlets and JSP by Marty Hall, Sun Microsystems Press Java Servlet Programming by Jason Hunter, O'Reilly Web Development with JavaServer Pages by Fields and Kolb, Manning
It also won't hurt to have a basic understanding of design patterns and the Unified Modeling Language (UML). This book demonstrates how to implement a number of design patterns in JSP-based web applications and uses UML class and sequence diagrams to show how classes are related and how they interact, respectively. See page 181 for a list of resources on design patterns and UML. This book was not written for page authors. If you are a page author with no Java experience, you will be better served by one of the books listed above.
How This Book Was Written Designing object-oriented software is very much an iterative process. You start with a few classes and build on them, all the while iterating over classes, both old and new, as you integrate them to build an ever-evolving system. In object-oriented parlance, that process is known as refactoring.
2
Advanced JavaServer Pages
After working for 15 years as a software engineer, I tend to write books the way I write software. Each of this book's chapters started out in humble fashion. And each chapter was subsequently refactored into the final product that you hold in your hands. You can get a glimpse into this process by looking at a JavaWorld article that I wrote about JSP templates.1 That article is the first cut of this book's Templates chapter, so you can see where the process started for that chapter and where it ended; both the chapter and the code that it discusses underwent much refactoring.
How To Use This Book This book is not a novel, so I don't expect anyone to sit down and read it cover to cover. Because most readers will read chapters out of order in a random fashion, nearly every chapter in the book can stand on its own. There is one exception to that rule. Chapter 6, which discusses a simple Model 2 framework, depends on Chapter 5, which introduces the Model 2 architecture. Chapter 6 retrofits an example from Chapter 5; therefore, Chapter 5 is a prerequisite for Chapter 6. The last chapter in this book is a comprehensive case study that employs the techniques discussed throughout this book to implement a nontrivial web application. You can read (or most likely, skim) that chapter first to get a feel for those techniques, or you can read it last to see how to integrate those techniques. Or you can do both.
This Book's Custom Tag Libraries This book discusses the implementation of approximately 50 JSP custom tags, ranging from internationalization tags to tags that use XML's Document Object Model to parse XML. There are no legal restrictions whatsoever on those tags, so you are free to use those tags in any manner you deem appropriate. See “This Book's Code” to see how you can download those tags. This book's custom tags serve two purposes. First, they illustrate how you can implement your own custom tags. Second, they serve to reinforce the concepts discussed throughout this book. But those custom tags are not the focus of this book; rather, it's the concepts that those tags embody that are important. For example, if you look at the internationalization chapter, you will see that most of that chapter is dedicated to internationalizing text, numbers, dates, and currency in a JSP-based web application. The last few pages of that chapter show how to implement two custom tags that perform internationalization. But it's the internationalization concepts, and not the custom tags, that take center stage in that chapter.
This Book's Code You can download all of the code from this book, including the book's custom tag libraries, from the following URL: http://www.phptr.com/advjsp.
1
See http://developer.java.sun.com/developer/technicalArticles/javaserverpages/jsp_templates.
3
Advanced JavaServer Pages
Conventions Used in This Book Table P-1 shows the coding conventions used in this book. Table P-1. Coding Conventions Convention Example public class ClassName Class names have initial capital letters. Method names have initial lower case, and the rest of the words getLength have an initial capital letter. Variable names have initial lower case, and the rest of the words private int length private int bufferLength have an initial capital letter.
Note that, for the most part, methods are referred to without their arguments; however, arguments are included when the discussion warrants them. Table P-2 shows the typographic conventions used in this book. Table P-2. Typographic Conventions Typeface or Symbol Description courier Indicates a command, file name, class name, method, argument, Java keyword, HTML tag, file content, code excerpt, or URL. bold courier Indicates a sample command-line entry. Indicates definitions, emphasis, a book title, or a variable that should be italics replaced with a valid value.
4
Advanced JavaServer Pages
Acknowledgments Although my name is on the cover, many people have made contributions to this book. First, I would like to thank my reviewers, who provided me with a great deal of valuable feedback. Craig McClanahan, who is the lead developer for Tomcat and the Apache Struts JSP application framework, offered many insights into servlets and JSP that only someone with his level of expertise could have provided. Scott Ferguson, the developer of the Resin servlet container, also provided excellent review comments and, much to my surprise, also offered some good advice on writing. Not only are technical details in the book more accurate because of his comments, but this book also provides more motivation for readers and presents topics in a more logical sequence because of Scott's review comments. Larry Cable, who coauthored the original JSP specification, was also instrumental in making this book more robust; for example, the detailed discussion of custom tag body content in the Custom Tag Advanced Topics chapter would not be in this book if it were not for one of Larry's many excellent suggestions. Rob Gordon, with whom who I had the pleasure of working when with one I was an employee at Sun Microsystems, also provided many insightful review comments that only someone with his level of expertise in writing, Java, and object-oriented software development could provide. Besides my reviewers, I would also like to thank the folks on the Struts mailing list who that provided excellent feedback on the JSP templates custom tag library that I contributed to Struts. With their feedback, I was able to vastly improve that tag library, which is discussed at length in this book's Templates chapter. Cedric Dumoulin deserves special mention for his ideas on extending that original tag library to include component capabilities. I would also like to thank Yun Sang Jung and Chen Jia Ping, who translated English properties files to Korean and Chinese, respectively, for the I18N and Case Study chapters. Rational Rose Software provided me with a copy of Rational Rose for Java, which was used for all of the UML diagrams in this book. Mary Lou Nohr, who has been my editor since the first edition of Graphic Java graphic Java way back in 1996, once again did a masterful job of making substantial improvements to my writing. Greg Doench, from Prentice Hall, and Rachel Borden, from Sun Microsystems Press, also deserve mention for having faith in me and for helping to pull this book together. Patti Guerrieri from Prentice Hall, who is always a pleasure to work with, did a great job of taking my final manuscript and polishing it into this book. Finally, as always, I would like to thank Lesa and Ashley Anna Geary. Without their patience and understanding, I would never have been able to get this book out the door. A final tip of the hat to Blazey who has been my constant companion while writing this book.
5
Advanced JavaServer Pages
Chapter 1. CUSTOM TAG FUNDAMENTALS Topics in this Chapter • • • • • • • • • •
•
Using Custom Tags—The JSP File Defining Custom Tags—The TLD Implementing Custom Tags—Tag Handlers Specifying the TLD in WEB-INF/web.xml The Tag Life Cycle Thread Safety Tags with Attributes Accessing Page Information Error Handling The Tag Package - The Tag Interface - The TagSupport Class: Ancestors, Values, and IDs Tags with Bodies
XML is a hot technology, in no small part because it's a simple metalanguage used to create tags. XML tags represent data specific to a particular domain; for example, the following XML fragment represents a CD collection: RadioheadOK Computer$14.99 ...
Like XML, JSP can be used to create tags; however, whereas XML tags represent data, JSP custom tags represent functionality specific to a particular domain.1 For example, the JSP fragment listed in Example 1-1 uses custom tags to display a table from a database: Example 1-1 Database Access with Custom Tags Database Example <%@ taglib uri='/WEB-INF/tlds/database.tld' prefix='database'%> SELECT * FROM Customers, Orders
1
The term custom tag differentiates between built-in tags and user-implemented (custom) tags. XML has no built-in tags, so there is no need to differentiate.
6
Advanced JavaServer Pages
<%= column_name %>
<%= column_value %>
Example 1-1 creates an HTML table with a mixture of HTML and JSP custom tags. The connect tag makes a database connection, the query tag performs a database query, and rows, columns, and columnNames iterate over the query results. JSP custom tags can be quite sophisticated, as Example 1-1 illustrates. The query tag interprets its body content as SQL, and the connection and query tags collaborate with other tags in the page. The columns and columnNames tags iterate over the query result and create scripting variables named by the JSP developer. In the code fragment listed in Example 1-1, those names are column_name and column_value. Custom tags have an impressive list of features; they can: • • • • • • •
Have a body or be empty Be nested arbitrarily deep within other custom tags Manipulate flow of control; for example, e.g., if statement and iteration Manipulate the contents of their bodies; for example, e.g., filtering and editing Collaborate with other tags on a page Access page information (request, response, session, etc.) Create scripting variables
JSP custom tags conform to the XML specification, so they are, in fact, XML tags. This is significant because it means that JSP custom tags can be manipulated in XML and HTML tools. All of the custom tags in Example 1-1 have start and end tags with body content in between. Custom tags can also be empty, where the start and end tags are combined, like this:
All JSP custom tags are grouped into libraries. Custom tags are distinguished by a prefix associated with their library, which allows tags of the same name from different libraries to be used together.
7
Advanced JavaServer Pages
As of the JSP 1.1 specification, an initiative is underway to specify a standard tag library in a future version of the specification.2 That tag library will include tags for database access and a number of other useful utility tags. JSP Tip Custom Tags Are JSP's Most Powerful Feature Custom tags afford software developers and page authors the freedom to work independently. Page authors can concentrate on using sets of tags, such as HTML, XML, or JSP to create web sites. Software developers can concentrate on implementing low-level functionality, such as internationalization or database access, which is subsequently made available to page authors in the form of custom tags.
Using Custom Tags—The JSP File Figure 1-1 shows a JSP page that uses the simplest of custom tags—one that has no attributes and no body. The tag is a hit counter that keeps track of the number of times a JSP page has been accessed. Figure 1-1. A Counter Tag
The JSP page shown in Figure 1-1 is listed in Example 1-2.a.
2
The initiative is a Java Specification Request (JSR)—see http://java.sun.com/.
8
Advanced JavaServer Pages Example 1-2.a /test.jsp A Counter Tag <%@ taglib uri='/WEB-INF/tlds/counter.tld' prefix='util' %> This page has been accessed times.
Example 1-2.a includes a taglib directive specifying a URI that defines the library and its tags. That URI points to a tag library descriptor (TLD), thus the tld extension in counter.tld. The taglib directive also requires a prefix attribute that specifies the prefix used to access the library's tags; in Example 1-2.a, that prefix is util, so the counter tag is accessed with . The TLD in Example 1-2.a is located in WEB-INF/tlds. Locating the TLD there is not required but is recommended practice.
Defining Custom Tags—The TLD A tag library descriptor is an XML document that defines a tag library and its tags. Example 1-2.b lists the TLD for the tag used in Example 1-2.a. Example 1-2.b /WEB-INF/tlds/counter.tld 1.01.1Sun Microsystems Press Tag LibraryThis tag library has a single counter tagcountertags.CounterTagempty
The first line of the file identifies it as an XML document. The second line further identifies the document type as taglib and supplies a URL to a document type definition (DTD) that defines the structure of taglib documents. That DTD is used by servlet containers to validate the document. Tag libraries are defined with this tag: . In Example 1-2.b, the tag library's version is specified as 1.0, and the library must be used with a JSP implementation that conforms to the 1.1 version (or later) of the JSP specification.
9
Advanced JavaServer Pages
A short name is specified for the tag library listed in Example 1-2.b, in addition to some information about the library. Those values are typically used by page authoring tools. Tags are defined with , which has two mandatory elements: the tag's and its . The latter specifies the Java class that implements the tag's functionality. Those types of classes are known as tag handlers. The counter tag's body content is specified as empty, meaning that it's illegal for counter tags to have a body. Both and have more elements than the ones used above. See “ and ” for more information on those tags and their elements.
Implementing Custom Tags—Tag Handlers Tag handlers implement the Tag interface from javax.servlet.jsp.tagext. That interface defines six methods; the three that are most often used are listed below:3 int doStartTag() throws JspException int doEndTag() throws JspException void release()
Servlet containers invoke the Tag methods listed above in the order they are listed. The doStartTag and doEndTag methods are invoked at the start and end, respectively, of a tag. Both methods return integer constants defined in the Tag interface, specifying indicating how the servlet container should proceed when the methods return. The servlet container invokes the release method after calling doEndTag. The release method should release any resources the tag handler maintains. Let's see how the counter tag's handler, listed in Example 1-2.c, implements the Tag methods listed above. Example 1-2.c /WEB-INF/classes/tags/CounterTag.java package tags; import import import import
java.io.File; java.io.FileReader; java.io.FileWriter; java.io.IOException;
import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; import javax.servlet.http.HttpServletRequest; public class CounterTag extends TagSupport { private int count = 0; private File file = null;
3
See “The Tag Interface” for more information on the Tag interface.
10
Advanced JavaServer Pages
public int doStartTag() throws JspException { try { checkFile(); readCount(); pageContext.getOut().print(++count); } catch(java.io.IOException ex) { throw new JspException(ex.getMessage()); } return SKIP_BODY; } public int doEndTag() throws JspException { saveCount(); return EVAL_PAGE; } private void checkFile() throws JspException, IOException { if(file == null) { file = new File(getCounterFilename()); count = 0; } if(!file.exists()) { file.createNewFile(); saveCount(); } } private String getCounterFilename() { HttpServletRequest req = (HttpServletRequest)pageContext. getRequest(); String servletPath = req.getServletPath(); String realPath = pageContext.getServletContext(). getRealPath(servletPath); return realPath + ".counter"; } private void saveCount() throws JspException { try { FileWriter writer = new FileWriter(file); writer.write(count); writer.close(); } catch(Exception ex) { throw new JspException(ex.getMessage()); } } private void readCount() throws JspException { try { FileReader reader = new FileReader(file); count = reader.read(); reader.close(); } catch(Exception ex) { throw new JspException(ex.getMessage()); } } }
The tag handler listed above keeps track of the number of times that a tag—and therefore that tag's JSP page—has been accessed, by storing a count in a file. That file has the same name as its corresponding JSP page, with a .counter suffix; for example, if the file /index.jsp used
11
Advanced JavaServer Pages
a counter tag, a corresponding /index.jsp.counter file would contain a count of how many times /index.jsp had been accessed. Like many tag handlers, tags.CounterTag extends TagSupport, an implementation of the Tag interface that provides a number of utility methods. TagSupport is discussed in more detail in “The TagSupport Class: Ancestors, Values, and IDs”. The CounterTag.doStartTag method prints the count by using its pageContext4 to access the implicit out object and returns SKIP_BODY to indicate that the tag's body, if present, should be ignored. The servlet container subsequently invokes CounterTag.doEndTag, which returns EVAL_PAGE. That constant directs the servlet container to evaluate the rest of the page following the end tag. The CounterTag class does not implement a release method because neither of its two member variables—count and file—need to be reset for counter tags to be reused. That's because the count and file member variables are reset every time doStartTag is called.
Specifying the TLD in WEB-INF/web.xml The JSP file in Example 1-2.a uses the taglib directive to directly access a tag library descriptor. The taglib directive can also indirectly specify a TLD by referencing another taglib in the web application's deployment descriptor. For example, the taglib directive in Example 1-2.a could be specified as follows: // in a JSP file ... <%@ taglib uri='counters' prefix='util' %>
A second taglib directive in the web application's deployment descriptor specifies the actual location of the TLD for a tag library identified by a URI of counters: // in web.xml ... counters/WEB-INF/tlds/counter.tld
Indirectly specifying a TLD affords more flexibility because the TLD can be moved without modification to modifying the JSP file; therefore, indirect specification is preferred for deployment. Directly specifying a TLD is simpler and is usually preferred during development.
4
pageContext is a protected member of the TagSupport class.
12
Advanced JavaServer Pages
JSP Tip Creating Simple Custom Tags Implementing custom tags is straightforward in general and is quite simple for tags that do not have attributes or manipulate their bodies. Here are the steps required for creating simple custom tags: • Add a taglib directive to JSP files that use the tags. • Create a tag library descriptor (.tld) describing the tags. • Implement a tag handler that extends TagSupport and overrides doStartTag() or doEndTag().
and This section summarizes provides a summary of the elements for and in a tag library descriptor (TLD).5 See Example 1-2.b for an example of how you use those tags. Table 1-1 lists the elements associated with the tag. Table 1-1. Elements (listed in order of appearance) Element Type6 Description tlibversion 1 The version of the tag library jspversion ? The version of the JSP specification the library depends on; default is JSP 1.1 shortname 1 Used by JSP page authoring tools for identifying the library uri ? A URI that uniquely identifies the library info ? A description of describes how the tag library is used tag + The tags contained in the library
Tags are defined with ; for example, in Example 1-2.b, the specifics of the counter tag—its name, tag class, body content type, and information about the tag—are defined. Table 1-2 describes the elements associated with .
Element name tagclass
Table 1-2. Elements (listed in order of appearance) Type7 Description 1 The name comes after the tag prefix, as in: 1 The tag handler's class; tags must implement the Tag interface
teiclass bodycontent
? ?
The class that defines scripting variables for a tag A description of describes body content as one of: • Tag dependent (tag evaluates body content) • JSP (default) (servlet container evaluates body content) • Empty (body must be empty)
5 6 7
According to JSP1.1. 1 = one, required… ? = one, optional… + = one or more… 1 = one, required… ? = one, optional… * = zero or more…
13
Advanced JavaServer Pages
info attribute
? *
Information about the tag A tag attribute; see “ Elements”
Tag attributes are specified with the attribute element, which is further discussed in “Tags with Attributes”. The bodycontent attribute influences how a tag's body content is handled by servlet containers. The default value is JSP, which causes servlet containers to evaluate the body of the tag. If tagdependent is specified for bodycontent, the tag's body is not evaluated by the servlet container; that evaluation is left up to the tag handler. An empty value means that it's illegal for a tag to have a body.
The Tag Life Cycle Tag handlers are software components that are plugged into a servlet container. Servlet containers create tag handlers, initialize them, and call doStartTag, doEndTag, and release, in that order. For simple tags, initialization consists of setting the tag's page context and parent. Once a tag handler's release method has been invoked, that handler may be made available for reuse. Figure 1-2 shows an interaction diagram for the counter tag discussed throughout this chapter. Figure 1-2. Interaction Diagram for a Counter Tag
Figure 1-2 is typical of simple tags without bodies or attributes. The servlet container invokes doStartTag, and the tag reacts in some manner, subsequently returning SKIP_BODY to
indicate that the body of the tag, if present, should not be processed.
14
Advanced JavaServer Pages
Nearly all tags return EVAL_PAGE from doEndTag, so the servlet container processes the rest of the JSP page. After doEndTag, the servlet container calls the tag's release method, where resources, such as database connections, are released and class member variables are reset. More complex tags have more complicated interaction diagrams. For example, tag handlers that manipulate their bodies have additional methods that are invoked by the servlet container. Interaction diagrams for a tag with an attribute, and a tag that iterates over its body content are shown in can be found at Figure 1-5 and Figure 2-2, respectively. Figure 1-5. Interaction Diagram for the GetRequestParameterTag
Thread Safety Regarding the lifetime of Tag instances, the JSP 1.1 specification states: At execution time the implementation of a JSP page will use an available Tag instance … that is not being used … . Afterward , it will release the instance and make it available for further use. The specification's intent is clear: tags are used by one thread at a time. Single-threaded tag access means that tag handlers don't have to guard class members against multithreaded access. Of course, tag handlers must be careful with other thread-sensitive data, such as session or application attributes. Because servlet containers can reuse tag handlers, you must be diligent about implementing the release method and careful about instantiating resources in doStartTag; for example, this tag handler should work as expected—
15
Advanced JavaServer Pages
public class TagHandler extends TagSupport { private Hashtable hashtable; ... public int doStartTag() throws JspException { hashtable = new Hashtable(); ... } public void release() { hashtable = null; } ... }
—but this tag handler is likely to cause a null pointer exception if it's reused: public class TagHandler extends TagSupport { private Hashtable hashtable; ... public TagHandler() { hashtable = new Hashtable(); ... } public void release() { hashtable = null; } ... }
Tags with Attributes Custom tags can have any number of attributes—required or optional—specified as attr=quoted value, where attr is the attribute name and quoted value is the attribute's value. Here's a tag with a single attribute:
Attributes can be specified with request time attribute values, like this:
The fragment above sets the collection attribute to a variable that represents a collection. Adding an attribute to a tag is a three-step process, outlined below. 1. Add the attribute, where applicable, to existing tags in JSP files. 2. Add an attribute tag to the TLD. 3. Implement a setAttr8 method in the tag handler.
8
Substitute JavaBeans-compliant attribute name for Attr.
16
Advanced JavaServer Pages
It's also common to implement a getter method in the tag handler; that way, nested tags have access to properties. The preceding steps listed above are illustrated below with a simple but useful custom tag that has a single attribute. First, we'll take a look at the motivation for the tag, followed by a discussion of its implementation. It's often desirable for textfields to retain their values when a form is redisplayed. For example, Figure 1-3 shows a registration page that is redisplayed if it's not filled out correctly. The left picture shows the incomplete registration, and the right picture shows the response, which redisplays the registration form with a message. The fields in the form retain their values so that users are not unduly punished by having to retype them. Figure 1-3. Textfields that Retain Their Values
Your first attempt to inclination for retaining textfield values might be something like this:
The value attribute of the HTML input tag is set to the request parameter corresponding to the textfield's value. Thus, if the page shown in Figure 1-3 is redisplayed with the original request parameters, the fields will retain their values. There is one drawback to this implementation, as illustrated by Figure 1-4. If there are no request parameters correspond ing to the field names, as is the case when the form is initially displayed, ServletRequest.getParameter returns null, which is displayed in the fields.
17
Advanced JavaServer Pages Figure 1-4. Drawback to Using request.getParameter Directly
One solution to this problem is to implement a custom tag that returns a request parameter's value if the parameter exists, and an empty string otherwise. Example 1-3.a illustrates the use of such a tag with a partial listing of the JSP page shown in Figure 1-3. The requestParameter tag has a single required attribute corresponding to the name of the request parameter. Example 1-3.a /register.jsp <%@ taglib uri='WEB-INF/tlds/html.tld' prefix='html'%> ...
First Name:
Last Name:
18
Advanced JavaServer Pages
E-mail Address:
...
Tag attributes are declared in the tag library descriptor. Example 1-3.b lists the TLD for the tag library containing the requestParameter tag. Example 1-3.b /WEB-INF/tlds/html.tld ... ... requestParametertags.GetRequestParameterTagemptypropertytruetrue
The property attribute is required and can be specified with a request time attribute value. Valid elements for the attribute tag are listed in Table 1-3.9
Element name required rtexprvalue
10
Type 1 ? ?
Table 1-3. Elements Description The attribute's name If true, the attribute must be specified If true, the attribute can be specified with a JSP request time attribute value
The tag handler for the requestParameter tag is listed in Example 1-3.c. Example 1-3.c /WEB-INF/classes/tags/GetRequestParameterTag.java package tags; import javax.servlet.ServletRequest; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class GetRequestParameterTag extends TagSupport { private String property;
9
The elements are valid according According to the JSP 1.1 specification.
10
1 = one, required… ? = one, optional…
19
Advanced JavaServer Pages
public void setProperty(String property) { this.property = property; } public int doStartTag() throws JspException { ServletRequest req = pageContext.getRequest(); String value = req.getParameter(property); try { pageContext.getOut().print(value == null ? "" : value); } catch(java.io.IOException ex) { throw new JspException(ex.getMessage()); } return SKIP_BODY; } }
Tag handlers must implement a setter method compliant with the JavaBeans API compliant setter method for each of their attributes. The setter methods are passed attribute values, which are typically assigned to members of the tag handler's class. The setter methods are guaranteed to be invoked before the tag handler's doStartTag method is called. The GetRequestParameterTag class provides a setter method for the property attribute. That attribute's value is stored as a private member of the GetRequestParameterTag class and is subsequently used in the doStartTag method. The interaction diagram in Figure 1-5 depicts is an interaction diagram depicting the sequence of events that occurs when the requestParameter tag is processed. JSP Tip Tag Attributes and Tag Handler Properties For every attribute defined for a custom tag, there must be a corresponding JavaBeans property in the tag handler class. The tag handler must also provide a setter method for the property, which is guaranteed to be invoked before the tag handler's doStartTag method is called. Because of this sequence of method calls, the doStartTag method always has access to tag attribute values.
Accessing Page Information Custom tags often need access to information about their page, perhaps to examine request parameters or to retrieve an object from a particular scope. Page information is made available to tags with the pageContext, an instance of PageContext, that is a protected member of the TagSupport class. The PageContext class provides a set of utility methods that falls into the following categories:11
11
See Figure 1-6 for more on the PageContext class.
20
Advanced JavaServer Pages
• • • •
Accessing attributes in page scope Accessing attributes in a specified scope Forwarding and including Servlet container methods
All but the last category listed above are useful to custom tag implementers. For example, a custom tag could access an object in page scope like this: public class SomeTag extends TagSupport { public int doStartTag() throws JspException { User user = (User)pageContext.getAttribute("user"); .... } }
A tag could also access an object from a specific scope: User user = (User)pageContext.getAttribute("user", PageContext.SESSION_SCOPE);
The PageContext.getAttribute method returns an Object reference, given a name and a scope. The valid scopes are: PAGE_SCOPE, REQUEST_SCOPE, SESSION_SCOPE, and APPLICATION_SCOPE, all of which are defined in the PageContext class. The pageContext can also be used to access request information, such as a request's locale, as follows: public class SomeTag extends TagSupport { public int doStartTag() throws JspException { ServletRequest request = pageContext.getRequest(); Locale locale = request.getLocale(); ... } }
Table 1-4 lists PageContext methods that are useful to custom tag implementers. Table 1-4. PageContext Methods for Custom Tag Implementers Methods Description Object findAttribute(String) Searches page, request, session, and application scopes for an attribute Object getAttribute(String) Returns an object from page scope, or null if the object is not found void setAttribute(String, Stores an object in page scope Object) void removeAttribute(String) Removes an object from page scope JspWriter getOut() Returns the JspWriter that tags use to produce output ServletRequest getRequest() Returns the request object ServletResponse getResponse() Returns the response object
21
Advanced JavaServer Pages
ServletContext getServletContext() HttpSession getSession() void forward(String path) void include(String path)
Returns the application object Returns the session object Forwards requests to a relative path Includes an HTML or JSP file, given a relative path
setAttribute, getAttribute, and removeAttribute are all overloaded with methods that take an additional integer value. That integer value represents scope, allowing attributes to
be stored in different scopes. Methods that do not specify a scope operate on attributes in page scope. The PageContext class also provides access to implicit variables from the tag's page, such as out, session, etc. PageContext.getOut is one of the most heavily used methods, especially by tags that filter or edit body content. It's a simple matter to forward to, or include, a Web component such as a servlet or JSP page from within a custom tag. The PageContext forward and include methods are passed a string representing the relative path of the resource. As of the JSP 1.1 specification, it is illegal to include a resource from within a body tag handler. This restriction is due to underlying servlet semantics regarding buffering; it should be remedied in the JSP 1.2 specification.
Error Handling Tag handlers must be able to react to exceptional conditions; for example, a tag handler may choose to throw an exception if a tag attribute is specified with an illegal value. Exceptions, in the form of JspExceptions, can be thrown by tag handlers. Those exceptions are handled by displaying the error page associated with the page in which the tag resides. For example, the JSP code fragment listed below specifies a tag library and an error page. ... <%@ taglib uri='util.tld' prefix='util'%> <%@ page errorPage='error.jsp' %> ...
A hypothetical tag handler for the JSP code fragment listed above might look like this: import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class SomeTagHandler extends TagSupport { private boolean someCondition; ... public int doStartTag() throws JspException { ... if(someCondition == false) throw new JspException("informative message"); ... } }
22
Advanced JavaServer Pages
The error.jsp page referenced above is invoked as a result of the exception thrown by the SomeTagHandler.doStartTag method.
The Tag Package The javax.servlet.jsp.tagext package provides interfaces and classes needed to implement custom tags, in addition to a number of classes that maintain information about tag libraries. The former, which is of interest to custom tag implementers, is the focus of this section. The class diagram in Figure 1-6 depicts shows a class diagram depicting the classes and interfaces involved in the implementation of custom tags. Figure 1-6. Tag Package Class Diagram (javax.servlet.jsp.tagext)
All tag handlers must implement the Tag interface, and nearly all do so by extending either TagSupport or BodyTagSupport (or some subclass thereof). 23
Advanced JavaServer Pages
From their names, it might appear as though BodyTagSupport is for tags that have a body, and TagSupport is for tags that do not; however, that is not the case. Both kinds of tags can have a body, but TagSupport extensions are restricted to ignoring body content or passing it through unchanged. Extensions of BodyTagSupport, on the other hand, can manipulate their body content in any fashion. Tags have access to page information, including the implicit JSP objects, through a page context that is associated with every tag. As can be seen from Figure 1-6, the PageContext class provides a wealth of information about the page in which a tag resides. The Tag Interface The Tag interface defines fundamental tag capabilities; its methods are listed below: void setPageContext(PageContext) void setParent(Tag) int doStartTag() throws JspException int doEndTag() throws JspException void release() Tag getParent()
The methods in the first group of methods listed above are listed in the order in which they are invoked. When a start tag is encountered, the servlet container calls setPageContext and setParent, in that order. doStartTag is called next, followed by a call to doEndTag at the corresponding end tag.12 The doEndTag method is followed by a call to the tag's release method, which will should release tag handler resources. Both doStartTag and doEndTag return integer values that determine the course of action taken by the servlet container when the methods return. Table 1-5 lists valid return values and their meanings. Table 1-5. Return Values for Tag.doStartTag and Tag.doEndTag Method Valid Return Values doStartTag() SKIP_BODY: do not process body EVAL_BODY_INCLUDE: pass through body content unchanged doEndTag() SKIP_PAGE: do not process the page beyond the end tag EVAL_PAGE: process the page after the end tag
All tags have a parent tag, which is null for top-level tags and is the innermost containing tag for nested tags. For example, in the following JSP fragment, the inbetween tag is the parent of the innermost tag, and the outermost tag is the parent of the inbetween tag.
12
If a tag is empty, doEndTag is called immediately after doStartTag.
24
Advanced JavaServer Pages
...
All tags enclosing other tags are referred to as ancestors of the enclosed tags, so in the listing above, the inbetween and outermost tags are ancestors of the innermost tag. The Tag interface provides access to a tag's parent, but the setter methods for the parent and page context are meant for servlet container implementers and normally should not be invoked by JSP developers. Developers rarely implement the Tag interface directly because it is much more convenient to extend TagSupport or BodyTagSupport. See “Body Tag Handlers” for a discussion of tags that extend BodyTagSupport. The TagSupport Class: Ancestors, Values, and IDs Tags that do not manipulate body content or flow of control typically extend the TagSupport class, which in turn implements the Tag interface. TagSupport provides the following functionality: • • •
Locate a tag's ancestors Access a tag's ID Store and retrieve named values
By default, TagSupport extensions ignore their body content and process the JSP page following their end tag. They accomplish this This is accomplished by returning SKIP_BODY from doStartTag and EVAL_PAGE from doEndTag. The TagSupport methods are listed below: // TagSupport implements the Tag interface and adds the following // methods: protected String id; protected PageContext pageContext; static Tag findAncestorWithClass(Tag, Class) Object getValue(String key) void setValue(String key, Object value) void removeValue(String key) Enumeration getValues() String getId() void setId() TagSupport extensions have access to two protected variables: the tag's ID, and the page
context. 25
Advanced JavaServer Pages
The findAncestorWithClass method locates a tag's ancestor of a specified Java class. That static method is passed the tag where the search originates and the ancestor's class. See “Locating Ancestor Tags” for an example of accessing an ancestor tag. The third group of TagSupport methods listed above provide access to a tag's values, which are key/value pairs where the key is a string and the value can be any object. JSP Tip Most Custom Tags Extend TagSupport or BodyTagSupport The TagSupport and BodyTagSupport classes provide enough basic functional ity that they are almost always extended instead of implementing the Tag and BodyTag interfaces directly. Because Java does not support multiple inheritance, existing classes don't have the option to extend TagSupport or BodyTagSupport. Those classes are good candidates to implement Tag or BodyTag directly. In practice, such classes are rare.
Tags with Bodies Any tag can have a body, but only tag handlers that implement the BodyTag interface— hereafter known as body tag handlers—can manipulate their body content.13 Tag handlers that do not implement BodyTag are restricted to either ignoring their body content or passing it through unchanged. The JSP page shown on the right in Figure 1-7 contains a simple tag that displays its body content if the login is successful and the user's role is recognized as 'user'. Otherwise, the tag's body content is ignored. Figure 1-7. An Authentication Tag
13
Tags that specify empty for their body content cannot have a body.
26
Advanced JavaServer Pages
The example shown in Figure 1-7 consists of a login page with a form that forwards to a welcome page—welcome.jsp—if the login is valid.14 That welcome page, which is listed in Example 1-4.a, uses an authenticate tag, which passes its body through unchanged if the user's role is 'user'. Example 1-4.a /welcome.jsp Welcome <%@ taglib uri='/WEB-INF/tlds/authenticate.tld' prefix='security'%>
Welcome <%= request.getUserPrincipal() %>
You are a user
The tag handler for the authenticate tag is listed in Example 1-4.b. Example 1-4.b /WEB-INF/classes/tags/AuthenticateTag.java package tags; import javax.servlet.http.HttpServletRequest; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.TagSupport; public class AuthenticateTag extends TagSupport { private String role = null; public void setRole(String role) { this.role = role; } public int doStartTag() throws JspException { HttpServletRequest request = (HttpServletRequest) pageContext.getRequest(); if(request.isUserInRole(role)) { return EVAL_BODY_INCLUDE; } return SKIP_BODY; } public int doEndTag() throws JspException { return EVAL_PAGE; } } AuthenticateTag extends TagSupport and supports a single property corresponding to the role attribute in the authenticate tag.
14
Actually, the servlet container invokes welcome.jsp; see “Security” for more information concerning login and security in general.
27
Advanced JavaServer Pages
If the user's role is 'user', doStartTag returns EVAL_BODY_INCLUDE, and the body of the tag is included. If the user's role is not 'user', the method returns SKIP_BODY and the body content is ignored. JSP Tip Including Body Content Custom tags that do not implement the BodyTag interface can pass through body content unchanged by returning EVAL_BODY_INCLUDE from doStart Tag. If tags need to manipulate the content of their bodies, they must implement the BodyTag interface, as discussed in “Body Tag Handlers”.
Conclusion Custom tag libraries are JSP's greatest strength, as which will become more apparent as JSP matures and its acceptance grows. Custom tags make JSP easier to use and, therefore, available to a wider audience. As of this writing, there were a number of custom tag library initiatives were underway. The standard tag library, which will become part of a future JSP specification, will have a profound effect on JSP's usability. By providing tags for commonplace functions functionality such as iteration and database access, the standard tag library will broaden JSP's appeal. Many custom tags are simple tags with limited attributes that do not process their body content. Along with an introduction to the JSP tag package, simple tags were have been the focus of this chapter. The next chapter discusses more sophisticated aspects of custom tags, including tags that process the contents of their bodies in some fashion.
28
Advanced JavaServer Pages
Chapter 2. CUSTOM TAG ADVANCED CONCEPTS Topics in this Chapter • • •
•
•
•
Body Tag Handlers Iteration Scripting Variables - Storing Beans in Page Scope - Specifying Scripting Variable Information - Associating a Tag Handler and Scripting Variables - Using Custom Tag IDs Body Content - Understanding How Body Content Works - Generating JavaScript Nested Tags - Locating Ancestor Tags - Sharing Data Conclusion
Sophisticated custom tags often manipulate their body content; for example, a custom tag could wrap its body content in an HTML SELECT tag, so that tag might be used like this— ...
—and end up producing HTML like this:
Besides manipulating body content, many custom tags make beans or scripting variables available to their JSP pages. It's a bit more work for tag handlers to create scripting variables, but the simplification of the tag's JSP page is usually worth the effort. Consider an iterate tag that makes the current item in a collection available as a bean: Item: <%= item %>
versus a scripting variable: Item: <%= item %>
29
Advanced JavaServer Pages
Both implementations of the iterate tag listed above are discussed in “Iteration” and “Scripting Variables” . This chapter begins with a discussion of custom tags that can iterate and manipulate their body content. Tags that make beans and scripting variables available to their JSP pages are the next topic of discussion, followed by an in-depth look at how body content works. This chapter concludes with a discussion of nested tags, including locating ancestor tags and sharing data between tags.
Body Tag Handlers Body tag handlers, meaning tags that implement the BodyTag interface, have two abilities that other tag handlers lack ; they can : • •
They can iterate (see “Iteration”) They can manipulate their body content (see “Body Content”)
The BodyTag Interface The BodyTag interface extends Tag and defines the methods listed below. // BodyTag extends the Tag interface, and adds these methods: void doInitBody() int doAfterBody() void setBodyContent(BodyContent)
The methods defined by the BodyTag interface are the foundation for custom tag iteration and body content manipulation. Servlet containers invoke those methods like this: // A servlet container invokes BodyTag methods like this: if(tag.doStartTag() == EVAL_BODY_TAG) { tag.setBodyContent(bodyContent); ... tag.doInitBody(); do { // evaluate body content } while(tag.doAfterBody() == EVAL_BODY_TAG); } doInitBody is invoked exactly once, whereas doAfterBody can may be invoked repeatedly.
Thus, custom tags can iterate; for example, here's a tag that loops five times: ...
30
Advanced JavaServer Pages
The loop tag could access the from and to attributes in doInitBody to set up a loop. That tag's doAfterBody method would repeatedly return EVAL_BODY_TAG until the loop is finished and SKIP_BODY is returned. Both doInitBody and doAfterBody have access to a tag's body content, but doInitBody is called before that body content has been evaluated for the first time; see “Body Content” for more information about evaluating body content. Table 2-1 lists the valid return values for BodyTag methods that influence flow of control. Table 2-1. BodyTag Method Return Values Method Valid Return Values doStartTag() EVAL_BODY_TAG: evaluate body content and store the result in a BodyContent object SKIP_BODY: do not evaluate body doAfterTag() EVAL_BODY_TAG: reevaluate body content SKIP_BODY: do not reevaluate body content doEndTag()
EVAL_PAGE: process the page after the end tag SKIP_PAGE: do not process the page beyond the end tag
The BodyTagSupport Class Nearly all body tag handlers extend the BodyTagSupport class—an implementation of BodyTag—instead of directly implementing the BodyTag interface. BodyTagSupport is popular because it provides default implementations of both TagSupport and BodyTag by extending the former and implementing the latter. BodyTagSupport defines the methods listed below.
// BodyTagSupport extends TagSupport and // implements BodyTag. It adds the following methods: BodyContent getBodyContent() JspWriter getPreviousOut() BodyTagSupport adds the two methods listed above to those inherited from BodyTag and TagSupport. The getBodyContent method returns a tag's body content, and the getPreviousOut method returns a writer associated with the tag's enclosing tag, or the out
implicit variable if the tag is a top-level tag. See “Understanding How Body Content Works” for more information about those two methods. By default, BodyTagSupport extensions evaluate their body once. That is evidenced by the default return values for the BodyTagSupport methods, listed in Table 2-2.
31
Advanced JavaServer Pages
Method doStartTag()
Table 2-2. Default BodyTagSupport Behavior Returns EVAL_BODY_TAG: evaluate body
doAfterTag()
SKIP_BODY: do not repeat evaluation
doEndTag()
EVAL_PAGE: evaluate the rest of the page
Iteration Body tag handlers have a built-in do-while loop that lets them iterate, as discussed in “The BodyTag Interface” ; for example, the JSP page shown in Figure 2-1 features a custom tag that iterates over the contents of a Java collection. Figure 2-1. An Iterator Tag
The JSP page shown in Figure 2-1 is listed in Example 2-1.a. Example 2-1.a /test.jsp An Iterator <%@ taglib uri='/WEB-INF/tlds/iterator.tld' prefix='it' %> <% java.util.Vector vector = new java.util.Vector(); vector.addElement("one"); vector.addElement("two"); vector.addElement("three"); vector.addElement("four"); %>
32
Advanced JavaServer Pages
Iterating over <%= vector %> ...
Item: <%= item %>
In Example 2-1.a, vector of strings is specified as the iterate tag's collection attribute. That iterate tag iterates over those strings and stores the current string in page scope. In the code fragment listed above, the body of that iterate tag uses to retrieve that string from page scope, and a JSP expression displays it. Example 2-1.b lists the tag handler for the iterate tag. Example 2-1.b /WEB-INF/classes/tags/IteratorTag.java package tags; import java.util.Collection; import java.util.Iterator; import javax.servlet.jsp.JspException; import javax.servlet.jsp.tagext.BodyTagSupport; public class IteratorTag extends BodyTagSupport { private Collection collection; private Iterator iterator; public void setCollection(Collection collection) { this.collection = collection; } public int doStartTag() throws JspException { return collection.size() > 0 ? EVAL_BODY_TAG : SKIP_BODY; } public void doInitBody() throws JspException { iterator = collection.iterator(); pageContext.setAttribute("item", iterator.next()); } public int doAfterBody() throws JspException { if(iterator.hasNext()) { pageContext.setAttribute("item", iterator.next()); return EVAL_BODY_TAG; } else { try { getBodyContent().writeOut(getPreviousOut()); } catch(java.io.IOException e) { throw new JspException(e.getMessage()); } return SKIP_BODY; } } }
33
Advanced JavaServer Pages
Example 2-1.b lists the IteratorTag methods in the order they are invoked. The servlet container calls the setCollection method first and passes it the value of the tag's collection attribute. Next, the servlet container calls doStartTag, which returns EVAL_BODY_TAG only if there are items in the collection. If there are no items, doStartTag returns SKIP_BODY and the servlet container will not invoke doInitBody or doAfterBody. If the collection has items, the servlet container calls doInitBody, which obtains an iterator from the collection, retrieves the collection's first item, and stores it in page scope under the name "item". After the body is initialized and the tag's body content has been evaluated, doAfterBody is invoked. If the collection has more items, doAfterBody retrieves the next item and stores it in page scope. Subsequently, doAfterBody returns EVAL_BODY_TAG, forcing a reevaluation of the tag's body content. If there are no more items in the collection, body content is written out, SKIP_BODY is returned, and the iteration terminates. The iterate tag handler does not implement a release method because that class does not contain any information that needs to be reset in order for an iterate tag to be reused. At first glance, it may seem rather curious that the iterate tag writes its body content to something known as the previous out. You may also wonder why that body content is written out in doAfterBody instead of doEndTag. Both of those questions are answered in “Understanding How Body Content Works” . Figure 2-2 shows a interaction diagram for the IteratorTag class listed in Example 2-1.b. Each item in the collection is stored in page scope by the tag handler, like this:
34
Advanced JavaServer Pages Figure 2-2. IteratorTag Interaction Diagram
pageContext.setAttribute("item", iterator.next());
Those items are subsequently retrieved in the JSP page with , like this:
Notice that the key specified in setAttribute—"item"—must match the id attribute in the useBean tag and the scopes must also be the same. You can eliminate those dependencies by making items available as scripting variables. In that case, there is no need for . Just such an implementation of the IteratorTag class is discussed in “Scripting Variables”. JSP Tip Body Tags Handlers Cannot Automatically Include Their Bodies Tag handlers that do not implement the BodyTag interface can return EVAL_BODY_INCLUDE from doStartTag to alert the JSP container to pass their body content through unchanged.
35
Advanced JavaServer Pages
However, by implementing the BodyTag interface, a tag handler is indicating that it—not the JSP container—is responsible for its body content. Therefore, it is illegal for body tags handlers to return EVAL_BODY_INCLUDE from doStart Tag; instead, body tag handlers must manually pass through their body content, as illustrated in Example 2-1.b.
Scripting Variables It's not uncommon for custom tags to make objects available to their JSP page. For example, the iterate tag discussed in “Iteration” provides access to items in a collection. It's often more convenient for custom tags to make objects available as scripting variables rather than simply making beans available. For example, consider the JSP file listed in Example 2-2.a, which uses an iterate tag that makes the current item in a collection available as a scripting variable. Example 2-2.a /test.jsp Scripting Variable Example <%@ taglib uri='iterator.tld' prefix='it' %> <% java.util.Vector vector = new java.util.Vector(); vector.addElement("one"); vector.addElement("two"); vector.addElement("three"); vector.addElement("four"); %> Iterating over <%=vector %> ...