JSP Custom Tags Agenda Part I Tags Actions

  • Slides: 80
Download presentation
JSP Custom Tags

JSP Custom Tags

Agenda, Part I • • Tags? Actions? Huh? Tag Interface Tag Attributes Tags and

Agenda, Part I • • Tags? Actions? Huh? Tag Interface Tag Attributes Tags and Scripting Variables JSP Custom Tags 2

Agenda, Part II • • • Body Tags Nested Tags Iterating Tags and the

Agenda, Part II • • • Body Tags Nested Tags Iterating Tags and the JSP Container Packaging and Deployment JSP Custom Tags 3

Tags? Actions? Huh? A brief introduction to custom tags. JSP Custom Tags 4

Tags? Actions? Huh? A brief introduction to custom tags. JSP Custom Tags 4

Definitions • A “tag” is an XML element that can be used in a

Definitions • A “tag” is an XML element that can be used in a JSP to incorporate functionality without using scriptlet code. • An “action” is the task performed by the tag. JSP Custom Tags 5

Definitions, cont. • A “tag handler” is basically a Java. Bean that implements the

Definitions, cont. • A “tag handler” is basically a Java. Bean that implements the Tag interface and has property setter methods corresponding to each tag attribute. The JSP container uses this class to process the tag. • A “tag library” or “taglib” is a collection of one or more custom tags. JSP Custom Tags 6

Tag Syntax A custom tag consists of a start tag with a taglib-identifying prefix

Tag Syntax A custom tag consists of a start tag with a taglib-identifying prefix (possibly containing attributes), a body (which can be empty) and an end tag: <log: log. Message category. Name="test. Cat" priority="fatal"> The body contents… </log: log. Message> JSP Custom Tags 7

Alternate Tag Syntax If a tag does not contain any body contents, it can

Alternate Tag Syntax If a tag does not contain any body contents, it can be used like this: <log: log. Message category. Name="test. Cat" priority="fatal" message="test"/> JSP Custom Tags 8

The <%@taglib%> Directive Before using a tag library, you must inform the JSP container

The <%@taglib%> Directive Before using a tag library, you must inform the JSP container about the tag library using the <%@taglib%> directive before the first use of a tag: <%@ taglib uri="/logtags" prefix="log" %> The taglib directive also allows you to specify a prefix to use with a given tag library in its use in your JSP Custom Tags 9

Tag Libraries • One or more tags can be packaged together as a “tag

Tag Libraries • One or more tags can be packaged together as a “tag library. ” • A tag library is described in a tag library descriptor (. TLD) file. • Tags from different libraries can be called from the same JSP, but each must be declared separately with its own <%@taglib%> directive and must be uniquely identified with its own prefix. JSP Custom Tags 10

What Can Tags Do? A custom tag can: • Know about its JSP environment.

What Can Tags Do? A custom tag can: • Know about its JSP environment. • Perform simple tasks. • Performatting or other customization of output. • Introduce variables usable from scriptlets later in the JSP. • Work together either as nested pairs or separately. JSP Custom Tags 11

Why Are Tags Important? • Simplification of complex tasks across multiple JSPs in a

Why Are Tags Important? • Simplification of complex tasks across multiple JSPs in a web application. • Complex code hiding. • Design tool incorporation. • Portable to any JSP container. • Incorporation of functionality into JSPs not using Java as the scripting language. JSP Custom Tags 12

Tag Interface All tag handler classes must implement the Tag interface. JSP Custom Tags

Tag Interface All tag handler classes must implement the Tag interface. JSP Custom Tags 13

