<taglib:tutorial lesson="16"/>

In this part of the tutorial, we will look at how a fragment of a JSP page can be passed to a SimpleTag as an attribute

1 Lesson 16, Fragments as attributes

    In this part of the tutorial we will look closer at the JspFragment as an attribute type and how this can be utilized by SimpleTags to allow JSP fragments to be given to the Simple Tag handler without resolving to using a Tag body. We will continue to use the setup previously defined for this tutorial. During this lesson, you will implement a SimpleTag that will accept fragments of JSP as attributes and then write a view that can be used to try it out.

2 What is a JspFragment?

    The abstract JspFragment interface was added in JSP 2.0 and represents a piece of a JSP page that was translated into a JspFragment during the translation phase and then given to the Tag handler either as its body or as an attribute of type JspFragment. Notice though, that such attributes has to be given using the <jsp:attribute/> standard action as some containers wont allow you to use the shortform when the attribute is to be dealt with as a JspFragment.

    When a SimpleTag has a body, the container will set the JspFragment through the setJspBody() method of the SimpleTag interface.

    When a SimpleTag has an attribute defined as being of the type JspFragment the Container will translate the value of the attribute into a JspFragment which is then available to the SimpleTag interface.

    The JspFragment can be evaluated any number of times as well as be passed along to other Simple Tag Handlers. It can even be parameterized by setting PAGE-scoped attributes in the JspContext associated with the fragment (the JspContext is associated with the surrounding page by the Container before passing it to the Handler).

    As can be seen in figure 1 below, the JspFragment only holds two methods:


    Figure 1. The abstract JspFragment class

    The invoke(java.io.Writer out) method can be used by the Simple Tag Handler to execute the fragment as many times as wanted. All output will be directed to the supplied Writer. If the Writer passed in is null, then the output of the fragment will be directed to the Writer returned by its associated JspContexts getOut() method. The method may throw a JspException if an error occurs when the JspFragment is invoked. It can also throw a SkipPageException if the page that directly (or indirectly) invoked the Simple Tag Handler that in its turn runned the invoke method is to stop evaluating the fragment. This Exception is also thrown by the Container if a Simple Tag Handler that might be part of the fragment throws SkipPageException or a Classic Tag Handler returned Tag.SKIP_PAGE. A third Exception that may be thrown is the java.io.IOException that is thrown if an error while writing to the Writers stream.

    The getJspContext() method is used to get a JspContext, which we briefly discussed here.

    The body of a Simple Tag Handler, represented by a JspFragment, may not include any Scriptlets. So the <body-content> in the Taglibrary descriptor may not be "JSP" for a Simple Tag Handler.

    With this ends the introduction of the JspFragment interface. If you want to learn more or gain a deeper understanding about the JspFragment interface, consult the JSP 2.0 API or the JSP 2.0 specification.

