Custom JSP Actions
Over the last few months, we have looked at server-side Java from a number of perspectives. We began with servlets, Java classes that are executed from within a servlet container. While programmers are not especially daunted by servlets, graphic designers might feel otherwise.
Solving this problem are JavaServer Pages (JSPs) that combine Java and HTML, using a syntax similar to Microsoft's Active Server Pages (ASP) or the open-source HTML::Mason system for mod_perl. Each JSP is really a servlet in disguise; the JSP engine translates the page into a servlet, and then compiles the servlet into a Java .class file.
JSPs can include straight Java code that can make it easier to perform complex actions. But at a certain point, this code can overwhelm the HTML, making it impossible to maintain the JSP. Nonprogrammers are also turned off by large amounts of code inside a JSP, defeating much of the purpose of using JSPs instead of straight servlets.
Last month, we looked at one way to avoid putting code inside of JSPs using JavaBeans. Using simple XML-based tags, a nonprogrammer can put together JSPs that exhibit complex behaviors, without having to write a single line of code. Indeed, the real magic of JavaBeans is not the beans themselves, but rather the special tags that allow us to work with them so easily.
This month, we will learn how to write our own “custom actions”, as they are known—XML-based tags that allow us to work with Java classes and methods without having to work with Java itself. Our examples are designed to work with the open-source Jakarta-Tomcat implementation of servlets and JSPs. However, they should work with any JSP implementation that works with custom actions.
There are a number of reasons to use custom tags. First of all, they reduce the amount of Java code we must put inside of our JSPs, making them easier to read, understand and maintain. In addition, custom tags are less complicated than Java code, making them suitable for a wider audience than Java code users. Finally, each custom tag library points to a centrally written and maintained Java class. Using custom actions, a site can thus create a library of tags appropriate for its particular needs. One Java programmer can create and publish a tag library for a number of graphic designers and JSP authors. As we will see, custom tags aren't a panacea, but they can be quite useful, and I consider them one of the most compelling reasons to use JSP over competing technologies.
Custom actions provide us with a shorthand for complex Java code within our JSP. Anything you do with a custom action could also be accomplished with Java code inside of the scriptlet (<% %>) tag. After all, the JSP is turned into a servlet before it is compiled and executed for end users.
As we saw last month with JavaBean tags, custom actions are defined with XML rather than HTML. This can be confusing and frustrating at first, especially for those of us who have acquired bad habits when writing HTML. The following might appear to be legal:
<P><jsp:getProperty name="simple" property="userID"></P>
But in fact, the above line will not work and will result in an exception and stack trace within the JSP. That's because all tags in XML must be closed somehow. If a <tag> has no matching </tag>, then it must indicate that it closes itself with <tag/>. Thus, the above line must actually be written as:
<P><jsp:getProperty name="simple" property="userID"/></P>Custom actions are merely syntactic sugar for Java methods. Each tag library defines a set of actions. For example, the jsp tag library defines three actions: “getProperty”, “setProperty” and “useBean”. Each action is defined by a single Java class, known as a tag handler.
To define a tag library, we create an XML file known as a tag library descriptor, or TLD. The TLD connects each action to its appropriate tag handler class, listing optional and mandatory attributes, as well as other information about the tags.
To use our custom actions within a JSP, we use a special directive to load our TLD. This helps the JSP engine to validate the custom tags within our JSP, as well as to find the tag handler class associated with these actions.
We will now define one simple custom action in order to understand the underlying mechanics of working with tag handler classes, TLDs and JSPs.
Our custom action will be a “hello” tag, which takes an optional “firstname” parameter. If the parameter is there, our tag will produce a simple “hello” message to the named user. If the parameter is missing, our tag will produce a generic “hello” message.
The first step is to write a simple tag handler that will implement this functionality. Such a tag handler is shown in Listing 1, defining the HelloTag class. I put the HelloTag.java source file, along with all JSP- and servlet-related classes, under the $TOMCAT_HOME/classes directory. Since HelloTag.java is in the il.co.lerner package, and since $TOMCAT_HOME on my machine is /usr/java/jakarta-tomcat-3.2.1, this means I placed my Java source file in:
After compiling HelloTag.java into HelloTag.class, this tag handler can be incorporated into one or more tag libraries.
Each tag handler class must implement one of two different standard interfaces, Tag or BodyTag. (The latter is for custom actions that have a body between their opening and closing tags, rather than those we will discuss this month, which have no body.)
In practice, there is no reason to implement these interfaces. It is easier and more practical to inherit from the TagSupport and BodyTagSupport classes, which provide default implementations for the interfaces. By subclassing TagSupport, we can save ourselves some work, overriding only those methods for which we don't want the default behavior. In the end, our implementation of HelloTag requires only three methods: setFirstname, doEndTag and release.
The first method, setFirstname, looks and acts just like a JavaBean property-setting method, taking a single argument and returning void. setFirstname is invoked automatically when the JSP engine encounters our custom action with a “firstname” parameter. The parameter value is set to the value passed in the tag. As with JavaBeans, the method that sets firstname must be named setFirstname, with a capital “F”.
Our second method, doEndTag, is invoked when the JSP engine encounters our custom action's closing tag. The doEndTag method takes no arguments and returns an integer. But instead of returning an integer, we will return one of the symbolic constants provided for us. Normally, we will return EVAL_PAGE, which tells the JSP engine that it should continue to evaluate the remainder of the JSP from which our custom action was invoked. If we wish to stop the JSP engine from evaluating the file any more, either because we have encountered an error or because we want to forward the user to another URL, we can return SKIP_PAGE instead.
Inside of doEndTag, we can place any Java code we might like. In addition to any instance variables we create, we have access to information about the JSP itself, including its HTTP request and response. This is how we can write information to the user's browser, replacing the custom tag with HTML, XML or plain text. (Custom actions generally return plain text, allowing the JSP author to choose how that text will be formatted.) Using the PageContext object, defined by our TagSupport superclass, we can retrieve an output stream and send data to it:
Finally, we define the release method, which takes no parameters and returns void. release() is invoked when the custom action has finished execution, and it gives the tag handler class a chance to clean up after itself. In general, this means setting each of the instance variables to null, but it might also involve closing a connection to a relational database or sending information to the error log. In HelloTag.java, we simply assign firstname the null value, and then ask our superclass to nullify each of its own values.
Now that we understand each of the individual methods in HelloTag, how do they work together? When a JSP contains a custom action mapped to our class (via the TLD, described below), each of the action's parameters invokes a “set” method in our class. For example, someone passing the parameter firstname=“foo” will effectively invoke setFirstname(“foo”).
Since we want to make firstname an optional parameter, we give it a default value (null) when we first create it. When the JSP engine finishes evaluating our custom action, it invokes doEndTag and looks at the value of firstname. If firstname is null, it sends a generic (“Hi there!”) message to the end user. If firstname is non-null, however, doEndTag uses its value to send a more personal message to the end user.
When the custom action has finished executing, the JSP engine invokes release(), resetting firstname and a number of other objects.
|Dynamic DNS—an Object Lesson in Problem Solving||May 21, 2013|
|Using Salt Stack and Vagrant for Drupal Development||May 20, 2013|
|Making Linux and Android Get Along (It's Not as Hard as It Sounds)||May 16, 2013|
|Drupal Is a Framework: Why Everyone Needs to Understand This||May 15, 2013|
|Home, My Backup Data Center||May 13, 2013|
|Non-Linux FOSS: Seashore||May 10, 2013|
- Dynamic DNS—an Object Lesson in Problem Solving
- Making Linux and Android Get Along (It's Not as Hard as It Sounds)
- Using Salt Stack and Vagrant for Drupal Development
- New Products
- A Topic for Discussion - Open Source Feature-Richness?
- Drupal Is a Framework: Why Everyone Needs to Understand This
- Validate an E-Mail Address with PHP, the Right Way
- RSS Feeds
- Readers' Choice Awards
- Tech Tip: Really Simple HTTP Server with Python
- BASH script to log IPs on public web server
1 hour 30 min ago
5 hours 6 min ago
- Reply to comment | Linux Journal
5 hours 38 min ago
- All the articles you talked
8 hours 2 min ago
- All the articles you talked
8 hours 5 min ago
- All the articles you talked
8 hours 6 min ago
12 hours 31 min ago
- Keeping track of IP address
14 hours 22 min ago
- Roll your own dynamic dns
19 hours 35 min ago
- Please correct the URL for Salt Stack's web site
22 hours 47 min ago
Enter to Win an Adafruit Pi Cobbler Breakout Kit for Raspberry Pi
It's Raspberry Pi month at Linux Journal. Each week in May, Adafruit will be giving away a Pi-related prize to a lucky, randomly drawn LJ reader. Winners will be announced weekly.
Fill out the fields below to enter to win this week's prize-- a Pi Cobbler Breakout Kit for Raspberry Pi.
Congratulations to our winners so far:
- 5-8-13, Pi Starter Pack: Jack Davis
- 5-15-13, Pi Model B 512MB RAM: Patrick Dunn
- 5-21-13, Prototyping Pi Plate Kit: Philip Kirby
- Next winner announced on 5-27-13!
Free Webinar: Hadoop
How to Build an Optimal Hadoop Cluster to Store and Maintain Unlimited Amounts of Data Using Microservers
Realizing the promise of Apache® Hadoop® requires the effective deployment of compute, memory, storage and networking to achieve optimal results. With its flexibility and multitude of options, it is easy to over or under provision the server infrastructure, resulting in poor performance and high TCO. Join us for an in depth, technical discussion with industry experts from leading Hadoop and server companies who will provide insights into the key considerations for designing and deploying an optimal Hadoop cluster.
Some of key questions to be discussed are:
- What is the “typical” Hadoop cluster and what should be installed on the different machine types?
- Why should you consider the typical workload patterns when making your hardware decisions?
- Are all microservers created equal for Hadoop deployments?
- How do I plan for expansion if I require more compute, memory, storage or networking?