Tag Interface public interface Tag { void set. Page. Context(Page. Context pc) void set.

Tag Interface public interface Tag { void set. Page. Context(Page. Context pc) void set. Parent(Tag t) Tag get. Parent() int do. Start. Tag() throws Jsp. Exception int do. End. Tag() throws Jsp. Exception void release() } JSP Custom Tags 14

Tag Interface Constants The Tag interface also includes the following constants: public final static

Tag Interface Constants The Tag interface also includes the following constants: public final static EVAL_BODY_INCLUDE public final static int SKIP_BODY = 0; int = 1; int SKIP_PAGE = 5; int EVAL_PAGE = 6; JSP Custom Tags 15

Return Values int do. Start. Tag() • SKIP_BODY • EVAL_BODY_INCLUDE int do. End. Tag()

Return Values int do. Start. Tag() • SKIP_BODY • EVAL_BODY_INCLUDE int do. End. Tag() • SKIP_PAGE • EVAL_PAGE JSP Custom Tags 16

Tag. Support Class Additions In addition to those methods in Tag, the Tag. Support

Tag. Support Class Additions In addition to those methods in Tag, the Tag. Support class also provides the following: public static final Tag find. Ancestor. With. Class(Tag from, Class klass) public void set. Id(String id) public String get. Id() public void set. Value(String k, Object o) public Object get. Value(String k) public void remove. Value(String k) public Enumeration get. Values() JSP Custom Tags 17

Tag Library Descriptor Files • The JSP container generates code in the JSP implementation

Tag Library Descriptor Files • The JSP container generates code in the JSP implementation class for a tag according to information you set in the TLD. • The TLD is an XML document. • It contains a single <tag/> element for each tag in the library. JSP Custom Tags 18

TLD Elements The TLD has the following elements: • The <taglib/> element contains all

TLD Elements The TLD has the following elements: • The <taglib/> element contains all the other elements in the TLD. • <tlibversion/> • <uri/> • <jspversion/> • <shortname/> JSP Custom Tags 19

TLD Elements, cont. The <taglib/> element contains a <tag/> element for each tag in

TLD Elements, cont. The <taglib/> element contains a <tag/> element for each tag in the library. The possible sub-elements of <tag/> are as follows: • <name/> • <tagclass/> • <teiclass/> • <bodycontent/> • <info/> • <attribute/> JSP Custom Tags 20

TLD Example <? xml version="1. 0" encoding="ISO-8859 -1" ? > <!DOCTYPE taglib PUBLIC "-//Sun

TLD Example <? xml version="1. 0" encoding="ISO-8859 -1" ? > <!DOCTYPE taglib PUBLIC "-//Sun Microsystems, Inc. //DTD JSP Tag Library 1. 1//EN" "http: //java. sun. com/j 2 ee/dtds/web-jsptaglibrary_1_1. dtd"> <taglib> <tlibversion>1. 0</tlibversion> <jspversion>1. 1</jspversion> <shortname>logtags</shortname> <uri></uri> <info>Log 4 J Wrapper Tags</info> <tag> <name>init. Log</name> <tagclass> com. oreilly. jsp. taglibs. logtags. Init. Log. Tag </tagclass> <info>Logging system initialization tag. </info> </taglib> JSP Custom Tags 21

Example Background • Log 4 j is an open source project. • Allows developers

Example Background • Log 4 j is an open source project. • Allows developers to fully control logging of an application. • Fully runtime configurable. • Gentle learning curve. • See http: //jakarta. apache. org/log 4 j/ JSP Custom Tags 22

Example Background • A category is a named entity that categorizes a part of

Example Background • A category is a named entity that categorizes a part of the logging space. • An appender is a logging output destination. • A layout is a formatter for output to an appender. • Priority is the minimum level of messages to be logged for a category. JSP Custom Tags 23

Example Background root category 1 category 2 category 3 JSP Custom Tags appender 1

Example Background root category 1 category 2 category 3 JSP Custom Tags appender 1 appender 2 appender 3 appender 4 24

Tag Attributes A tag can have zero or more attributes. Attribute values provide information

Tag Attributes A tag can have zero or more attributes. Attribute values provide information to the tag’s tag handler class at execution time. JSP Custom Tags 25

Tag Attributes • Each attribute has a corresponding set. Xxxx() method. • Each attribute

Tag Attributes • Each attribute has a corresponding set. Xxxx() method. • Each attribute value included in the tag triggers a call to its corresponding setter. No particular order. • The setter called can be determined by capitalizing the first letter of the attribute name and adding the word “set” as a prefix. JSP Custom Tags 26

Tag Attribute Setters <log: log. Message message=“test msg”/> would call set. Message(String p. Message)

Tag Attribute Setters <log: log. Message message=“test msg”/> would call set. Message(String p. Message) with the String value “test msg” JSP Custom Tags 27

Attribute Data Types • The signature of the attribute setter method in the tag

Attribute Data Types • The signature of the attribute setter method in the tag handler class dictates the data type expected for that attribute. JSP Custom Tags 28

Tag Attribute Setters <log: init. Log override=“true”/> would call set. Override(boolean p. Override) with

Tag Attribute Setters <log: init. Log override=“true”/> would call set. Override(boolean p. Override) with the boolean value true. JSP Custom Tags 29

Tag Attribute Value Conversion Property Type Conversion Method • boolean or Boolean • Boolean.

Tag Attribute Value Conversion Property Type Conversion Method • boolean or Boolean • Boolean. value. Of(String) • byte or Byte • Byte. value. Of(String) • char or Character • String. char. At(int) • double or Double • Double. value. Of(String) • int or Integer • Integer. value. Of(String) • float or Float • Float. value. Of(String) • long or Long • Long. value. Of(String) JSP Custom Tags 30

JSP Container Call Sequence The JSP container call sequence goes as follows: 1. Instantiates

JSP Container Call Sequence The JSP container call sequence goes as follows: 1. Instantiates tag handler. 2. Calls setup methods (set. Page. Context(), etc). 3. Calls each set. Xxxx() method depending on each attribute’s presence in the tag. 4. Calls do. Start. Tag(). 5. Calls do. End. Tag(). JSP Custom Tags 31

Call Sequence Diagram <%@ taglib …%> <log: category priority=“fatal” > 1 -> set. Priority()

Call Sequence Diagram <%@ taglib …%> <log: category priority=“fatal” > 1 -> set. Priority() 2 -> do. Start. Tag() Body contents. </log: category> 3 -> do. End. Tag() JSP Custom Tags 32

Runtime Attribute Values Attribute values can be the result of a runtime expression: <%

Runtime Attribute Values Attribute values can be the result of a runtime expression: <% Collection rooms. Col = new Array. List(); %> <log: log. Collection collection="<%=rooms. Col%>“/> NOTE: This is how to handle other data types. JSP Custom Tags 33

<attribute/> Element There is an <attribute/> element in the TLD for each attribute for

<attribute/> Element There is an <attribute/> element in the TLD for each attribute for each tag: <attribute> <name>message</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> JSP Custom Tags 34

Example Background root category 1 category 2 category 3 JSP Custom Tags appender 1

Example Background root category 1 category 2 category 3 JSP Custom Tags appender 1 appender 2 appender 3 appender 4 35

Example Background A log 4 j category’s “priority” setting specifies the “level” required for

Example Background A log 4 j category’s “priority” setting specifies the “level” required for a message to be logged for a given category: • Debug • Info • Warn • Error JSP Custom Tags • Fatal 36

Example Background Each category has methods corresponding to the various priorities. You use the

Example Background Each category has methods corresponding to the various priorities. You use the appropriate method to log a message of a given priority for the category of interest: • • • my. Cat. debug(“Here’s a message. ”); my. Cat. info(“Here’s a message. ”); my. Cat. warn(“Here’s a message. ”); my. Cat. error(“Here’s a message. ”); my. Cat. fatal(“Here’s a message. ”); JSP Custom Tags 37

Tags and Scripting Variables Tags can introduce variables that can be used later in

Tags and Scripting Variables Tags can introduce variables that can be used later in a JSP scriptlet or another custom tag. JSP Custom Tags 38

Tag-Introduced Scripting Variables This mechanism allows your tag to introduce a variable that you

Tag-Introduced Scripting Variables This mechanism allows your tag to introduce a variable that you can use in scriptlets later in the JSP: <log: category id=“my. Cat”/> <% my. Cat. debug(“Hello!”); %> JSP Custom Tags 39

Introduction of Scripting Variables To have your tag introduce a scripting variable, you must

Introduction of Scripting Variables To have your tag introduce a scripting variable, you must perform the following steps: 1. Somewhere in your tag handler class you must add your variable to one of the JSP scopes. 2. Create a Tag. Extra. Info class for your tag handler class and reference it in your TLD. JSP Custom Tags 40

Creating a Scripting Variable <log: category id=“my. Cat”/> From Tag. Support base class: void

Creating a Scripting Variable <log: category id=“my. Cat”/> From Tag. Support base class: void set. Id(String id) { this. id = id; } From your tag’s do. Start. Tag() method: Category root. Cat = Category. get. Root(); page. Context. set. Attribute(id, root. Cat, Page. APPLICATION_SCOPE); JSP Custom Tags 41

Tag. Extra. Info Class public class Category. Tag. Extra. Info extends Tag. Extra. Info

Tag. Extra. Info Class public class Category. Tag. Extra. Info extends Tag. Extra. Info { public Variable. Info[] get. Variable. Info(Tag. Data data) { return new Variable. Info[] { new Variable. Info( data. get. Attribute. String("id"), "org. apache. log 4 j. Category", true, Variable. Info. AT_END) }; } } JSP Custom Tags 42

Variable Scope Tags can introduce scripting variables with one of three scopes represented by

Variable Scope Tags can introduce scripting variables with one of three scopes represented by the three Variable. Info constants, NESTED, AT_BEGIN, and AT_END. The following diagram explains these three scopes: NESTED AT_BEGIN AT_END do. Start. Tag() do. End. Tag() JSP Custom Tags 43

TLD Addition for TEI Class You must also add a <teiclass/> element to your

TLD Addition for TEI Class You must also add a <teiclass/> element to your TLD: <teiclass> com. foo. Category. Tag. Extra. Info </teiclass> JSP Custom Tags 44

Example Background Simple. Appender. Tag Category. Tag Log. Message. Tag <log: simple. Appender id="my.

Example Background Simple. Appender. Tag Category. Tag Log. Message. Tag <log: simple. Appender id="my. Appender" … /> <log: category id="test. Cat" appender="my. Appender“ … /> <log: log. Message category. Name="test. Cat" … /> JSP Custom Tags 45

Body Tags process their tag body contents. The body contents can contain JSP content.

Body Tags process their tag body contents. The body contents can contain JSP content. JSP Custom Tags 46

Body. Tag Syntax An example call to a body tag: <log: log. Message priority=“fatal”>

Body. Tag Syntax An example call to a body tag: <log: log. Message priority=“fatal”> There was a fatal exception. Time: <%=Calendar. get. Instance(). get. Time()%> </log: log. Message> JSP Custom Tags 47

Body. Tag Interface public interface Body. Tag extends Tag { public final static int

Body. Tag Interface public interface Body. Tag extends Tag { public final static int EVAL_BODY_TAG = 2; void set. Body. Content( Body. Content b); void do. Init. Body() throws Jsp. Exception; int do. After. Body() throws Jsp. Exception; JSP Custom Tags 48 }

Body. Content Class • The tag accesses its body contents through a Body. Content

Body. Content Class • The tag accesses its body contents through a Body. Content object which is set by the JSP container. • Body. Content is a subclass of Jsp. Writer which is used to write content to the response body. • Body. Content also has several methods to manipulate (flush, clear, return as a string) the contents of the body. JSP Custom Tags 49

JSP Container Call Sequence The JSP container call sequence is different for a body

JSP Container Call Sequence The JSP container call sequence is different for a body tag than it is for a simple tag: 1. Instantiates tag handler, calls setup functions. 2. Calls setters. 3. JSP container calls do. Start. Tag(). 4. Calls set. Body. Content(). 5. Calls do. Init. Body(). 6. Calls do. After. Body(). 7. JSP container calls do. End. Tag(). JSP Custom Tags 50

Call Sequence Diagram <%@ taglib …%> <log: log. Message priority=“fatal” > Body contents. </log:

Call Sequence Diagram <%@ taglib …%> <log: log. Message priority=“fatal” > Body contents. </log: log. Message> 1 -> set. Priority() 2 -> do. Start. Tag() 3 ->set. Body. Content() 4 -> do. Init. Body() 5 -> do. After. Body() 6 -> do. End. Tag() JSP Custom Tags 51

Body. Tag Methods • In most cases, it is not necessary to override the

Body. Tag Methods • In most cases, it is not necessary to override the set. Body. Content() method already provided in the Body. Tag. Support base class. • Use the do. Init. Body() method to initialize any variables you need for the do. After. Body(). Again this is rarely needed. • The do. After. Body() is where you will do the real work of your body tag. JSP Custom Tags 52

Body. Content and out • Body. Content allows you to write to a Jsp.

Body. Content and out • Body. Content allows you to write to a Jsp. Writer without interfering with the Jsp. Writer affiliated with the JSP itself. • This allows you to write to an output buffer and decide whether or not to write the buffer’s content to the response stream after you have completed your tag’s processing. JSP Custom Tags 53

The out Variable <%@taglib%> <tag 1> <tag 2>. . . </tag 2> </tag 1>

The out Variable <%@taglib%> <tag 1> <tag 2>. . . </tag 2> </tag 1> out = Jsp. Writer (JSP) out = Body. Content (tag 1) out = Body. Content (tag 2) out = Body. Content (tag 1) out = Jsp. Writer (JSP) JSP Custom Tags 54

The out Variable, Part 1 1. At the beginning of a JSP, the JSP

The out Variable, Part 1 1. At the beginning of a JSP, the JSP container sets the out variable to the Jsp. Writer returned by the page. Context’s get. Out() method. 2. The container experiences body tag 1. a) Creation of Stack of Jsp. Writers. b) Place JSP out on stack. c) Reset out to Body. Content for tag 1. JSP Custom Tags 55

