|
|
Optimize with a SATA RAID Storage Solution
Range of capacities as low as $1250 per TB. Ideal if you currently rely on servers/disks/JBODs
Page 3 of 3
template:put stores beans in request scope but not directly because if two templates use the same content names, a nested template could overwrite the enclosing template's content.
To ensure that each template has access only to its own information, template:insert maintains a stack of hashtables. Each insert start tag creates a hashtable and pushes it on the stack. The enclosed put tags create beans and store them in the newly created hashtable. Subsequently, get tags in the included template access the beans in the hashtable. Figure 4 shows how the stack is maintained for nested templates.
Figure 4. Storing template parameters in request scope
Each template in Figure 4 accesses the correct footer; footer.html for template_1.jsp and footer_2.html for template_2.jsp. If the beans were stored directly in request scope, step 5 in Figure 4 would overwrite the footer bean specified in step
2.
The remainder of this article examines the implementation of the three template tags: insert, put, and get. We begin with sequence diagrams, starting with Figure 5. It illustrates the sequence of events for the insert and put tags when a template is used.
Figure 5. Sequence diagrams for the put and insert tags
If a template stack does not already exist, the insert start tag creates one and places it in request scope. A hashtable is subsequently created and pushed on the stack.
Each put start tag creates a PageParameter bean, stored in the hashtable created by the enclosing insert tag.
The insert end tag includes the template. The template uses get tags to access the beans created by put tags. After the template is processed, the hashtable created by the insert start tag is popped off the stack.
Figure 6 shows the sequence diagram for template:get.
Figure 6. Sequence diagrams for the get tag
Tag handler implementations for the template tags prove straightforward. Example 3.a lists the InsertTag class -- the tag handler for template:insert.
Example 3.a. InsertTag.java
package tags.templates;
import java.util.Hashtable;
import java.util.Stack;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;
public class InsertTag extends TagSupport {
private String template;
private Stack stack;
// setter method for template attribute
public void setTemplate(String template) {
this.template = template;
}
public int doStartTag() throws JspException {
stack = getStack(); // obtain a reference to the template stack
stack.push(new Hashtable()); // push new hashtable onto stack
return EVAL_BODY_INCLUDE; // pass tag body through unchanged
}
public int doEndTag() throws JspException {
try {
pageContext.include(template); // include template
}
catch(Exception ex) { // IOException or ServletException
throw new JspException(ex.getMessage()); // recast exception
}
stack.pop(); // pop hashtable off stack
return EVAL_PAGE; // evaluate the rest of the page after the tag
}
// tag handlers should always implement release() because
// handlers can be reused by the JSP container
public void release() {
template = null;
stack = null;
}
public Stack getStack() {
// try to get stack from request scope
Stack s = (Stack)pageContext.getAttribute(
"template-stack",
PageContext.REQUEST_SCOPE);
// if the stack's not present, create a new one and
// put it into request scope
if(s == null) {
s = new Stack();
pageContext.setAttribute("template-stack", s,
PageContext.REQUEST_SCOPE);
}
return s;
}
}
Example 3.b lists the PutTag class, the tag handler for template:put:
Example 3.b. PutTag.java
package tags.templates;
import java.util.Hashtable;
import java.util.Stack;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import beans.templates.PageParameter;
public class PutTag extends TagSupport {
private String name, content, direct="false";
// setter methods for Put tag attributes
public void setName(String s) { name = s; }
public void setContent(String s) {content = s; }
public void setDirect(String s) { direct = s; }
public int doStartTag() throws JspException {
// obtain a reference to enclosing insert tag
InsertTag parent = (InsertTag)getAncestor(
"tags.templates.InsertTag");
// put tags must be enclosed in an insert tag
if(parent == null)
throw new JspException("PutTag.doStartTag(): " +
"No InsertTag ancestor");
// get template stack from insert tag
Stack template_stack = parent.getStack();
// template stack should never be null
if(template_stack == null)
throw new JspException("PutTag: no template stack");
// peek at hashtable on the stack
Hashtable params = (Hashtable)template_stack.peek();
// hashtable should never be null either
if(params == null)
throw new JspException("PutTag: no hashtable");
// put a new PageParameter in the hashtable
params.put(name, new PageParameter(content, direct));
return SKIP_BODY; // not interested in tag body, if present
}
// tag handlers should always implement release() because
// handlers can be reused by the JSP container
public void release() {
name = content = direct = null;
}
// convenience method for finding ancestor names with
// a specific class name
private TagSupport getAncestor(String className)
throws JspException {
Class klass = null; // can't name variable "class"
try {
klass = Class.forName(className);
}
catch(ClassNotFoundException ex) {
throw new JspException(ex.getMessage());
}
return (TagSupport)findAncestorWithClass(this, klass);
}
}
PutTag.doStartTag creates a PageParameter bean -- listed in Example 3.c. -- subsequently stored in request scope.
Example 3.c. PageParameter.java
package beans.templates;
public class PageParameter {
private String content, direct;
public void setContent(String s) {content = s; }
public void setDirect(String s) { direct = s; }
public String getContent() { return content;}
public boolean isDirect() { return Boolean.valueOf(direct).booleanValue(); }
public PageParameter(String content, String direct) {
this.content = content;
this.direct = direct;
}
}
PageParameter serves as a simple placeholder for the content and direct attributes set in the template:put tag. We see the GetTag class, the tag handler for template:get, in Example 3.d:
Example 3.d. GetTag.java
package tags.templates;
import java.util.Hashtable;
import java.util.Stack;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.TagSupport;
import beans.templates.PageParameter;
public class GetTag extends TagSupport {
private String name;
// setter method for name attribute
public void setName(String name) {
this.name = name;
}
public int doStartTag() throws JspException {
// obtain reference to template stack
Stack stack = (Stack)pageContext.getAttribute(
"template-stack", PageContext.REQUEST_SCOPE);
// stack should not be null
if(stack == null)
throw new JspException("GetTag.doStartTag(): " +
"NO STACK");
// peek at hashtable
Hashtable params = (Hashtable)stack.peek();
// hashtable should not be null
if(params == null)
throw new JspException("GetTag.doStartTag(): " +
"NO HASHTABLE");
// get page parameter from hashtable
PageParameter param = (PageParameter)params.get(name);
if(param != null) {
String content = param.getContent();
if(param.isDirect()) {
// print content if direct attribute is true
try {
pageContext.getOut().print(content);
}
catch(java.io.IOException ex) {
throw new JspException(ex.getMessage());
}
}
else {
// include content if direct attribute is false
try {
pageContext.getOut().flush();
pageContext.include(content);
}
catch(Exception ex) {
throw new JspException(ex.getMessage());
}
}
}
return SKIP_BODY; // not interested in tag body, if present
}
// tag handlers should always implement release() because
// handlers can be reused by the JSP container
public void release() {
name = null;
}
}
GetTag.doStartTag retrieves the page parameter bean from request scope and obtains the content and direct properties from the bean. Subsequently, the content is either included or printed, depending on the value of the direct property.
Templates are a simple but useful concept that should be in every JSP developer's repertoire. Templates encapsulate layout and therefore minimize the impact of layout changes. Further, templates can discriminate content based on user roles and can be nested within other templates and JSP pages.
Templates are not a standard JSP feature, but you can easily implement them with custom tags, as discussed in this article. Those tags can be used as is, or they can be used as the baseline for a template mechanism with more features.
Read more about Core Java in JavaWorld's Core Java section.