This chapter will demonstrate how to create custom tags and use them in JSPs. This will require a more detailed explanation of how tags work, as well as covering new topics such as XML and tag libraries. The database access classes (created in Chapter 10) will be used in the server-based application to insert, display, and delete information from a database. We will also demonstrate how the MVC architecture makes incorporating these functions immensely easier.
At the end of this chapter, you should understand:
At the end of this chapter, you should be able to:
JSTL and JSP tags are very easy to use. Just as driving a car doesn’t require knowing how an internal combustion engine works, using a tag does not require understanding how tags work. This is precisely how tag creators meant it to be! Tags were created so that page designers can easily invoke complex functions. In the previous examples, however, the number of bean tags required was quite large and beyond what a page designer should be required to know. Custom tags will be used to eliminate this problem by further simplifying what the page designer must code. However, creating a tag requires a deeper understanding how tags work.
Tags actually invoke a type of Java class called a “tag handler. Tag handler classes are usually defined as a subclass of either the TagSupport or BodyTagSupport classes. The examples in this chapter will define tag handlers as subclasses of TagSupport. As the TagSupport name implies, the tag handler will inherit many variables and methods that do much of the “tag work.”
As mentioned earlier, JSP and JSTL tags come with RAD. What this means is that RAD ensures that the Java classes (i.e., the tag handlers that perform the tag functions) are accessible to the JSPs. For example, the tag handlers for JSP tags are stored in a file that RAD will automatically check when the JSP is run. This means that to implement a JSP tag (like the include tag) the programmer simply inserts the tag into the source code. RAD will find the correct tag handler class.
JSTL tags are not as simple. When a JSTL tag is added, RAD not only inserts the tag into the source code but also inserts a taglib (tag library) directive. The directive identifies the file (i.e., tag library) that contains the tag definition. The tag definition specifies which tag handler class should be invoked for the tag. For instance, when the JSTL if tag was added to the JSP, RAD added the following taglib directive:
<%@taglib uri=" http://java.sun.com/jsp/jstl/core " prefix="c"%>
The URI (Uniform Resource Identifier) value can be an actual file within the project such as /WEB-INF/tls/TNT.tld or an alias. In this chapter, you will create a folder named tls in WEB-INF and then, within tls, a tag library file called TNT.tld. A tag library URI (i.e., an alias) of http://www.tnt.com/taglib will then be created and associated with /WEB-INF/tls/TNT.tld. This association is defined in a project file called the Web Deployment Descriptor.
Notice that the taglib directive also defines a prefix of c for the JSTL tags. This means that when a JSTL tag (that is defined in the jstl/core tag library) is inserted in the page, the JSTL tag keyword must begin with the letter c followed by a colon. In other words, the prefix identifies which directive the server should use to locate the tag library associated with this tag.
In fact, RAD inserted the following JSTL tags when the if tag was added to the JSP:
<c:if test=""></c:if>
The actual JSTL tag keyword is if but notice that it is preceded by c:. So, in this example, the c: tells the server that the tag library is located at http://java.sun.com/jsp/jstl/core. The server then goes to the Web Deployment Descriptor to get the actual location of the tag library.
Got it? Let’s recap. When the server encounters a JSP tag in the page, the server:
With the if tag, the tag handler evaluates a condition. If the condition is true, the body of the tag will be embedded into the response object. For example, if the body contained the text “Yowser!” and the condition was true, the text “Yowser!” would be embedded in the page and appear in the browser.
Who cares? Well, we do. We need a simple tag to create and populate the Employee bean from the JSP fields. In addition, we are also going to create a tag that creates a specialized state choice field that limits the user to only valid state abbreviations. By creating a state field tag, we will be able to use it on multiple JSPs rather than defining a state choice field for each JSP. More important, if a state must be added to or deleted from the list, only the tag handler will have to be changed, not every JSP that displays the state value. Finally, the application will be modified to display the tax amount and gross salary for an employee on the same page as all the other employee information.
As mentioned, we will create a custom tag (getEmp) to replace the following bean tags in EnterEmpInfoJSP:
<jsp:useBean id="EmpBean" class="c9java.Employee" scope="request">
<jsp:setProperty name="EmpBean" property="empStreet"
param="streetAddrTF" />
<jsp:setProperty name="EmpBean" property="exemptions" param="exmpDDM" />
<jsp:setProperty name="EmpBean" property="empState" param="stateTF" />
<jsp:setProperty name="EmpBean" property="empNum" param="empNumTF" />
<jsp:setProperty name="EmpBean" property="empName" param="empNameTF" />
<jsp:setProperty name="EmpBean" property="empZip" param="zipTF" />
<jsp:setProperty name="EmpBean" property="payRate" param="hPRTF" />
<jsp:setProperty name="EmpBean" property="empCity" param="cityTF" />
</jsp:useBean>
We will do this by creating:
We will then use the custom tag in EnterEmpInfoJSP, by inserting:
Tutorial: Creating a Custom Tag
Let’s try it
The following source code will be inserted in CrtEmpBean and displayed in Page Designer:
package c11;
import javax.servlet.jsp.tagext.TagSupport;
public class CrtEmpBean extends TagSupport {
}
Although the generated code is not very impressive, the variables and methods inherited from TagSupport will make this class function as a tag. However, at least one inherited method has to overridden and coded to perform the needed application functions.
The Tag Handler
As mentioned, CrtEmpBean inherits many methods from TagSupport. We will focus on the doStartTag and doEndTag methods that are inherited from TagSupport. It was stated earlier that when the tag is encountered in the JSP, the server runs the associated tag handler class. Specifically, when the start tag is encountered, the server runs the doStartTag method and when the end tag is encountered, the doEndTag method is run.
In the example, we want the doStartTag method to:
Several things will be needed to do this. First, private variables of type Employee and EmpExtractor are required. Second, the HttpServletRequest class needs to be imported into CrtEmpBean. This is required because getEmployeeInstance is expecting an HttpServletRequest, but the request object is of type ServletRequest. So, the ServletRequest object must be cast as an HttpServletRequest. Finally, we will be using the ever-helpful PageContext object (provided by the server) to get the request and define the Employee object as a request bean (or, said another way, define the Employee object as an attribute of the request).
The code to get the request object is:
pageContext.getRequest()
Because the returned request must be cast into an HttpServletRequest, this statement should be preceded with the following to cast the request:
(HttpServletRequest)
This results in:
(HttpServletRequest)pageContext.getRequest()
Then the HttpServletRequest needs to be passed to EmpExtractor. Do this by embedding the above statement in the method call as follows (where ee is the EmpExtractor variable):
ee.getEmployeeInstance(
(HttpServletRequest)pageContext.getRequest());
To define the returned Employee object as a request bean, we need to get the request again (using the page context’s getRequest method again). Then use the request’s setAttribute method to define the employee object as a bean called EmpBean. The statement to do that is as follows (where emp is the Employee variable):
pageContext.getRequest().setAttribute("EmpBean", emp);
Tutorial: Defining the Tag Handler Class
Time to define the tag handler class:
import javax.servlet.http.HttpServletRequest;
private Employee emp;
private EmpExtractor ee = new EmpExtractor();
RAD creates a doStartTag method. Notice the return statement. Even though we are overriding the superclass’ doStartTag method (to create and populate the Employee bean), the superclass’ doStartTag method is still executed. This is because the superclass’ doStartTag method performs many functions we still want to occur. For instance, the doStartTag method returns a value indicating success or failure of the method. This still must occur for the tag to work. Therefore, the superclass’ doStartTag method is executed and its return value is returned as our doStartTag method’s return value.
emp = ee.getEmployeeInstance((HttpServletRequest)pageContext.getRequest());
pageContext.getRequest().setAttribute("EmpBean", emp);
The code should look like the following:
package c11;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
import javax.servlet.http.HttpServletRequest;
public class CrtEmpBean extends TagSupport {
private Employee emp;
private EmpExtractor ee = new EmpExtractor();
public int doStartTag() throws JspException {
emp = ee.getEmployeeInstance((HttpServletRequest)
pageContext.getRequest());
pageContext.getRequest().setAttribute("EmpBean", emp);
return super .doStartTag();
}
}
Defining a Tag
The tag handler class (CrtEmpBean) has been created but the tag has not yet been defined. A tag is defined in a tag library. A tag library is a file with an extension of tld (tag library descriptor) that contains XML (Extensible Markup Language). XML is fast becoming the glue that holds the Internet together. XML is a markup language just like HTML is a markup language. However, HTML is limited to defining Web pages. XML can be used to define any type of information: data, documents, file locations, etc. Really, XML is a language that allows a programmer to define a language. However, we will be using XML in a much more limited capacity.
In the case of a tag library, the server expects the XML to identify a tag and its associated tag handler class. There are other optional XML statements and we will look at one of these later in the chapter. The server also requires that the tag library file (of type tld) be in the WEB-INF folder.
Tutorial: Defining a Tag
Let’s define a tag:
The folder tls should appear in the Enterprise Explorer and be selected.
RAD will display the empty file.
<taglib>
<tag>
<name>getEmp</name>
<tagclass>c11.CrtEmpBean</tagclass>
</tag>
</taglib>
This defines the tag (getEmp) and the tag handler (c11/CrtEmpBean) that will be run by the server when the tag is encountered.
<%@taglib uri="/WEB-INF/tls/TNT.tld" prefix="TNT"%>
<jsp:useBean id="EmpBean" class="c9java.Employee" scope="request">
<jsp:setProperty name="EmpBean" property="empNum" param="empNumTF" />
<jsp:setProperty name="EmpBean" property="empStreet"
param="streetAddrTF"/>
<jsp:setProperty name="EmpBean" property="exemptions" param="exmpDDM"/>
<jsp:setProperty name="EmpBean" property="empZip" param="zipTF"/>
<jsp:setProperty name="EmpBean" property="empState" param="stateTF"/>
<jsp:setProperty name="EmpBean" property="payRate" param="hPRTF"/>
<jsp:setProperty name="EmpBean" property="empName" param="empNameTF"/>
<jsp:setProperty name="EmpBean" property="empCity" param="cityTF"/>
</jsp:useBean>
with these start and end tags for the custom tag:
<TNT:getEmp>
</TNT:getEmp>
Remember, the bean tags are being deleted because CrtEmpBean’s doStartTag method will perform these functions (with the help of the EmpExtractor and PageContext objects).
Time to test.
The browser window should look like Figure 11-1.
Although not very pretty, this proves that the Employee bean was created and that DispEmpInfoJSP was able to access the bean. More important, this means that the custom tag, getEmp, worked!
When to Create Custom Tags
Does this seem like a lot of work to duplicate what can be done with already existing tags? Well, it’s not as much work as it may seem. Some of the work was set-up that will not have to be repeated (e.g., creating the tls folder and the tld file). In addition, although the custom tag didn’t save you, the programmer, any time, the number of tags needed in the JSP was cut down substantially and will save the page designer time. Also, all those bean tags and parameters are difficult for a non-programmer to understand and use; therefore the chance of error was greater and the time to implement longer.
So, when should a custom tag be used? Obviously, if there are no tags to supply the needed function, a custom tag is needed. If the amount of coding in the JSP, whether tags or scriptlets, is beyond the capability of the page designer, the programmer should create a custom tag(s). Also, if the required function is needed in many pages, there are two advantages to using the custom tag. One, the function is coded only once, in the tag handler class (i.e., the page designer does not enter all the bean tags in multiple pages). Two, any changes to the function are made only once in the tag handler class. If the bean tags needed to be modified, the designer would have to change every page that contained the bean tags. So, the bottom line is that if a function is not available, used on many pages, or very complex, a custom tag should be created.
In the above example, we short-stepped the custom tag implementation process. We specified the tag library and path in the directive rather than defining a URI in the Web Deployment Descriptor and using the URI in the directive. The advantage to using a URI is that if the tag library is moved or renamed, only the Web Deployment Descriptor needs to be modified. If the actual path is specified in the directive, every page that uses that tag library has to be updated.
So, we will create a URI for the tag library. In addition, a new custom tag for a state drop-down menu will be created. We will then create new JSPs to use the custom tag and show how a custom tag is easily reused and modified. Finally, we will demonstrate how to pass information to a tag handler using a tag attribute.
Tutorial: Defining a URI
There are many types of deployment descriptors. Essentially, they all contain configuration information that the server will need to deploy an application. The term “configuration information” covers a wide range of information like security settings, data locations and, no coincidence here, tag library locations. The directory path and file that a URI corresponds to is a great example of information “the sever will need to deploy the application” because the application would not work if the deployment descriptor did not contain this information. As mentioned, the advantage is that this is information is specified in one location (a file called web.xml) rather than in all the JSPs and servlets. In RAD, you must create the deployment descriptor.
A file called web.xml will be created in the WebContent/WEB-INF folder. The file can be edited directly with a text editor but there a more user friendly tool called the Deployment Descriptor Editor.
The Design view makes the Deployment Descriptor look very organized (see Figure 11-3). In actuality, the Deployment Descriptor (the web.xml file) simply contains a lot of XML.
We will use the editor’s Variable view to insert the XML that maps the URI http://www.tnt.com/taglib to /WEB-INF/tls/TNT.tld.
The Design view will be redisplayed with a Details area.
In the source code, RAD inserted the following XML to map the URI to the tld file:
<jsp-config>
<taglib>
<taglib-uri> http://www.tnt.com/taglib</taglib-uri >
<taglib-location>/WEB-INF/tls/TNT.tld</taglib-location>
</taglib></jsp-config>
<%@taglib uri= "http://www.tnt.com/taglib " prefix="TNT
Attributes are relatively easy to define and even easier to use. As a matter of fact, you have already used attributes in the application. (The include tag inserted into the JSP specified both page and flush attributes.) To define and use an attribute:
The state drop-down menu will have an optional attribute called selected that allows a designer to specify a state abbreviation to be selected when the drop-down menu is displayed. The following XML code identifies the tag, tag handler, and defines the tag attribute selected.
<tag>
<name>stateDDM</name>
<tagclass>c11.TNTStateDDM</tagclass>
<attribute>
<name>selected</name>
<required>false</required>
</attribute>
</tag>
The required tag is not required because attributes by default are optional. However, to define a required attribute, the “subelement” required must be specified and its value set to true.
An attribute must be specified in the start tag to make it available to the doStartTag method. The attribute name is specified after the tag name, separated by at least one space. For example, the following would define GA as the selected value in the custom tag stateDDM:
<TNT:stateDDM selected="GA"></TNT:stateDDM >
When the server encounters the keyword selected, the setter (in the TNTStateDDM tag handler class) will be invoked and passed the value GA. The TNTStateDDM class must check the value of the variable selected and generate the appropriate HTML such that the value is selected in the drop-down menu.
How to Generate a Visual Component
The stateDDM tag will generate a drop-down menu that has only three values to choose from FL, GA, and OK. (The tag handler c11.TNTStateDDM, of course, is actually doing all the work. In this case, the work entails inserting the HTML that defines the drop-down menu into the JSP. TNTStateDDM will do this similarly to how servlets manipulate a page. In a servlet, the programmer had to:
The tag handler (TNTStateDDM) will do the same, however, the JSP container (part of the server) makes things much easier by providing a JspWriter that is already associated with the JSP response object. In other words, the tag handler does not have to perform steps A and B from above. Rather the JSP writer is retrieved (using the ever-useful PageContext object) and the write method is executed. The following statement will do this:
pageContext.getOut().write(" HTML to be embedded ");
There is just one small hitch. Notice that the following HTML (which defines a drop-down menu, with the three state abbreviation options, and FL specified as the selected value) contains double quotes and that the write command above requires that the HTML be enclosed in double quotes.
<SELECT name="stateDDM">
<OPTION value="FL" selected>FL</OPTION>
<OPTION value="GA">GA</OPTION>
<OPTION value="OK">OK</OPTION>
</SELECT>
As soon as the first double quote in the HTML is encountered, the RAD source editor will think that the code to be embedded is finished. In other words, if the following write statement (with the double quotes around stateDDM) is specified:
pageContext.getOut().write("<SELECT name="stateDDM">");
RAD will read this far in the statement:
pageContext.getOut().write("<SELECT name="
and think that only <SELECT name= should be embedded in the JSP. To make matters worse, RAD expects an ending parenthesis and semicolon to finish the write statement. When instead RAD runs into the remaining HTML, the entire statement will be flagged as an error.
Fortunately, RAD (and most editors) can be forced to include special characters, such as a double quote, as part of the parameter value rather than as part of the statement’s syntax by preceding the special character with a backslash (). So, each time a double quote appears in the HTML, it must be preceded with a backslash. The correct statement to insert the first line is:
pageContext.getOut().write("<SELECT name="StateDDM">");
TNTStateDDM will set FL as the default selected value but must check the selected attribute to see if another value should be selected instead.
The last thing we will do is change the JSP so that it uses the new tag stateDDM. This is going to be a little tricky. Currently, we import the entire EnterEmpInfoForm.html file that includes a state text field. We no longer want to do that. Instead, we want to include the beginning of the form (up to but not including the current state field definition). Then we want to insert the new custom tag for stateDDM and then an include tag .for the remaining portion of the EnterEmpInfoForm.html file. Two new files called StartEEIF.html and EndEEIF.html will be created to hold the HTML needed to define the form around the new tag. We will then add the include tags for the new files and the stateDDM tag into EnterEmpInfoJSP.
Tutorial: Generating a Visual Component
Let’s generate a visual component:
private String selected = "FL";
The following source code should be in the class:
package c11;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;
public class TNTStateDDM extends TagSupport {
private String selected = "FL";
public String getSelected() {
return selected;
}
public void setSelected(String selected) {
this .selected = selected;
}
public int doStartTag() throws JspException {
return super .doStartTag();
}
}
pageContext.getOut().write("<SELECT name="stateDDM">");
pageContext.getOut().write("</SELECT>");
These statements will be flagged as errors because the write method can throw an exception. We will have RAD generate the appropriate try / catch..
The try/catch statements will be added and the statements will no longer be flagged as errors.
Before we go any further, we have to cover a topic that should have been covered much earlier: arrays. An array is a “structure” that can hold many primitive values or many reference variables of the same type. In other words, an array could be defined to hold multiple int values or multiple String variables. However, an array cannot be defined to hold both int values and String variables. Another way to say this is that arrays are typed. When the array is defined, the type of values it will hold must be specified. So for example, the following would define an array object that can hold seven int values:
new int[7];
Of course, this new array of integers can’t be accessed because it was assigned to a variable. To use this array, an integer array variable (e.g., myFirstArray) must be defined and the array assigned to it as follows:
int [] myFirstArray = new int [7];
Great, we have achieved array! Maybe we should put something into the array? To reference a particular array “bucket,” simply specify the bucket’s index/position number and assign a value using the equal sign. For instance, the following would assign the value 42 to the fifth bucket:
myFirstArray[4] = 42;
You can think about the array as looking like the following:
Are you wondering why the value 42 is where it is? Can you guess why? The first bucket has an index number of zero. In other words, the first bucket is referred to as myFirstArray[0]. So, myFirstArray[4] actually refers to the fifth bucket (or position) within the array. To reiterate, an array with a length of seven has seven buckets with position/index numbers ranging from zero to six. You can think of the array and its index numbers as looking like the following:
Index Number 0 1 2 3 4 5 6
Be careful, the index numbers are a source of countless errors!
Are you also wondering where all the zeros come from? The JVM inserts initial values when an array is created. For numeric arrays, that value is zero (character arrays are set to the character 0 and referenced variable arrays are set to null).
We are going to use an array to store our state values. Using an array will make adding or deleting states from the stateDDM tag much easier. For example, if we did not use an array, the following code would be needed to define the three state options in a drop-down menu:
<SELECT name="stateDDM">
<OPTION value="FL">FL</OPTION>
<OPTION value="GA">GA</OPTION>
<OPTION value="OK">OK</OPTION>
</SELECT>
If we wanted to add five states, we would have to add five start and end OPTION tags with the new abbreviations specified in the body of the tags. Unfortunately, because the page designer can specify a state to be selected, it’s even more complicated. The tag handler needs to check the selected variable’s value and generate the appropriate HTML for that option to be selected. For example, if selected equals FL, the following HTML must be generated.
<OPTION value="FL" selected>FL</OPTION>
So, the tag handler needs several if statements like the following:
if (selected == "FL"){
<OPTION value="FL" selected>FL</OPTION>
} else {
<OPTION value="FL">FL</OPTION>}
if (selected == "GA"){
<OPTION value="GA" selected>GA</OPTION>
} else {
<OPTION value="GA">GA</OPTION>}
if (selected == "OK"){
<OPTION value="OK" selected>OK</OPTION>
} else {
<OPTION value="OK">OK</OPTION>}
Now when five states need to be added, the programmer must enter five if/else statements and ten start and end OPTION tags. That’s a lot of typing and, of course, this means mistakes can easily be made. To cut down on the amount of “hard-coded” HTML, a String array will hold the state values and a for loop will:
Tutorial: Using an Array
Now let’s use an array
String[] stateArray = {"FL", "GA", "OK"};
Notice that no size was specified for the array. An array can be assigned values, and the JVM will create an array of the needed size - in this case three.
StringBuffer optionTagStart = new StringBuffer("<OPTION value="");
StringBuffer optionSelected = new StringBuffer("" selected>");
StringBuffer optionNotSelected = new StringBuffer("">");
StringBuffer optionTagEnd = new StringBuffer("</OPTION>");
StringBuffer optionTags = new StringBuffer();
Because the HTML to define each option will be constantly changing in the loop, defining optionTags as a StringBuffer is a more computer-efficient choice than defining it as a String. However, because only Strings can be embedded into the JSP, we will need to convert the StringBuffer to a String thereby adding a little extra code.
Notice that when a state is to be selected, only the start tag is different from a non-selected state. In other words, the end tag never changes. Looking closer at the start tags, notice that non-selected and selected OPTION tags actually begin the same. The difference is the characters that follow the value attribute in the start tag. If a value is not selected, then the value in the start tag is simply followed by a double quote and a greater than sign. If the value is selected, then it is followed by a double quote, a space, the keyword selected, and then the greater than sign. So, two strings (optionSelected and optionNotSelected) were created to hold these two possible sets of characters.
for ( int ctr = 0; ctr < stateArray.length; ctr++) {
optionTags.append(optionTagStart).append(stateArray[ctr]);
if (stateArray[ctr] == this .selected) {
optionTags.append(optionSelected);
} else {
optionTags.append(optionNotSelected);
}
optionTags.append(stateArray[ctr]).append(optionTagEnd);
pageContext.getOut().write(String. valueOf (optionTags));
optionTags.delete(0,optionTags.length());
}
You should note several things. The for loop’s counter(ctr) is initialized to zero to correspond to the index value of the array’s first position/bucket. The loops test condition checks that the count is less than the length of the array. Think about that. If the array has a length of three, the largest index value in the array is two (i.e., the array has three values with index values of 0, 1, and 2). We want the for to execute three times for the counter values of 0, 1, and 2. So we want the for to only execute when the variable ctr is less than the length of the array (3).
Inside the loop, the constant text that comprises the beginning of the start OPTION tag (optionTagStart) is concatenated to the state value stored in the first position of the array (i.e., FL). This means that after the first statement in the loop, optionTags has the following value:
<OPTION value="FL
The if statement then compares the value in the first array position to the variable select-ed’s value. Based on whether they are equal or not, either the optionSelected or optionNotSelected string is concatenated to (and finishes) the start OPTION tag. In this case, we’ll assume that no attribute value was specified by the page designer and that the value of selected is the default value FL. This means that the characters stored in optionSelected are appended to optionTags resulting in a value of:
<OPTION value="FL" selected>
Next, the state abbreviation to appear in the drop-down menu (i.e., the value in position 0 of the array) and the end tag are appended to optionTags, resulting in a character string of:
<OPTION value="FL" selected>FL</OPTION>
The OPTION tag for the first state value is converted to a String and written to the JSP. The contents of optionTags is deleted and the loop begins again (the counter value is incremented by one, compared against the length of the array, etc). The loop will execute two more times and the HTML for the two remaining state values will be embedded into the JSP.
Because of the array and loop, if five states had to be added, five state values would simply be added to the array definition. No other java code or HTML statements would be needed to add the five states.
Tutorial: Defining and Using the Custom Tag
We can now define and use the tag
<tag>
<name>stateDDM</name>
<tagclass>c11.TNTStateDDM</tagclass>
<attribute>
<name>selected</name>
<required>false</required >
</attribute>
</tag>
The cursor should be positioned at the beginning of the state field’s INPUT tag (indicated by the first arrow in Figure 11-5).
All the HTML before the INPUT tag will be copied to a file called StartEEIF.html. The HTML after the tag (indicated by the second arrow in Figure 11-5) will be copied to a file called EndEEIF.html. In the JSP, the new stateDDM tag will be inserted and include tags for the new html files will be inserted before and after the stateDDM tag.
Please note that EndEEIF shouldn’t be an HTML file. Because the copied HTML does not define a “complete” page, entering it in an HTML file will generate numerous source code warning messages (that we will ignore) and warnings like the encoding conflict.
If the include tag was selected, the form should be outlined in black and the properties view will show that the jsp:include tab, on the left, is selected (as in Figure 11-6).
The first half of the employee form will be displayed in Page Designer. The new state custom tag needs to be inserted after the half form.
Because the taglib directive is already in the JSP, RAD displays the TNT tag library and all its tags. (Click on the other tag library option if you would like to display all the tags in jstl/core tag library.)
Page Designer should look like Figure 11-8.
Does Figure 11-8 surprise you? If so, then the Preview view will also be surprising! Remember the browser can only interpret HTML and scripts. Custom tags and JSP tags are resolved by the server (i.e., the server invokes the tag handler class). So, the only way to test a custom tag is to run the JSP on the server.
Tutorial: Testing the Custom Tag
We’ve built it, so we have to test it:
The page should look like Figure 11-9.
Notice that FL appears as the default value.
Uh-oh, no state value appears. What happened?
Well, we changed the name of the state field from stateTF to stateDDM and EmpExtractor tries to set the Employee object’s state property from the value of stateTF. So, we have to change EmpExtractor.
Now we will test that the tag attribute was defined correctly and works.
Switching to the Source view would show that RAD has changed the start tag to the following:
<TNT:stateDDM selected="OK">
OK should be displayed as the selected value in the state drop-down menu.
Tutorial: Database Access with Tags
We will demonstrate the flexibility and advantages of both the MVC architecture and custom tags by changing the application to work with a DBMS. An Insert JSP (InsEmpInfo.jsp) will be created that allows a user to add employees to the database and that uses the stateDDM custom tag. Two files (StartIEIF.html and EndIEIF) will be created that define the beginning and ending of a form with all the needed data entry fields. New custom tags (insEmp, delEmp) and tag handlers (InsertEmp, DeleteEmp) will be created to perform database inserts and deletions. InsEmpInfo.jsp will use the Employee bean (created by the getEmp tag) and the insEmp and getEmp tags to insert the information.
Specifically, when InsEmpInfo.jsp is first accessed, a data entry form with the stateDDM field will be displayed. After the information has been entered and the form’s submit button clicked, InsEmpInfo will be invoked again. InsEmpInfo will check to see if the request is a post and, if so, execute the getEmp tag (to create an Employee bean) and the insEmp tag (to insert the information).
StartEEIF.html and EndEEIF.html have much of the HTML we need for the new form. Therefore, we will use them as the base for StartIEIF.html and EndIEIF.html, thereby avoiding a lot of typing (and probably mistakes).
StartIEIF needs to specify the form action as InsEmpInfo.jsp so that when the button is clicked, InsEmpInfo is invoked again by the server. Also, the title should be changed to StartIEIF.html
<title>StartIEIF.html</title>
<FORM action="InsEmpInfo.jsp" method="post">
EndIEIF needs to define the remaining data entry fields and the end of the form. Because EndEEIF was copied as EndIEIF, the remaining data entry fields already exist in EndIEIF. However, the function options list box (i.e., display, tax and gross amounts) is also in EndIEIF. So, the HTML that defines the table row holding the function list box and text needs to be deleted.
<tr>
<td align= "right" >
<font face= "Tahoma" size= "4" color= "black" >
Function
</font>
</td>
<td>
<select size= "2" name= "functionLB" >
<option value= "Display" >Display</option>
<option value= "Gross" >Gross</option>
<option value= "TaxAmt" >TaxAmt</option>
</select>
</td>
</tr>
The button start tag should look like the following:
<input type="submit" name="submitBtn" value="Insert">
Because EnterEmpInfoJSP has much of the code we need for InsEmpInfo, we will use EnterEmpInfoJSP as the base for InsEmpInfo and, again, save a lot of typing and mistakes.
InsEmpInfo needs to be modified to include the HTML in StartIEIF and EndIEIF.
Let’s make sure InsEmpInfo looks right.
The browser should look like Figure 11-11. Notice that the function box is gone and the button has the text Insert. Clicking the drop-down menu button will show the three state options.
InsEmpInfo must now be modified to create EmpBean and invoke the insert function. However, before that can be done the classes that provide the database functions must be created.
Because the client-based application followed the MVC architecture (i.e., the model is separate from the view and controller), implementing the database functions in a server-based application is almost as simple as copying and pasting the model classes. In other words, because the model was built independently of the view, the classes that comprise the model can be easily used in projects that use different views. If the client-based application had embedded the database functions in a Frame subclass (i.e., combined the view and model functions), the database functions could not have been simply copied into a server-based application.
However, just as with the client-based application, the correct database driver needs to be available for the Employee class. If DB2 or Oracle is being used, the driver must be added to the Web App Libraries in the project’s Java build path.
The driver file should appear as in Figure 11-12.
If you are using an Access database, you will have to move the database on to the RAD test server. Access databases cannot be accessed remotely, so they must reside on the server.
If you can’t figure out the location of the server definition, after we create and implement the insert tag, simply run the insert JSP and try to insert an employee. The resulting error messages will display the folder location.
All that needs to be done now is to make the view (InsEmpInfo) invoke the model (i.e., the Employee class’s insert function). As mentioned, a new tag call insEmp will be created to perform the insert function. The tag will be added to InsEmpInfo such that the insEmp tag is executed after the button is clicked.
Let’s make the necessary changes:
private Employee emp;
emp = (Employee) pageContext.getRequest().getAttribute("EmpBean");
This statement retrieves EmpBean using the PageContext object. Unfortunately, the bean is returned as a type of Object. So, the Object must be cast as an Employee before it can be assigned to the variable emp.
emp.doInsert();
<tag>
<name>insEmp</name>
<tagclass>c11.InsertEmp</tagclass>
</tag>
This can be done by deleting the source code in the Source view or by selecting the choose icon in Page Designer (see Figure 11-13) and pressing the Delete key.
The if and custom tags should look like the following:
<c:if test='${pageContext.request.method=='POST'}'>
<TNT:getEmp>
</TNT:getEmp>
<TNT:insEmp></TNT:insEmp>
</c:if>
As with most tags, the custom tags can be specified even more simply as follows:
<TNT:getEmp/> <TNT:insEmp/>
The last modification needed is to add a new state (IN) to all the state drop-down menus throughout the application. If the state drop-down menu had been defined directly in multiple JSPs, all the JSPs would have to be modified. Because the state drop-down menu is a custom visual component (implemented with custom tags), making this application-wide change means simply updating the tag handler.
That’s all it takes to change every JSP’s state field.
The InsEmpInfo page will be redisplayed and all the data removed from the fields. But, did it work? Well, if you can access the database directly, display the Employee table and verify that the record was inserted. However, to prove that the insert worked, we will modify the get-Emp tag (i.e., CrtEmpBean.java) so that database information will be displayed.
Tutorial: Modifying the Tag Handler
CrtEmpBean will be modified to check the database when an employee number is entered on EnterEmpInfoJSP. If there is employee information in the database, then any information that came from the form (i.e., that is in EmpBean) will be overwritten with the database information. As before, the display page will then display the EmpBean data. We will also modify the display page to include the gross salary and tax amounts for the employee and provide the option to delete the employee information from the database.
emp.getEmpInfo(pageContext.getRequest().getParameter("empNumTF").toString());
This statement retrieves the employee number entered in the empNumTF field using the request object’s getParameter method. Just as when page context was used to get the Employee bean, a parameter is returned as an object of type Object. In this case, the Object object is converted to a String using its own toString method (rather than casting it, as was done earlier). Finally, the employee number is passed to the Employee class’s getEmpInfo method. This method retrieves employee information from the database and populates all the Employee object’s properties. If there is no employee record, the information entered on the page will not be overwritten.
This is not the most efficient way to perform this function. For instance, the Employee object may be written to twice - once with the form data and then possibly again with the database information. For efficiency, the database should be checked before the form information is added to the employee object. Then, only if there is no information for the employee in the database, should the object be populated with the form information. However, this inefficient solution is simpler to code because only one Java statement is needed.
Notice how easy it was to access the database: one statement. Remember, this was easy because the client-based application was implemented using the MVC architecture. Following the MVC architecture not only standardizes application design, it also makes applications more flexible and easier to modify.
Tutorial: Deleting Records Using a Custom Tag
DispEmpInfoJSP needs two new fields to display the gross salary and tax amounts along with all the other employee information. All the fields will be included on a form with a delete button. When pressed, the delete button will forward the request to a confirmation JSP (ConfirmEmpDel, see Figure 11-18). If the user clicks the confirm button on the page, the request will be forwarded to a delete employee JSP (EmpDel) where the record will be deleted and a message displayed confirming the delete (see Figure 11-19). A new delete tag (delEmp) will be defined and added to the EmpDel JSP.
private Employee emp;
emp = (Employee) pageContext.getRequest().getAttribute("EmpBean");
emp.doDelete();
Is the procedure for creating a custom tag starting to seem familiar? As a quick reminder, the steps are:
Steps 1 through 6 did A. Step 7 and 8 will do B and, after creating two JSPs, steps 20 through 24 will do C.
<tag>
<name>delEmp</name>
<tagclass>c11.DeleteEmp</tagclass>
</tag>
Please confirm that employee
should be permanently deleted from the database
by clicking the Confirm button
This will select the tag and the line breaks (as in Figure 11-15).
Page Designer and the Properties view should look like Figure 11-16.
The delEmp tag needs to be added to EmpDel.jsp and EmpDel.jsp should display a message saying the employee information has been deleted.
The Insert Custom Tag Window will be displayed with no URIs listed in the left pane or tags in the right pane. The taglib directive for the TNT tag library needs to be added to the page so that the delEmp tag can be specified.
The Insert Custom Tag Window will be displayed with the TNT tag library URI displayed in the left pane and the four TNT tags listed in the right pane.
In the Source view, notice that RAD added the following taglib directive and custom tags:
<%@taglib prefix="TNT uri=" http://www.tnt.com/taglib "%>
<TNT:delEmp></TNT:delEmp>
Even though the employee information will be deleted from the database, the employee bean still exists. Therefore, we can still retrieve the name of the employee that was deleted and display it in the deletion message.
We are now going to add the tax and salary amounts to the display page.
There should now be two blank rows at the end of the table.
Page Designer and the Properties view should look like Figure 11-17.
Tutorial: Testing the Insert and Delete
First, we will verify that the insert was performed and then test the delete function.
The Tracy Tester information and a Delete button should be displayed. This proves that the insert performed earlier worked.
An error message will be displayed in the browser saying that the confirm page cannot be displayed. In the console pane, the reason for the error will be a null bean. What happened? When the Delete button was clicked (on the display page), a new request was created and forwarded to the confirm page. The new request does not have an EmpBean. “Why not?” you ask.
Because EmpBean was defined with a scope of request. Because EnterEmpInfoJSP created a new request, the EmpBean was not associated with this new request.
There are many ways to solve this problem. We will change the scope of EmpBean from request to session. The bean scope can be changed very easily because the bean is defined in only one place, the CrtEmpBean class. (If bean tags had been used, many JSPs would have had to be updated.) The statement that defines the Employee object as an attribute of the request will be changed to define the Employee object as an attribute of the session. This means that InsertEmp and DeleteEmp need to be changed to get the bean from the session not the request.
pageContext.getSession().setAttribute("EmpBean", emp);
emp = (Employee) pageContext.getSession().getAttribute("EmpBean");
Notice that the only change to these statements is that instead of invoking the page context’s getRequest method, the getSession method is used.
Again, the Tracy Tester information and a Delete button should be displayed. This time, however, CrtEmpBean created EmpBean as a session bean.
The Tracy Tester name should be displayed with the text and a Confirm button as in Figure 11-18.
The EmpDel JSP should be run, resulting in the employee record for Tracy Tester being deleted from the database. The browser will display the text that confirms that the record was deleted (see Figure 11-19).
The browser will display the information entered above. This means that there was no information for employee 444 in the database to override the information entered in step 11, which confirms that the Tracy Tester information was deleted from the database.
Checking the Console view will also show a message saying there was a problem getting information from the result set (in the Employee class). Which makes sense because there was nothing returned in the result set when the select statement for employee number 444 was executed.
The server-based application has been upgraded, but to make the coding easier we have violated some standards and introduced inefficiencies. One violation that we have not pointed out is that the application does not separate the view and controller functions. The JSP pages contain forward tags. Really, a servlet should be performing this controller function. The advantage to using a servlet is that if a new page were added or deleted, no other pages would be modified. The MVC architecture would ensure that any navigation changes would be made in one place, the servlet, and not affect the JSPs that comprise the view.
In addition, the application has not been very careful about creating and closing multiple connections. For instance, instead of creating a connection each time an Employee object is created, it would have been more efficient to create a connection when the application is started and then have all the employee objects use that connection.
Fortunately, the next chapter covers Java Server Faces, which provides easy tools to make these improvements.
Let’s check our results:
Review Questions
The Shipment application will be modified to insert, display, and delete shipment information in a DBMS using custom tags. This will require:
CrtShipBean InsertShip DeleteShip
The Shipment application never had an extractor class (as the employee application did). Therefore, the CrtShipBean class will perform that function (i.e., retrieve the Shipment information from the request and populate the Shipment object properties). The Java Resources/ src/c11/CrtShipBean’s doStartTag method needs to be overridden to do the following:
If the source attribute is equal to request:
If the source attribute is equal to database:
Then, in either case, define the Shipment object as a session attribute called ShipBean.
private String source = null ;
Shipment ship;
if (source == "request"){
ship = new Shipment();
ship.setShipmentNum(pageContext.getRequest().getParameter("shipNumTF").toString());
ship.setSupplierName(pageContext.getRequest().getParameter("suppNameTF").toString());
ship.setRcvMon(pageContext.getRequest().getParameter("MonthDDM").toString());
ship.setRcvDay(pageContext.getRequest().getParameter("DayDDM").toString());
ship.setRcvYear(pageContext.getRequest().getParameter("YearDDM").toString());
ship.setRcvHour(pageContext.getRequest().getParameter("HourDDM").toString());
ship.setRcvMin(pageContext.getRequest().getParameter("MinuteDDM").toString());
ship.setRcvAMPM(pageContext.getRequest().getParameter("AMPMDDM").toString());
ship.setEmployeeNum(pageContext.getRequest().getParameter("empNumTF").toString()); }
if (source == "database"){
ship = new Shipment(pageContext.getRequest().getParameter(" shipNumTF").toString()); }
pageContext.getSession().setAttribute("ShipBean", ship);
Shipment XXX
has been inserted into the database
<p align= "center" >Enter Shipment Number:
<input type= "text" name= "shipNumTF" size= "20" ></p>
Using the JSP forward tag means that ConfirmDel will be invoked by a post request (because DelShip was invoked by a post request not a get request). This means ConfirmDel should display the confirmation message, if the request is a post, and if the request is a get, the deletion should be performed.
The DisplayShip JSP should display all the ship properties. Bean getProperty tags can be used to retrieve all the property values; however, formatting the retrieved date and time information is cumbersome and redundant. We will change two Shipment class methods to correctly format and return the date and time.
rcvDate = getRcvMon() + "/" + getRcvDay() + "/" + getRcvYear();
rcvTime = getRcvHour() + ":" + getRcvMin() + " " + getRcvAMPM();
Another formatting problem may need to be addressed. Month, day, minute, and hour values less than ten are frequently displayed as single-digit numbers in the drop-down menus. This causes formatting problems when the date and time values are displayed. For example, if the time is five minutes past noon, the value would be displayed as 12:5 PM. In addition, some DBMSs pad string values with spaces to fill up the field. For example, this means that five minutes after three o’clock in the afternoon will look like 3 :5 PM. Again, the best solution is to change the Shipment class to return the information correctly formatted. If you’re having these problems, do steps 26 through 29.
return rcvHour.trim();
if ((Integer. parseInt (rcvMin.trim()) < 10)
&& (rcvMin.trim().length() < 2) ){
rcvMin = "0" + rcvMin.trim(); }
Each of the property tags should specify ShipBean as the Name. For the date and time values, use the rcvDate and rcvTime properties, rather than the six individual properties that make up the date and time.
Results of the Review Exercise
After the exercise, we have the following:
Check that the Exercise Was Done Correctly
Finally, we check that everything went as planned:
The insert confirmation page should be displayed.
Shipment 8888’s information should be displayed as in Figure 11-22.
The confirm deletion page should be displayed.
The confirm deletion Web page should be displayed saying the shipment was deleted.
The browser should show the “page cannot be displayed” message because a ShipBean could not be created with information from the database for shipment number 8888. (Actually, the error is thrown when DispShip tries to access the nonexistent 8888 shipment from the result set; specifically a null cursor exception is thrown.) Of course, the exception should be caught and handled by the application and we should fix how the time is displayed, but we will leave that for a rainy day.
3.21.246.223