The out Variable, Part 2 1. The JSP container experiences the second body tag,

The out Variable, Part 2 1. The JSP container experiences the second body tag, body tag 2, which is nested inside body tag 1. a) Place out from body tag 1 onto stack. b) Reset out to Body. Content for body tag 2. 2. The JSP container experiences the end of body tag 2. a) Reset out to body tag 1’s Body. Content from stack. JSP Custom Tags 56

The out Variable, Part 3 1. The JSP container experiences the end of body

The out Variable, Part 3 1. The JSP container experiences the end of body tag 1. a) Reset out to the JSP’s Jsp. Writer object from stack. JSP Custom Tags 57

The out Variable <%@taglib%> <tag 1> <tag 2> </tag 1> out = Jsp. Writer

The out Variable <%@taglib%> <tag 1> <tag 2> </tag 1> out = Jsp. Writer (JSP) out = Body. Content (tag 1) out = Body. Content (tag 2) out = Body. Content (tag 1) out = Jsp. Writer (JSP) JSP Custom Tags 58

Output of Body Content If your tag’s body content should be output into the

Output of Body Content If your tag’s body content should be output into the response body, you must use the write. Out() method of the Body. Content Object (from do. End. Tag()): if (null != body. Content) { Jsp. Writer last. Out = body. Content. get. Enclosing. Writer(); body. Content. write. Out(last. Out); } JSP Custom Tags 59

