I've heard it again and again and again: "I love writing scriptless JSP pages using the JSTL and EL, but dang, why isn't there a way to reference constants in the EL?!?!"
This constant consternation regarding constants is a refrain that resonates strongly with me. When I first started writing JSP pages without scriptlets and scriptlet expressions, the inability to easily reference class constants was a sorely missed capability.
In this article, the term class constant is used to mean a class field with the modifiers public
, static
and final
.
I even flirted with cheating a little by using scriptlet expressions to reference class constants in otherwise scriptless pages. This dalliance didn't last very long as not only did it violate my sense of architectural integrity, it broke down in places where scriptless body content must be maintained (for example: within tag files and within the bodies of custom actions based on SimpleTag
).
One particular area in which I felt the pain most intensely was in the naming of the form elements within an HTML <form>
to be submitted to my action servlets...
The Form Element Names Example
In my web applications, I dislike repeating literal string values in more than one place. Heck, that applies to any literal values — not just strings. Few would argue that this is not a good practice to follow.
I also usually employ a pattern in which the aggregate collection of data submitted as part of a request is abstracted into a class that understands how to deal with that data. These classes are responsible for collecting the data from the request parameters, performing any conversions from string data to other formats (integers, dates and so on), performing simple context-free validations, and in general, making it easy for the action servlets to interact with the submitted data on an abstracted level. This pattern, which I unimaginatively call a Form, should be familiar to users of Struts or similar web application frameworks that employ comparable abstractions.
Within these "Form" classes I define a series of class constants that represent the names of the form elements. Additionally, these constants serve as the keys in a map of the submitted form data that the class represents. As an example, for a typical "login form", you might see:
package org.bibeault.jrjournal.ccc.test;
public class LoginForm implements Form { 1
public static final String KEY_USERNAME = "username"; 2
public static final String KEY_PASSWORD = "password";
...
1 The LoginForm
class is declared. The Form
interface being implemented is defined by the light-weight framework that I employ and will not be further discussed as it is not particularly relevant to this discussion.
2 For each named form element, a class constant is declared that represents a string literal containing the name of the element. In this example, our form is a simple one; defining only two elements.
In this way, the string literals that represent the form element names are defined once as class constants and then referenced throughout the application using compile-time class constant references. This prevents simple typos in the names of the form elements from turning into hard-to-find run-time bugs. Rather, a typo in the referenced constant name induces a compile-time error that is easy to find and fix quickly.
Prior to the advent of scriptless pages, you might have found the following form elements on a JSP page employing the defined LoginForm
to create an HTML login form (within an appropriate <form>
tag, of course):
<div>Username: <input type="text" name="<%= LoginForm.KEY_USERNAME %>"/>
</div>
<div>Password: <input type="password" name="<%= LoginForm.KEY_PASSWORD %>"/>
</div>
Because compile-time class constants are being referenced (rather than hard-coding the names of the elements) bothersome run-time bugs that might be introduced by mistyping the element names are avoided. For example, mistyping the hard-coded element name as "usrename"
would result in a run-time bug (in which a call to getParameter()
on the servlet request using the correct name would always result in a null value). By contrast, mistyping the class constant reference as LoginForm.KEY_USRENAME
would result in a loud and obnoxious — but quick and easy-to-fix — compile-time error when the page is translated.
But within a scriptless page we're rather sunk. Because the JSP EL (Expression Language) makes no provisions for accessing class constant values, we have no means to easily replace the scriptlet expressions that reference the class constants representing the element names with scriptless EL expressions.
Wouldn't it be nice to be able to use something like:
<div>Username: <input type="text" name="${LoginForm.KEY_USERNAME}"/>
</div>
<div>Password: <input type="password" name="${LoginForm.KEY_PASSWORD}"/>
</div>
But we know that the EL makes no such provisions for referencing class constants in this or any other direct manner.
What to do? What to do?
We want to avoid resorting to scriptlets, but we also want to do something better than falling back to hard-coded string literals in the JSP pages.
What to do?
Reflection to the Rescue!
As the EL does not provide any mechanism to allow class constant references on the pages, we'll use some "Java voodoo" to do it ourselves! And the voodoo of which I speak is none other than Java reflection: one of the handiest tools in our Java toolbox.
Regardless of what we decide to do on-page, we're going to need a means to look up the value of a class constant given its fully qualified name. To use an example from our previous discussion, we'd like to be able to use a string such as org.bibeault.jrjournal.ccc.test.LoginForm.KEY_USERNAME to lookup its value "username"
.
Since we'd probably like to reference such a mechanism from multiple places (and because abstraction is just such a wonderful thing in its own right) we'll create a "class constant inspector" class to look up and obtain the value of a class constant given its fully qualified name (which I refer to within this article as its "path").
We will then be able to use this "inspector" in various ways that may suit our needs within the web application.
The ClassConstantInspector
Class
The full implementation of the ClassConstantInspector
class is available in the jar file accompanying this article (see the Resources section), but we will examine the "meaty" parts here.
Using this "inspector" class is quite simple: an instance is constructed supplying the path to the class constant, then the instance can be queried for the value of the constant.
The constructor for the class does the lion's share of the work:
public ClassConstantInspector( String constantPath ) 1
throws ClassNotFoundException, NoSuchFieldException {
FieldPathParser parser = new FieldPathParser( constantPath ); 2
this.field = Class.forName( parser.getDeclaringClassName() )
.getField( parser.getFieldName() ); 3
if (!Modifier.isPublic( this.field.getModifiers() ) || 4
!Modifier.isStatic( this.field.getModifiers() ) ||
!Modifier.isFinal( this.field.getModifiers() ) ) {
throw new IllegalArgumentException( "Field " + constantPath +
" is not a public static final field" );
}
}
1 The constructor is called with the path to the class constant to be inspected; for example, the string "org.bibeault.jrjournal.ccc.test.LoginForm.KEY_USERNAME".
2 The passed path name is broken down into its class name and field name ("org.bibeault.jrjournal.ccc.test.LoginForm" and "KEY_USERNAME" respectively) using the FieldPathParser
class. This latter class simply breaks the passed string into its component parts and will not be further discussed in this article. An implementation using a regular expression to perform the parsing is provided in the ccc.jar
file referenced in the Resources section.
3 The java.lang.reflect.Field instance describing the target field is obtained and stored in an instance variable.
4 The field is checked to make sure that it is a "class constant"; that is, a public
, static
and final
field. If not, an IllegalArgumentException is thrown.
After construction, the value of the field can be queried by calling the getValue
method:
public Object getValue() throws IllegalAccessException, InstantiationException {
return this.field.get( null ); 1
}
1 The value of the field is obtained using the get()
method of Field
. Note that since the field is static (as enforced by the constructor check), null
is passed as the parameter. For non-static fields, this parameter would be an instance of the containing object to query.
As a convenience, a static function that can be used to query for a class constant value is also included:
public static Object getValue( String constantName )
throws NoSuchFieldException, ClassNotFoundException,
IllegalAccessException, InstantiationException {
return new ClassConstantInspector( constantName ).getValue();
}
With that under our belt, let's explore how we can exploit this mechanism to ease our constant constants consternation in scriptless JSP pages.
The <ccc:setConstant>
Custom Action
When enhancing the capabilities of a JSP page, the custom action mechanism is frequently one of the first means considered.
Custom actions may be better known to you as "custom tags". As of the JSP 2.0 Specification, the term action is used rather than "tag" to described the standard and custom tag mechanisms.
One of the first directions we might consider when defining a custom action that uses our new class constant inspector is one that looks up and emits the value of the constant given its path. This would be very much like the JSTL <c:out>
action except that the tag would emit the value of a class constant rather the evaluation of the value
attribute.
We can certainly define such an action using the tools at our disposal. But upon a few moments of reflection (no pun intended) we can determine that such a tag would have some severe limitations; the primary of which would be that, since it is a custom action, it could not be used in contexts where custom actions are disallowed: attributes of other actions, for example.
Rather, we will pattern our class constant custom action on the JSTL <c:set>
tag whose purpose is to evaluate a value and create a scoped variable that represents that value. Such scoped variables can then be used in contexts where custom actions cannot; most interestingly, within EL expressions which can be used liberally within a JSP page, including within the attributes of standard and custom actions.
The TLD entry for our custom action (with the <description>
elements removed for brevity) is:
<tag>
<name>setConstant</name>
<tag-class>org.bibeault.jrjournal.ccc.SetConstantTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>constant</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>scope</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
The ccc.tld
file for the code examples in this article is available in the accompanying jar file referenced in the Resources section).
Its resemblance to the JSTL <c:set>
action is readily apparent, with the constant
attribute specifying the class constant path replacing <c:set>
's value
attribute (which is usually used to specify an EL expression to be evaluated).
The implementation of this tag is surprisingly straightforward:
public class SetConstantTag extends SimpleTagSupport {
private String fieldName;
private String varName;
private String scopeName;
public void setConstant( String value ) { this.fieldName = value; }
public void setVar( String value ) { this.varName = value; }
public void setScope( String value ) { this.scopeName = value; }
public void doTag() throws JspException {
try {
ScopedContext scopedContext = (this.scopeName == null) ?
ScopedContext.PAGE : ScopedContext.getInstance( this.scopeName ); 1
Object constantValue =
ClassConstantInspector.getValue( this.fieldName ); 2
getJspContext().setAttribute( this.varName,
constantValue,
scopedContext.getValue() ); 3
}
catch (Exception e) {
throw new JspException( "Exception setting constant " +
this.fieldName, e ); 4
}
}
}
1 The scoped context name (one of: page
, request
, session
or application
) is used to look up the instance of the ScopedContext
type-safe enumeration that describes the named context. This abstraction associates the scoped context names with the integer values expected by the PageContext
class. Its implementation is available in the ccc.jar
file.
2 The ClassConstantInspector
class is used to lookup the value of the specified class constant.
3 The scoped variable is created using the specified var name and scope.
4 Any exception throw during the tag processing is re-thrown as the root cause of a JspException
.
Applying this new custom action to the "login form" example discussed earlier might look something like:
<%@ taglib uri="http://bibeault.org/tld/ccc" prefix="ccc" %>
...
<ccc:setConstant constant="org.bibeault.jrjournal.ccc.test.LoginForm.KEY_USERNAME"
var="usernameKey"/>
<ccc:setConstant constant="org.bibeault.jrjournal.ccc.test.LoginForm.KEY_PASSWORD"
var="passwordKey"/>
...
<div>Username: <input type="text" name="${usernameKey}"/></div>
<div>Password: <input type="password" name="${passwordKey}"/></div>
With this custom action, we have established a means to reference class constants on our JSP pages without resorting to scriptlets or scriptlet expressions. This represents a huge step forward over hard-coding the literals values in scriptless contexts.
But... (isn't there always a "but"?)
This mechanism is not without issues. Foremostly, it violates my architectural sensibilities in that we now have two variations of actions to create scoped variables: <c:set>
for values derived from EL expressions, and <ccc:setConstant>
for values derived from class constants. Would it not be completely awesome if we could figure out some way to achieve the same ends using just <c:set>
?
But how?
The value of the value
attribute of the <c:set>
action can contain any EL expression, but we've already established that EL expressions cannot reference class constants. So how could we possibly piggy-back on <c:set>
using our class constant inspection mechanism?
Well, the EL may not be able to access class constants but, as of JSP 2.0, it can access static functions via the EL function mechanism. Where can we go with that?
The ccc:constantValue
EL Function
As of JSP 2.0, functions that can be called from within EL expressions can be mapped to static methods of Java classes. See section JSP.2.6 of the JSP 2.0 Specification for details.
Magically — well, by design — we already have a static method that we created in the ClassConstantInspector
class that we can leverage for this very purpose. By adding the following entry in the TLD file:
<function>
<name>constantValue</name>
<function-class>org.bibeault.jrjournal.ccc.ClassConstantInspector</function-class>
<function-signature>
java.lang.Object getValue( java.lang.String )
</function-signature>
</function>
we can enable access to the ClassConstantInspector.getValue() static method from within EL expressions.
So now, in our JSP page, we can use <c:set>
to create scoped variables with the values of class constants.
Applying this to our login form example, we get:
<%@ taglib uri="http://bibeault.org/tld/ccc" prefix="ccc" %>
...
<c:set var="usernameKey"
value="${ccc:constantValue('org.bibeault.jrjournal.ccc.test.LoginForm.KEY_USERNAME')}"/>
<c:set var="passwordKey"
value="${ccc:constantValue('org.bibeault.jrjournal.ccc.test.LoginForm.KEY_PASSWORD')}"/>
...
<div>Username: <input type="text" name="${usernameKey}"/></div>
<div>Password: <input type="password" name="${passwordKey}"/></div>
This is getting better and better! We can now assign the value of class constants to scoped variables using the standard JSTL <c:set>
action rather than resorting to creating our own non-standard action to do so.
With this EL function we could also forgo the scoped variables entirely, using the function directly in the name
attributes of the form elements if you don't mind the wordiness of the function reference.
But (there's that "but" again), since we are hyper-critical perfectionists (well, at least I am), we examine our creation with a discerning eye. And when we do, we identify some limitations:
- The EL function mechanism is only available for JSP 2.0 containers. And while the time has come for everyone to migrate to JSP 2.0 if possible, engineering realities dictate that some people may still need to function in a JSP 1.2 world for the time being.
The custom action we defined in the previous section can be retro-fitted to function in a pre-JSP 2.0 environment if re-implemented using classic tag handling support.
- This mechanism, cool as it may be, may not scale all that well. This EL function mechanism (as well as the custom action we defined in the previous section) requires us to either create a scoped variable for each and every class constant that we wish to reference within the JSP page, or use some pretty long and gnarly-looking notation directly in the markup.
- The EL function mechanism itself could be viewed as "notationally messy". Call me a neat-nik.
So, as powerful as this mechanism is, it may not be a perfect fit for all situations. Can we come up with even more "magic" using our class constant inspection voodoo?
The Power (and Possible Perversion) of the Map
In a previous JavaRanch Journal article we discussed the "Power of the Map" as it relates to the EL. In that article, we discovered that the java.util.Map
interface was a mighty tool for referencing properties in a dynamic fashion. Could this flexibility also help us ease our constant constants consternation?
Recall that in an EL expression such as ${abc.xyz}
— which is a short-hand notation for ${abc['xyz']}
— if the scoped variable abc
implements java.util.Map
then the value of the EL expression is the value for the mapped key "xyz"
.
Can that help us? In the "Power of the Map" article we discussed having the servlet controller construct a Map of all the constants we'd need that we can send to the page as a request-scoped variable, but can we simplify that?
What if, rather than using a HashMap
(or other pre-defined implementations of java.util.Map
), we created our own implementation of java.util.Map
that could look up the value of a key on the fly? Would that be incredibly clever, or would it be a heinous perversion of all that is good? Or both?
The ClassConstantMapper
Class
Considering that the get()
method of java.util.Map
is the only method of interest when accessing Map entries via the EL, let's define a class that implements that interface:
public class ClassConstantMapper implements java.util.Map {
and declare the get()
method required by the java.util.Map
interface as follows:
public Object get( Object key ) { 1
try {
return ClassConstantInspector.getValue( key.toString() ); 2
}
catch (Exception e) {
throw new IllegalArgumentException( "Error looking up value of constant " +
key.toString() + ": " + e.getMessage() );
}
}
1 When an instance of this class is used as a scoped variable, any references to a property of this variable will cause the get()
method to be invoked with the property name as the key parameter.
2 The services of the ClassConstantInspector
are used to determine the value of the class constant specified by the passed key and is returned as the value.
Since the other methods defined by the java.util.Map
interface are either of no interest, or have no real meaning in this context (what would we return for the entrySet()
method? A Set
of all class constants for all the classes in the class path? Absurd!), we throw an UnsupportedOperationException
for every method except get()
. For example:
public Set entrySet() { throw new UnsupportedOperationException(); }
We also need a means to establish a scoped variable of this class so that we can reference it on the pages. We could simply expect that the servlet controller would set it up for the page, or we could define a new custom action that would do it for us on the page itself. But, since our class has a no-args constructor, we could simply employ the services of the <jsp:useBean>
standard action to establish a scoped variable using an id of our choice.
So our login form example becomes:
<jsp:useBean id="constants" class="org.bibeault.jrjournal.ccc.ClassConstantMapper"/>
...
<div>Username: <input type="text"
name="${constants['org.bibeault.jrjournal.ccc.test.LoginForm.KEY_USERNAME']}"/>
</div>
<div>Password: <input type="password"
name="${constants['org.bibeault.jrjournal.ccc.test.LoginForm.KEY_PASSWORD']}"/>
</div>
Note that with this mechanism, the constants
scoped variable can be used to reference any and all constants that we need to use on the page. Also note that since the class constant path used as the "property name" will always contain at least one period character, we will always need to use the [ ] operator notation.
This gives us a similar mechanism to the EL function described in the previous section, but with a slightly less punctuation-heavy notation.
Is this a wonderfully clever leverage of existing mechanisms, or a delicious violation of the intentions of the Map interface? You decide!
Since there is nothing page-specific about this "constant mapper" class, why bother to declare it on each page using the <jsp:useBean>
standard action? If we were to establish the "constant mapper" scoped variable in application scope, it would automatically be available to each and every page in the web application.
The ConstantMapperEstablisher
Class
But how would such an application-scoped variable be best established? If we put a <jsp:useBean>
action on every page specifying a scope of application
that eliminates any advantage of putting the variable into application scope in the first place. We could choose a single page to contain the action, but how could we guarantee that that page will be the first that gets hits after the web application is loaded?
The answer lies not within a JSP at all, but with a servlet context listener. This mechanism allows us to specify a class (actually, any number of classes that we want) that will be notified at web application startup time, giving us the opportunity to execute any initialization code we deem necessary. This is the perfect place to perform any application-level setup such as establishing a scoped variable for our constant mapping class.
The class implements the javax.servlet.ServletContextListener interface and is implemented as:
public class ClassConstantMapperEstablisher implements ServletContextListener {
public static final String PARAM_CONSTANT_MAPPER_NAME = 1
ClassConstantMapperEstablisher.class.getName() + ".classConstantMapperName";
public void contextInitialized( ServletContextEvent servletContextEvent ) { 2
ServletContext servletContext = servletContextEvent.getServletContext();
String varName =
servletContext.getInitParameter( PARAM_CONSTANT_MAPPER_NAME ); 3
if (varName == null) { 4
throw new IllegalStateException( "Context parameter " +
PARAM_CONSTANT_MAPPER_NAME +
" must be specified" );
}
servletContext.setAttribute( varName, new ClassConstantMapper() ); 5
}
public void contextDestroyed( ServletContextEvent servletContextEvent ) {} 6
}
1 Rather than hard-coding the name of the scoped variable to be established into application scope, the name will be specified by a context parameter in the deployment descriptor. In order to avoid possible namespace collisions with other context parameters, the context parameter name is prefixed with the listener class name.
2 The contextInitialized
event is triggered by the container when the context is placed into service.
3 The name of the scoped variable to place into application scope is obtained from the context parameter.
4 If the context parameter that specifies the scoped variable name is missing, complain. Loudly.
5 An instance of ClassContantMapper
is established in application scope using the name obtained from the context parameter.
6 The contextDestroyed
event is triggered when the context is taken out of scope. Nothing need be done in response to this event.
Establishing this class as a context listener for the web application requires two simple entries in the deployment descriptor (web.xml
) for the application; one for the required context parameter, and one for the listener:
<context-param>
<param-name>
org.bibeault.jrjournal.ccc.ClassConstantMapperEstablisher.classConstantMapperName
</param-name>
<param-value>constants</param-value>
</context-param>
<listener>
<listener-class>
org.bibeault.jrjournal.ccc.ClassConstantMapperEstablisher
</listener-class>
</listener>
This effectively creates a new implicit variable named constants
that can be accessed in all of our JSP pages (in the same manner as true implicit variables provided by the container such as requestScope
).
We have now described three different on-page mechanisms to access constants using our class constant inspector. They're a fairly powerful set of tools, each with their strengths for different circumstances, but they all exhibit a tendency towards on-page verbosity; usually in the guise of long constant path string literals (e.g. "org.bibeault.jrjournal.ccc.test.LoginForm.KEY_USERNAME"). Though this isn't anywhere near a crippling deficiency, there may be situations where something terser may be appropriate.
As previously mentioned, in the Power of the Map article we discussed having the servlet controller construct a Map of all the constants we'd need that we can send to the page as a request-scoped variable. This had the advantage of creating a Map that had simply named keys (as opposed to long class constant field paths). Could a variation on this theme using our "inspector technology" enable us to further simplify on-page constant referencing?
The <ccc:constantsMap>
Action
A lot of the time I find that most, or least a large portion, of the constants referenced within a page are defined within the same class. Consider our LoginForm
class example. For all the form elements defined by this abstraction, a class constant representing a string literal for the name of each element is defined. In this example there are only two, but there could be many more for more complicated forms.
We could write code that creates a map of these names and literals (perhaps within the LoginForm
class itself) and have the servlet controller establish it as a request-scoped variable before forwarding to the page. But wouldn't it be nice if the page itself could cause such a Map to be established without reliance on the controller or specialized code in the classes that define the constants?
For precisely that purpose we define the <ccc:constantsMap>
custom action. This action will, given a class name, search for all class constants defined by that class and establish a Map of those constants as a scoped variable.
The TLD entry for this tag:
<tag>
<name>constantsMap</name>
<tag-class>org.bibeault.jrjournal.ccc.ConstantsMapTag</tag-class>
<body-content>empty</body-content>
<attribute>
<name>className</name>
<required>true</required>
<rtexprvalue>true</rtexprvalue>
</attribute>
<attribute>
<name>var</name>
<required>true</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
<attribute>
<name>scope</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
</attribute>
</tag>
The implementation of the action, for once not relying upon our constant inspector class (though using the same voodoo), is:
public class ConstantsMapTag extends SimpleTagSupport {
private String className;
private String varName;
private String scopeName;
public void setClassName( String value ) { this.className = value; }
public void setVar( String value ) { this.varName = value; }
public void setScope( String value ) { this.scopeName = value; }
public void doTag() throws JspException {
try {
Map constantsMap = new HashMap(); 1
Class declaringClass = Class.forName( this.className );
Field[] fields = declaringClass.getFields(); 2
for (int n = 0; n < fields.length; n++ ) { 3
if (Modifier.isPublic( fields[n].getModifiers() ) && 4
Modifier.isStatic( fields[n].getModifiers() ) &&
Modifier.isFinal( fields[n].getModifiers() ) ) {
constantsMap.put( fields[n].getName(),
fields[n].get( null) ); 5
}
}
ScopedContext scopedContext = (this.scopeName == null) ? 6
ScopedContext.PAGE : ScopedContext.getInstance( this.scopeName );
getJspContext().setAttribute( this.varName, 7
constantsMap,
scopedContext.getValue() );
}
catch (Exception e ) {
throw new JspException( "Exception setting constants map for " +
this.className, e );
}
}
}
1 A Map instance to serve as the scoped variable is created.
2 By using the getFields()
method, we obtain not only fields defined by the named class, but any fields inherited by extended classes as well. To limit the search to fields declared within the named class we would have used the getDeclaredFields()
method.
3 All the located fields are iterated over.
4 Fields not considered "class constants" — that is, public, static and final fields — will be ignored.
5 Any class constants are added to the Map using the field name as the key and the field value as the mapped value.
6 The scope name string is converted to its ScopedContext
type-safe enum instance.
7 Finally, the Map is established as a scoped variable using the specified name in the specified scope.
This action's usage for our login form example would be :
<%@ taglib uri="http://bibeault.org/tld/ccc" prefix="ccc" %>
...
<ccc:constantsMap
className="org.bibeault.jrjournal.ccc.test.LoginForm"
var="LoginForm"/>
...
<div>Username: <input type="text" name="${LoginForm.KEY_USERNAME}"/></div>
<div>Password: <input type="password" name="${LoginForm.KEY_PASSWORD}"/></div>
Compare this with our original, script-infested example and the "Wouldn't it be nice" scenario that follows it. Is this not just too cool?
But once again, we have a ubiquitous "but"...
We've found a way to make the notation for making class constant references on the pages nice and easy, but have we really gained anything over just hard-coding the values directly onto the pages? Recall that when we did so, any errors in the names resulted in a non-specific run-time bug ("Why is the blasted username parameter always blank?"). But when we used scriptlet expressions that referenced class constants and made a similar typo, an error was reported at translation time. What happens when we use our <ccc:constantsMap>
mechanism?
When an EL reference is made to a Map using a key that does not exist, no error is thrown. Rather, a default is provided for the evaluation of the EL expression in which the reference was made. In a textual context, such as we are using here, the default is the empty string. Therefore, when we make a mistake such as ${LoginForm.KEY_USRENAME}
in typing the class constant name, the result will be a blank in the place of the EL expression.
Well, poo! While that's marginally better than simply hard-coding the name -- errors will result in predictable and invalid HTML patterns such as name=""
which is easier to search for than mis-typed names -- it's still a far cry from the satisfying in-your-face error thrown when we use scriptlet expressions and constant references.
Is there something we can do about that?
The ClassConstantsMap
Class
Because our class constant inspector is a run-time mechanism, we can't detect and report errors at translation time. But to achieve the goal of having the page blow up in our faces when we make a error in class constant references, a run-time error, as long as it is loud, obnoxious and obvious, will serve just as well.
Our problem arises from the fact that when the get()
method of any java.util.Map
is called with a key that does not exist, it returns a null
which the EL goes about converting to a default value suitable for the referencing evaluation context.
That's not what we want in this case: what we want is for the Map to raise hell if we try to fetch a nonexistent key. Since all the other behaviors of the java.util.HashMap
class that <ccc:constantsMap>
places into a scoped context work exactly as we would like, all we need to do in order to achieve the behavior we'd like to see is to extend the HashMap
class with a new implementation of its get()
method.
The implementation is simple:
public class ClassConstantsMap extends HashMap {
private String className;
public ClassConstantsMap( String className ) { 1
super();
this.className = className;
}
public Object get( Object key ) { 2
if (super.get( key ) == null) 3
throw new IllegalArgumentException( "Key " + key +
" could not be found in class constant map for " +
this.className );
return super.get( key ); 4
}
}
1 The constructor accepts the name of the class from which the Map is being constructed. This is used primarily during error reporting, which is the entire reason for the existence of this class in the first place. This class name is stored in an instance variable for later use.
2 Like any other implementation of java.util.Map
, the get()
method is invoked when an EL reference is made.
3 A check for the existence of an entry for the passed key is made and an exception is thrown if one does not exist. Note that no special considerations are made for entries that do exist but have a value of null
. In our scenario, we are making no distinctions between null entries and entries that do not exist.
Since neither null values nor null keys make much sense in this scenario, it would also be an easy matter to extend the HashMap
's put()
method to not accept null
for either the key or the value of an entry.
4 If the entry exists, it is returned via the extended HashMap
's get()
method.
By changing the Map class used in the handler for <ccc:constantsMap>
from HashMap
to our new ClassConstantsMap
class, we finally achieve our goal. We can easily make class constant references on the JSP page without resorting to scriptlet expressions, and if we make referencing errors, we will learn about them very quickly and spectacularly.
This time, there are no substantive "but's", and that's cool!
Happy Days are Here Again
In this article we've established a general means for determining the value of a class constant and explored a number of ways to exploit this capability to ease the pain of referencing class constants in JSP pages.
Hopefully, we have either created ready-made tools that you can begin to use in your scriptless JSP pages immediately, or have given you ideas and technology fragments that you can exploit in whatever manner makes the most sense for your own web applications.
Resources
Files
ccc.jar
-
The jar file containing the classes and TLD described in this article. This jar file is suitable for dropping into a web application's
WEB-INF/lib
folder in order to use these tags and classes as is.
This jar file also contains the source code for all the classes described in this article, as well as the generated javadocs for the classes and the TLD.
You will note that the classes in the jar file differ in a number of ways from those presented in this article. Some of the code presented in the body of the article has been simplified to focus upon the concepts being discussed. The actual classes as included in the jar file have more robust error checking, and common operations are factored into superclasses. Of particular note:
- The custom action implementations define
TagExtraInfo
inner classes to perform translation-time checking of their static attributes.
- Since both of the defined custom actions share the declaration and semantics of the
var
and scope
attributes, a "tag support" superclass for these tags handles those semantics. This superclass is suitable for extending by any custom action supporting these common attributes.
- The JSP 2.0 Specification
- The definitive reference for JSP 2.0 information.
Javadoc API
java.lang.reflect
- The javadoc API descriptions for the reflection classes.
java.util.Map
- The javadoc API for the java.util.Map interface.
javax.servlet.ServletContextListener
- The javadoc API descriptions for the javax.servlet.ServletContextListener class.