|
<taglib:tutorial lesson="5">
In this lesson, we will look at the TEI class and what it can do for us as Tag developers.
1 Lesson 5, Writing TEI Classes
In this lesson, we will look at the TEI class and what it can do for us as Tag developers. We will then write a simple iteration BodyTag that will dynamically add new elements to the Page scope.
Throughout this lesson, We will continue to use the setup previously defined for this tutorial. You should feel comfortable about the Tag interface, the TagSupport class and BodyTags before starting on this lesson.
2 What is a TEI class?
In order to add new elements dynamically and make these available to other actions (referred to as defining scripting variables) in the same page the Container wants us to specify what elements our Tags will add and for how long they will be available. This information is gathered in the TagExtraInfo class displayed below.
 The TagExtraInfo class
The reason for this information not being part of the Taglib descriptor is because it was considered a hindrance to the complexity of logic that might have been wanted. Whenever a page is parsed during translation-time and a Tag with an associated TEI is encountered, the Container uses the TEI methods in the following way:
 Figure 2: A sequence diagram showing the interaction between the Container and a TagExtraInfo instance.
-
The Container calls the setTagInfo() method with a TagInfo object. We can use this information in the following methods.
-
The Container calls the isValid() method with a TagData object. This method should return true if the Tag usage is considered valid.
-
The Container calls the getVariableInfo() method with a TagData object set, as before, with data about the Tag in use. This method should return an array of VariableInfo objects.
The TagInfo object that is set by the Container with the setTagInfo() method looks like in Figure 3 below.
 Figure 3: The TagInfo class
This class holds a number of methods that are useful. Amongst these are getBodyContent() that will tell us what the body of the Tag will contain. It will return one of the three constants BODY_CONTENT_JSP, BODY_CONTENT_EMPTY or BODY_CONTENT_TAG_DEPENDENT. The getTagName() method will give us the name of the Tag in use while getTagClassName() method will give us the name of the Tag class. The getAttributes() will give us an array of the TagAttributeInfo class (might be null). These objects holds information about the attributes the Tag accepts, such as if runtime expressions are accepted, their names, and so on. As you can surely understand, this class is well worth a visit to the JSP API or the JSP 1.1 specification.
After that the container has called the setTagInfo() method, it calls the isValid() method with a TagData object. This class looks like in Figure 4 below:
 Figure 4: The TagData class
This class also holds a number of useful methods, such as the getAttribute() method, that will return the specified attributes value object. If this attribute is a runtime value, an REQUEST_TIME_VALUE object will be returned instead, to inform us that this value is not accessible at translation-time. It will return null if the value is not set. Another useful method is the getId() method, that returns the id attribute or null if its not set.
As previously said, we use the isValid() method to define complex logic for deciding if the Tag usage is considered correct or not. For example, we could set up roles so that if a Tag accepts two optional attributes, at least one of them has to be set in order for our Tag to perform.
After that the isValid() method has been called, the Container calls the getVariableInfo() method in order to collect information about any scripting variables that our Tag wants to define. The method returns an array of VariableInfo objects that looks like in Figure 5 below.
 Figure 5: The VariableInfo class
These VariableInfo objects holds the following information about the variable name and type, if its a new variable or an previously existing one, and finally the scope, which tells the Container for how long this variable should be available.
The valid value for scope is one of the following:
-
NESTED which means that the variable will be available from the start of the Tag to the end of the Tag
-
AT_BEGIN which means that the variable will be available from the start of the Tag to the end of the JSP page
-
AT_ENDwhich means that the variable will be available from the end of the Tag to the end of the JSP page
So normally what we do in the getVariableInfo() method is to create an array of VariableInfo objects that we return to the Container.
With this I will end the introduction of the TEI classes. If you want to learn more or get a deeper understanding about the TEI class, consult the JSP API or the JSP 1.1 specification.
3 Writing the Iterate Tag
We will now create the BodyTag that will give us the iteration functionality that we want. The Iterate Tag will extend the BodyTagSupport class like shown in the image below.
 Figure 6: The IterateTag class