Return Values int do. Start. Tag() • SKIP_BODY • EVAL_BODY_INCLUDE (NOT ALLOWED) • EVAL_BODY_TAG

Return Values int do. Start. Tag() • SKIP_BODY • EVAL_BODY_INCLUDE (NOT ALLOWED) • EVAL_BODY_TAG int do. After. Body() • EVAL_BODY_TAG • SKIP_BODY int do. End. Tag() • SKIP_PAGE • EVAL_PAGE JSP Custom Tags 60

Body. Tag TLD Features To instruct the JSP container that the current tag is

Body. Tag TLD Features To instruct the JSP container that the current tag is a body processing tag, you must set the <bodycontent/> element: • empty • tagdependent • JSP (default) JSP Custom Tags 61

Nested Tags Nested actions are tags that are processed as part of the tag

Nested Tags Nested actions are tags that are processed as part of the tag body content. Typically these tags rely on certain properties of the surrounding tag. JSP Custom Tags 62

Nested Tags Nested tags can take two forms: • Nested tags may or may

Nested Tags Nested tags can take two forms: • Nested tags may or may not use a variable introduced by the surrounding tag. • Some nested tags rely on their surrounding tags for some information without which it will not function. JSP Custom Tags 63

Iterating Tags can iterate over their bodies repeatedly until a condition is met. JSP