3 Creating the SimpleIteratorTag

    To demonstrate how JspFragments can be passed in as attribute we are going to write yet another Iterator, but this time the page author will be given the option to process the entries differently depending upon if the current count is even or odd, as well as specifying header, footer and processing of empty collections.


    Figure 2. The SimpleIteratorTag

    The emptyJspFragment will, if specified, be evaluated instead of the SimpleBodys body if the collection attribute holds an empty Collection or null.

    The oddJspFragment will, if specified, be evalutated instead of the SimpleBodys body every other iteration (when the current count is an odd number).

    The evenJspFragment will, if specified, behave just like the odd attribute but when the current count is an even number.

    The header and footerJspFragments will, if specified, be evaluated before (header) or after (footer) the non empty collection is itterated.

    The actual logic will be a number of conditional checks where one or the other of the JspFragments will be invoked depending upon the outcome. The passed in JspFragments are treated just as the body of the SimpleTag, as will become obvious when looking at the actual implementation.

    1. In the '/WEB-INF/classes/com/acme/tag/' directory, create a new class called 'SimpleIteratorTag.java' with the following content:


      package com.acme.tag;

      import javax.servlet.jsp.tagext.SimpleTagSupport;
      import javax.servlet.jsp.tagext.JspFragment;
      import javax.servlet.jsp.JspException;
      import java.util.Collection;
      import java.util.Iterator;
      import java.io.IOException;

      public class SimpleIteratorTag extends SimpleTagSupport
      {
      private Collection collection;
      private JspFragment header;
      private JspFragment footer;
      private JspFragment empty;
      private JspFragment even;
      private JspFragment odd;
      private String type;
      private String id;
      private String counter;
      Listing 1: The start of the SimpleIteratorTag

      In the listing above, we define the variables that our SimpleTag will need, of which most will be used as attribute fields.

    2. Continue by adding accessor methods for these fields as in the listing below:


      public SimpleIteratorTag(){}

      public String getId()
      {
      return id;
      }

      public void setId(String id)
      {
      this.id = id;
      }

      public String getCounter()
      {
      return counter;
      }

      public void setCounter(String counter)
      {
      this.counter = counter;
      }

      public String getType()
      {
      return type;
      }

      public void setType(String type)
      {
      this.type = type;
      }

      public Collection getCollection()
      {
      return collection;
      }

      public void setCollection(Collection collection)
      {
      this.collection = collection;
      }

      public JspFragment getHeader()
      {
      return header;
      }

      public void setHeader(JspFragment header)
      {
      this.header = header;
      }

      public JspFragment getFooter()
      {
      return footer;
      }

      public void setFooter(JspFragment footer)
      {
      this.footer = footer;
      }

      public JspFragment getEmpty()
      {
      return empty;
      }

      public void setEmpty(JspFragment empty)
      {
      this.empty = empty;
      }

      public JspFragment getEven()
      {
      return even;
      }

      public void setEven(JspFragment even)
      {
      this.even = even;
      }

      public JspFragment getOdd()
      {
      return odd;
      }

      public void setOdd(JspFragment odd)
      {
      this.odd = odd;
      }
      Listing 2: A constructor and a bunch of accessors

    3. Now its time to add the doTag method that will hold all the conditional and iterative operations that our SimpleTag will require. Add the following:


      public void doTag() throws JspException, IOException
      {
      if(getCollection()!=null && getCollection().size()>0)
      {
      if(header!=null)
      header.invoke(null);
      Listing 3: If the Collection contains items any specified header fragments should be invoked.

      In the listing above, we check if the Collection contains any items. If so, we invoke any specified headerJspFragment.

    4. Continue with the following:


      Iterator items=getCollection().iterator();
      int count=0;
      while(items.hasNext())
      {
      count++;
      this.getJspContext().setAttribute(id,items.next());
      if(counter!=null)
      this.getJspContext().setAttribute(counter,new Integer(count));
      Listing 4: Iterating through the items of the Collection.

      Above, we create an Iterator on the collection that we then iterate through. For each iteration, we add the current item to the JspContext of the SimpleTag with the name specified in the id attribute, which will be shared by the various JspFragments involved.

      If a counter attribute has been specified, we also add the current iteration count to the JspContext of the SimpleTag using the name specified in the counter attribute.

    5. Continue with the following:


      if(count%2==0 && even!=null)
      even.invoke(null);
      else if (count%2!=0 && odd!=null)
      odd.invoke(null);
      else
      this.getJspBody().invoke(null);
      }
      Listing 5: Calculating which fragments to invoke

      In the code section above we invoke the even fragment on every even iteration count and the odd one on every odd count, provided that these attributes has been specified, otherwize the normal body of the SimpleTag will be invoked.

    6. Continue with the following:


      if(footer!=null)
      footer.invoke(null);
      Listing 6: If the Collection contained items any specified footer fragment should be invoked.

      In the listing above, we invoke the footerJspFragment if the Collection wasn't emtpy.

    7. End the SimpleTag with the following code:


      }
      else
      {
      if(empty!=null)
      empty.invoke(null);
      }
      }
      }
      Listing 7: Handle empty Collection and end the class

      In the code section above we invoke the emtpyJspFragment if this attribute has been specified and the specified collection Collection held no items.

      By now, your SimpleIteratorTag.java should look like this. Store the sourcecode in the /WEB-INF/classes/com/acme/tag/ directory as SimpleIteratorTag.java

    8. Compile the SimpleTag. If needed, details are given here. The result should be a file named SimpleIteratorTag.class in the /WEB-INF/classes/com/acme/tag/ directory.