The BodyTagSupport class is an implementation of the BodyTag class that we can utilise when writing BodyTags, just like we used the TagSupport class in lesson 2.
-
In the '/WEB-INF/classes/com/acme/tag/' directory, create a new class called 'IterateTag.java' with the following content:
package com.acme.tag;
import javax.servlet.jsp.*; import javax.servlet.jsp.tagext.*; import java.util.*;
public class IterateTag extends BodyTagSupport { private String name; private Iterator iterator; private String type;
|
Listing 1: Define the variables that the tag will need.
Above we define the three variables that our BodyTag will need.
-
Define a constructor:
public IterateTag() { super(); }
|
Listing 2: Adding the constructor
-
Now, add the following method:
public void setCollection(Collection collection) { if(collection.size()>0) iterator = collection.iterator(); }
|
Listing 3: Adding a method to set the Collection
Our Container will call the method above to set the Collection passed to the BodyTag. We do a check to see that the Collection passed in really hold any elements.
-
After that, we should have this method:
public void setName(String name) { this.name=name; }
|
Listing 4: Adding a method to set the name of the scripting variable.
The method above will be called by the Container to set the name we will use for the scripting variables we will create from the objects returned from the Collection.
-
Continue with the following:
public void setType(String type) { this.type=type; }
|
Listing 5: Adding a mehtod to set the type of the scripting variable.
This method will be used by the container to set the class type of the scripting variable.
-
Add the following method:
public int doStartTag() throws JspTagException { if(iterator==null) { return SKIP_BODY; } return addNext(iterator); }
|
Listing 6: Overriding the doStartTag method
The method above gets called at the start of the Tag. It first makes sure that we have an Iterator, and then calls the addNext() method that will add the next element of the Iterator to the PageContext, using the name previously passed in as a parameter to the Tag. The method will return EVAL_BODY_TAG if the Iterator contained another element. Otherwize, it will return SKIP_BODY.
-
Now add the following method:
public int doAfterBody() throws JspTagException { return addNext(iterator); }
|
Listing 7: Overriding the doAfterBody method
The method above gets called after every evaluation of the body of the Tag and will call the previously mentioned addNext() method.
-
Continue with the following method:
public int doEndTag() throws JspTagException { try { if(bodyContent != null) bodyContent.writeOut(bodyContent.getEnclosingWriter()); } catch(java.io.IOException e) { throw new JspTagException("IO Error: " + e.getMessage()); } return EVAL_PAGE; }
|
Listing 8: Overriding the doEndTag method
The method above gets called at the end of the tag. It returns the BodyContent to the JSP page and then tells the JSP container to continue to evaluate the JSP page.
-
Finish off the BodyTag with the following method:
protected int addNext(Iterator iterator) throws JspTagException { if(iterator.hasNext()) { pageContext.setAttribute(name,iterator.next(),PageContext.PAGE_SCOPE); return EVAL_BODY_TAG; } else { return SKIP_BODY; } } }
|
Listing 9: Add method to check for more elements in the Iterator
The method above will check if there are any more elements in the Iterator. If there are more elements, the next element will be added to the page scope with the given name and the EVAL_BODY_TAG value will be returned, telling the Container that the body of the Tag should be evaluated again. If the Iterator holds no more elements, it will return SKIP_BODY and the Container will continue with the doEndTag() method.
|
By now, your IterateTag.java should look like this. Store the sourcecode in the /WEB-INF/classes/com/acme/tag/ directory as IterateTag.java
|
-
Compile the Tag.
4 Creating the TEI class
With our BodyTag in place, lets get started on the TEI class that we will couple it with.
 Figure 7: The IterateTEI class
Now, as we don't have any complex logic for deciding if the BodyTag usage is valid or not, we don't need to use the isValid() method. We will just override the getVariableInfo() method to specify or scripting variable.
-
In the '/WEB-INF/classes/com/acme/tag/' directory, create a new class called 'IterateTEI.java' with the following content:
package com.acme.tag;
import javax.servlet.jsp.tagext.*;
public class IterateTEI extends TagExtraInfo { public IterateTEI() { super(); }
public VariableInfo[] getVariableInfo(TagData data) { return new VariableInfo[] { new VariableInfo ( data.getAttributeString("name"), data.getAttributeString("type"), true, VariableInfo.NESTED ), }; } }
|
Listing 10: The IterateTEI source listing.
Above, we use the TagData object to retrieve the value of the name and type parameters that was passed into the IterateTag. We also tell the Container to treat the added variable as if it was a new variable, and that the variable should be available from the start of the Tag to the end.
|
By now, your IterateTEI.java should look like this. Store the sourcecode in the /WEB-INF/classes/com/acme/tag/ directory as IterateTEI.java
|
-
Compile the TEI class.
5 Update the Tag Library Descriptor
We should now describe our new Tag together with its TEI in the previously created Tag library descriptor.
-
Open your 'taglib.tld' from your '/WEB-INF' directory with your favourite editor.
-
Add the following description for your new Tag and its TEI:
<tag> <name>iterate</name> <tagclass>com.acme.tag.IterateTag</tagclass> <teiclass>com.acme.tag.IterateTEI</teiclass> <bodycontent>JSP</bodycontent> <info>A simple Iterator</info> <attribute> <name>collection</name> <required>true</required> <rtexprvalue>true</rtexprvalue> </attribute> <attribute> <name>name</name> <required>true</required> </attribute> <attribute> <name>type</name> <required>true</required> </attribute> </tag>
|
Listing 11: The new Tag and TEI descriptor entries.
Above, we describe that the IterateTag uses the IterateTEI. You should recognise the rest by now.
|
Your taglib.tld should now look like this. Store the descriptor in the /WEB-INF/ directory as taglib.tld.
|
6 Creating some Beans
Now, before we try out the Iterator, lets create two beans that we can use for demonstration purpose.
 Figure 8: Sample Beans