Iterating Tags can iterate over their bodies repeatedly until a condition is met. JSP Custom Tags 64

Iterating Tags Iterating tags differ from other body tags: • Iterating tags return EVAL_BODY_TAG

Iterating Tags Iterating tags differ from other body tags: • Iterating tags return EVAL_BODY_TAG from do. After. Body() until some condition is met. JSP Custom Tags 65

Example Background Log. Collection. Tag: • Takes a collection and the name of a

Example Background Log. Collection. Tag: • Takes a collection and the name of a message variable. • Iterates through the collection and creates a logging message for each object. • Creates a scripting variable to be used by a nested Log. Message. Tag. • Repeats until the end of the collection. JSP Custom Tags 66

Packaging and Deployment Tag libraries are typically packaged together into a JAR file and

Packaging and Deployment Tag libraries are typically packaged together into a JAR file and deployed to a JSP container. JSP Custom Tags 67

Deployment: Three Options To deploy and use your tag library, you have three options:

Deployment: Three Options To deploy and use your tag library, you have three options: 1. Set the uri attribute of the taglib directive to the location of the TLD file. 2. Set the uri attribute of the taglib directive to the location of the JAR file. 3. Set the uri attribute of the taglib directive to a symbolic name set in the web. xml file. JSP Custom Tags 68