4 Creating the SimpleIteratorTEI

    As our SimpleTag will be adding objects of unknown types to the JspContext we need to provide our SimpleTag with a accompanying TEI class. If you have already gone through lesson 10 you will notice that this TEI will be very similar to the IteratorTEI we developed then.

    As this is our first TEI for JSP 2.0, it is worth mentioning that the Expression Language of JSP 2.0 does not require attributes to be addes as scripting variables to the JSP pages in order to find and use them, while standard actions and custom handlers (tags) still does.


    Figure 3. The SimpleIteratorTEI

    The major difference between the TEI developed in lesson 10 and this one will be that this TEI will define two scripting variables, namely:

      id - the name the iterated item will be added to the PAGE_SCOPE as. The type will be retrieved from the type attribute which we can get at thorugh the TagData, just as with the id attribute.

      counter - the name the iteration counter will be added to the PAGE_SCOPE as.

    1. In the '/WEB-INF/classes/com/acme/tag/' directory, create a new class called 'SimpleIteratorTEI.java' with the following content:


      package com.acme.tag;

      import javax.servlet.jsp.tagext.VariableInfo;
      import javax.servlet.jsp.tagext.TagData;
      import javax.servlet.jsp.tagext.TagExtraInfo;

      public class SimpleIteratorTEI extends TagExtraInfo
      {

      public SimpleIteratorTEI()
      {
      super();
      }

      public VariableInfo[] getVariableInfo(TagData data)
      {
      return new VariableInfo[]
      {
      new VariableInfo(
      data.getAttributeString("id"),
      data.getAttributeString("type"),
      true,
      VariableInfo.NESTED
      ),
      Listing 8: The SimpleIteratorTEI

      Above, we use the TagData object to retrieve the value of the id and type attributes that was passed into the SimpleIteratorTag. 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 SimpleTag to the end.

    2. Continue with the following:


      new VariableInfo(
      data.getAttributeString("counter"),
      data.getAttributeString("java.lang.Integer"),
      true,
      VariableInfo.NESTED
      )
      };
      }
      }

      Listing 9: the SimpleIteratorTEI continued

      Above, we declare a second VariableInfo that uses the TagData object to retrieve the value of the counter attribute as the name of the variable that is of the type java.lang.Integer and that the added variable should be a new variable that should be available from the start of the SimpleTag to the end.

      Your SimpleIteratorTEI.java should look like this. Store the sourcecode in the /WEB-INF/classes/com/acme/tag/ directory as SimpleIteratorTEI.java

    3. Compile the TEI-class. If needed, details are given here. The result should be a file named SimpleIteratorTEI.class in the /WEB-INF/classes/com/acme/tag/ directory.