You can get the source for the Company bean here, and the source for the Contact bean is here.
Make sure that you compile the beans and put them in your '/WEB-INF/classes/com/acme/bean/' directory.
7 Presenting the new Tag
In order to use your new Tag so that you can see your TEI in action, you will need a JSP page that uses it.
-
Create a new file called 'iterate.jsp' in your '/taglib-tutorial-web/' directory with the following content:
<%@ taglib uri="mytags" prefix="mt" %> <% com.acme.bean.Company comp=new com.acme.bean.Company(); comp.setName("Acme"); com.acme.bean.Contact contact1=new com.acme.bean.Contact(); contact1.setName("Bar, Foo"); contact1.setEmail("foo.bar@acme.com"); contact1.setPhone("+46 8 12 34 56"); contact1.setComment("Excellent with HTML"); com.acme.bean.Contact contact2=new com.acme.bean.Contact(); contact2.setName("Doe, Joe"); contact2.setEmail("joe.doe@acme.com"); contact2.setPhone("+46 8 65 43 21"); contact2.setComment("Very good at XML"); com.acme.bean.Contact contact3=new com.acme.bean.Contact(); contact3.setName("Doe, Jane"); contact3.setEmail("jane.doe@acme.com"); contact3.setPhone("+46 8 65 43 21"); contact3.setComment("JSP pro"); comp.addContact(contact1); comp.addContact(contact2); comp.addContact(contact3); request.setAttribute("company",comp); %>
|
Listing 12: A sample JSP page.
The scriptlet above will create a new Company with a number of Contacts and add this Company to the request.
-
Continue with the following code:
<HTML> <HEAD> <TITLE>My Connections</TITLE> </HEAD> <BODY BGCOLOR="#FFFFFF"> <jsp:useBean id="company" scope="request" type="com.acme.bean.Company"/> <FONT SIZE=+2>My connections at <B> <jsp:getProperty name="company" property="name"/> </B> </FONT> <HR> <TABLE> <TR> <TD>Name</TD> <TD>Phone</TD> <TD>Email</TD> <TD>Comment</TD> </TR>
|
Listing 13: A sample JSP page (continued).
The Code above will first get the company from the request and then print out a header for the output.
-
Continue with the following code:
<mt:iterate name="contact" collection="<%= company.getContacts()%>" type="com.acme.bean.Contact"> <TR> <TD> <jsp:getProperty name="contact" property="name"/> </TD> <TD> <jsp:getProperty name="contact" property="phone"/> </TD> <TD> <jsp:getProperty name="contact" property="email"/> </TD> <TD> <jsp:getProperty name="contact" property="comment"/> </TD> </TR> </mt:iterate>
|
Listing 14: A sample JSP page (continued)
The code above will iterate through the collection in the Company class and for each contact found print out some information for it.
As you can see, the Tag (with the help of our TEI class) adds the class Contact to the page scope for the duration of the Tag.
-
Finish the JSP with the following lines:
</TABLE> <HR> </BODY> </HTML>
|
Listing 15: A sample JSP page (continued)
If you've typed in this correctly, your iterate.jsp should look like this.
8 Using your new Tag
We are now ready to use our Tag.
|
Open the URL
http://localhost/taglib/iterate.jsp in a normal web browser. Hopefully the result looks like
this.
|
Continue with lesson 6, "Accessing environment entries".
Copyright ©
2005 IronFlare AB
|