Option One 1. Deploy your files: WEB-INF/ /classes *. class /tlds logtags. tld 2.

Option One 1. Deploy your files: WEB-INF/ /classes *. class /tlds logtags. tld 2. In your JSP, set the uri attribute of the taglib directive to the location of the TLD file: <%@ taglib uri=“/WEB-INF/tlds/logtags. tld” prefix=“log” %> JSP Custom Tags 69

Option Two – File Setup To create a JAR file, first arrange the files

Option Two – File Setup To create a JAR file, first arrange the files in the following folder organization: META-INF/ taglib. tld com/ oreilly/ jsp/ taglibs/ logtags/ *. class JSP Custom Tags 70

Option Two – JAR Creation Change to the previously created folder (containing the META-INF

Option Two – JAR Creation Change to the previously created folder (containing the META-INF and com folders) and execute the following: jar cvf logtags. jar META-INF com JSP Custom Tags 71

Option Two - Deployment Copy the JAR file to the lib sub-directory of WEB_INF

Option Two - Deployment Copy the JAR file to the lib sub-directory of WEB_INF folder for your JSP container: WEB-INF/ lib/ logtags. jar JSP Custom Tags 72

Option Two - Use In your JSP, set the uri attribute of the taglib

Option Two - Use In your JSP, set the uri attribute of the taglib directive to the location of your JAR file: <%@ taglib uri=“/WEB-INF/lib/logtags. jar” prefix=“log” %> JSP Custom Tags 73

Option Three – Symbolic Name You can also create a symbolic name in a

Option Three – Symbolic Name You can also create a symbolic name in a taglib element in your web application’s web. xml file: <web-app> <taglib-uri> /logtags </taglib-uri> <taglib-location> /WEB-INF/tlds/logtags. tld </taglib-location> </taglib> </web-app> JSP Custom Tags 74

Option Three – Symbolic Name Use To use a symbolic name in your JSP,

Option Three – Symbolic Name Use To use a symbolic name in your JSP, just set the uri attribute of the taglib directive to the symbolic name: <%@ taglib uri=“/logtags” prefix=“log” %> JSP Custom Tags 75

Review To create a tag library: 1. Create a tag handler class that implements

Review To create a tag library: 1. Create a tag handler class that implements Tag or Body. Tag. 2. In the tag handler class create setters for each attribute your tag will use. 3. In the tag handler class place processing in the do. Start. Tag, do. End. Tag, do. Init. Body and do. After. Body methods as needed. JSP Custom Tags 76

Review, cont. 4. Create a subclass of Tag. Extra. Info if your tag introduces

Review, cont. 4. Create a subclass of Tag. Extra. Info if your tag introduces a scripting variable for use later in the JSP. 5. In the Tag. Extra. Info subclass, add code for each variable introduced. 6. Compile your tag. 7. Create a tag library descriptor (TLD) file. 8. In the TLD file, add a <tag/> element for each tag in your library. JSP Custom Tags 77

Review, cont. 9. Inside each <tag/> element, set the appropriate sub-elements for attributes, etc.

Review, cont. 9. Inside each <tag/> element, set the appropriate sub-elements for attributes, etc. 10. Optionally create a JAR file using your tag handler classes and your TLD file. 11. Deploy your tag library into the appropriate subfolder of your web application. JSP Custom Tags 78

Review, cont. 12. For each tag library you wish to use in your JSP,

Review, cont. 12. For each tag library you wish to use in your JSP, use the <%@taglib%> directive to instruct the JSP container how to process your tags. 13. Use your tags in the JSP, being careful to include required attributes, etc. 14. Enjoy the accolades of those around you for being so smart as to use tag libraries! JSP Custom Tags 79

That’s all there is! Questions? JSP Custom Tags 80

That’s all there is! Questions? JSP Custom Tags 80