5 Update the Tag Library Descriptor

    We should now describe our new Tag together with its accompanying TEI in the Tag library descriptor we created in lesson 15.

    1. Open your 'taglib2.0.tld' from your '/WEB-INF' directory with your favourite editor.

    2. Edit the TagLibrary descriptor and add the SimpleIteratorTag as in listing 6 below.


      <tag>
      <name>iterate</name>
      <tag-class>com.acme.tag.SimpleIteratorTag</tag-class>
      <tei-class>com.acme.tag.SimpleIteratorTEI</tei-class>
      <body-content>scriptless</body-content>
      <attribute>
      <name>id</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
      <name>type</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
      <name>collection</name>
      <required>true</required>
      <rtexprvalue>true</rtexprvalue>
      <type>java.util.Collection</type>
      </attribute>
      <attribute>
      <name>counter</name>
      <rtexprvalue>true</rtexprvalue>
      </attribute>
      <attribute>
      <name>header</name>
      <fragment>true</fragment>
      </attribute>
      <attribute>
      <name>footer</name>
      <fragment>true</fragment>
      </attribute>
      <attribute>
      <name>even</name>
      <fragment>true</fragment>
      </attribute>
      <attribute>
      <name>odd</name>
      <fragment>true</fragment>
      </attribute>
      </tag>
      Listing 9: The modified Taglibrary descriptor.

      Notice the lack of the counter variable in the tag descriptor above. Although this Integer would make an excellent example of using the variable declaration in the TLD, the specification forbids us to have both TEI classes that returns a VariableInfo[] of a size larger than zero and having a variable-element in the TLD. For more information about the variable tag, see lesson 12.

      Also notice that body-content cannot be set to JSP as SimpleTags cannot contain Scriptlets.

    By now, your taglib2.0.tld should look like this. Store the descriptor in the /WEB-INF/ directory as taglib2.0.tld.

6 Creating some Beans

    If you haven't already gone through lesson 10, you should now do so now as we will reuse the beans created there (though initially created in lesson 5.

    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 if you haven't got them there already.

7 Presenting the new Tag

    In order to use your new SimpleTag you will need a JSP page that uses it.

    1. If you haven't already gone through lesson 5, you should do so now as we will modify the JSP page created there.

      Download the source for the original JSP page from here and save it as odd.jsp in the taglib-tutorial-web/ directory.

    2. Open odd.jsp with your favourite editor and change the Tag Library reference to utilize our new Tag Library:


      <%@ taglib uri="mytags" prefix="mt" %>
      Listing 10: The line to change.

      so that it looks like in listing 11 below instead:


      <%@ taglib uri="http://www.orionserver.com/tutorials/mytags3.jar" prefix="mt" %>
      Listing 11: The changed line.

    3. Change the iteration tag usage to take advantage of our new attributes:


      <mt:iterate name="contact" collection="<%= company.getContacts()%>" type="com.acme.bean.Contact">
      Listing 12: The line to change.

      so that it looks like in listing 13 below instead:


      <mt:iterate
      id="contact"
      collection="${company.contacts}"
      counter="counter"
      type="com.acme.bean.Contact"
      >

      Listing 13: The new lines.

    4. As some containers refuses to deal with JspFragment in shortform, we will specify our JspFragments in the longer form as shown in listing 14 below:


      <jsp:attribute name="header">
      <TABLE><TR><TD>Name</TD><TD>Phone</TD><TD>Email</TD><TD>Comment</TD></TR>
      </jsp:attribute>
      <jsp:attribute name="footer">
      </TABLE>
      </jsp:attribute>
      <jsp:attribute name="odd">
      <tr><td colspan=\"4\">${counter}) ${contact[\"name\"]}</td></tr>
      </jsp:attribute>

      Listing 14: The attributes.

      Notice how every attribute that should be treated as a JspFragment is specified using the <jsp:attribute/> standard action.

    5. As we specify the attributes that way, we need to follow suite and properly specify the body of our tag using the <jsp:body/< standard action, as shown in listing 15 below:


      <jsp:body>
      <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>
      </jsp:body>
      </mt:iterate>
      Listing 15: The body

    By now, your odd.jsp should look like this. Store the file in the taglib-tutorial-web/ directory as odd.jsp.

8 Using your new Tag

    We are now ready to use your new Tag.

    Open the URL http://localhost/taglib/odd.jsp in a normal web browser. Hopefully the result looks like this.

    Continue with lesson 17", "Attributes with dynamic names".

Copyright © 2005 IronFlare AB