One sometimes unexpected side-effect of moving to JSP 2.0 "scriptless pages" is the amount of thought it forces one to focus on the "controller-to-view interface". To me, this side-effect is another major advantage (in addition to the welcome simplification of the pages themselves) to making this transition.
"How on Earth is forcing me to actually think of something an advantage?" you may ask. My answer to that is that more thought to this interface usually means a better interface. And better interfaces equal better program structure. And unless you haven't written a line of code in your life, you know that better program structure equals happier programmers.
Now it's not that there's never been any thought given to this interface before, it's just that in the pre-scriptless era it was common -- because it was just so easy -- for us to code the controller servlet to throw any old data structure onto the request knowing that we could write all the Java we wanted on the page to deal with those structures.
Some of us, and I'll give myself credit to be among those, always had the notion that the less Java on the pages, the better, and gave some amount of thought to gearing the structures passed as request-scoped variables to the needs of the page. But still, because any arbitrary Java code could be included on-page, it wasn't a paramount consideration.
That has all changed. With scriptless JSP pages, where JSP 2.0 Expression Language (EL) expressions are the primary means to reference the scoped variables, adjusting the nature of the scoped variables to suit the pages becomes something to which more thought must be applied.
Over the course of converting a number of my client's web applications to use JSP 2.0 and scriptless pages, I've learned a lot about structuring the controller-to-view data, and one thing that has become apparent to me is the overlooked power of the Map as applied to this problem space.
By "Map", of course I mean implementations of the java.util.Map
interface.
Recall that in the Expression Language, the fetch operators (the dot and the square brackets) tailor their semantics to the type of their left operand. In the EL expression:
${xyz.abc}
or the equivalent
${xyz['abc']}
how the EL evaluator determines the value of abc
depends upon the type of the value for xyz
.
If xyz
evaluates to a discrete element, the evaluator attempts to treat abc
as a JavaBean property, and to determine its value using the accessor method getAbc()
.
If however, xyz
evaluates to a java.util.Map
implementation, the evaluator attempts to resolve the value of abc
as the value of the entry with key 'abc'
. (Interesting things also happen if xyz
is an implementation of java.util.List
or an array, but that, as they say, is another show).
This is powerful stuff and opens up the usage of the Map in this context in ways for which we might not previously have thought of using a Map construct.
One of the web applications that I have updated to use scriptless pages is a community-based recipe sharing site. The app is membership-driven, with each member assigned to a "role" that grants them certain permissions for the various actions that can be performed within the application.
Lowest is the guest role where unregistered visitors can browse the site contents. Registered visitors with the member role can submit recipes to be added to the site contents. Paid members with the gold member role can create private recipe collections and have access to some other advanced features. Moderators can approve submitted recipes, remove member accounts, and perform other policing actions. The most privileged role of administrator can perform all site actions.
Each role is actually a set of discrete permissions that determine what actions visitors possessing that role can initiate. The "add recipe" permission, for example, is granted to all roles but "guest".
Each visitor to the site is identified via an instance of a Member
object in that visitor's session. In the JSP 1.x version of the application, a method on this object exposed the permissions that the member was granted based upon the role assigned to that member.
If we imagine a location on the site where an "Add Recipe" button will appear for those visitors that have the "add recipe" permission (which would be all but "guests"), you might have seen JSP code like this:
<% Member member = (Member)session.getAttribute( "member" ); if (member.hasPermission( "ADD_RECIPE" )) { %> <button type="button" onclick="onAddRecipe()">Add Recipe</button> <% } %>
Note: this is a simplification of the actual code. There were ownership considerations that frequently needed to be taken into account, and the strings for the permission names were exported as String constants rather than being hard-coded into the pages. But I'm trying to keep the example code focused on the point to be made.
Converting this to a scriptless equivalent using JSTL tags and the EL might seem straight-forward at first, but when we try to access the "hasPermission" method of the member object, we run into problems.
The EL can easily access properties of objects such as Member
, but is unsuitable for calling general-purpose methods, such as hasPermission()
, that take parameters.
The means to determine whether a member possesses a particular permission needed to be changed to a mechanism that was "EL-friendly".
One tactic could have been to replace the general purpose method with discrete property accessors. Under this approach each and every permission would need to become a property of Member
:
public boolean getHasPermissionAddRecipe(); public boolean getHasPermissionApproveRecipes(); public boolean getHasPermissionBanMember(); ...
and so on.
I rejected this approach due to a number of problems. Not only did it not scale well -- as the number of permissions grows, so do the number of accessors necessary -- it artificially makes each individual permission a property of Member
. While I had no problem modeling the set of permissions as a property of a Member
, making the individual permissions Member
properties violated my "OO sensibilities" as it introduced a coupling between the Member
object and the available set of permissions. My original model had the Member
interface independent from the set of available permissions, and I certainly did not want to gain simplification of the JSP pages at the expense of compromising the model abstraction.
The next thought that occurred to me was to consider using EL functions. Since the problem seemed to be that EL property expressions didn't allow for general function calls, perhaps defining some EL functions would do the trick.
EL functions are means to access static methods defined on a Java class. By creating a static function, perhaps on the Member
class itself, I could pass the permission name as a string. But because the method must be static, I'd also need to pass the member object as well.
In a TLD file, such a function could be defined as:
<function> <name>hasPermission</name> <function-class>com.whatever.membership.Member</function-class> <function-signature> boolean hasPermission( com.whatever.membership.Member, java.lang.String permission ) </function-signature> </function>
and on-page, its usage would be (assuming that the TLD containing the function was declared and mapped to the prefix 'membership'):
<c:if test="${membership:hasPermission( sessionScope.member, 'ADD_RECIPE' )}">...
Although I liked this solution much better than adding individual properties for the various permissions, I thought that it was a bit wordy on the page, didn't read well at all, and the static nature of the method seemed like a bit of a OO wart on the Member
class.
What I really needed was a different means to map the name of a permission to its granted/denied setting.
Light dawned when I finally realized that the solution was in the problem statement itself all along: I needed a means to map the name of a permission to its granted/denied setting.
Thinking back to our previous example of ${xyz.abc}
, we saw that not only could abc
be a JavaBean property, but it could also be a map key. And one great advantage that a map key has over a JavaBean property, is that it is run-time assignable as opposed to the compile-time hard-coding of accessor methods.
My solution was to create a new property for the Member
class that represented the set of permissions granted to that member. I named this property hasPermission
and its value is a Map that associates a granted permission name to the value Boolean.TRUE
.
Hence, its accessor is:
public Map getHasPermission();
and it can be used on-page thusly:
<c:if test="${sessionScope.member.hasPermission.ADD_RECIPE}"> <button type="button" onclick="onAddRecipe()">Add Recipe</button> </c:if>
which makes for a beautifully readable EL test expression for the <c:if>
tag.
All it takes is to add the permission names to be tested as a key to the Map -- in our example "ADD_RECIPE" -- with a value that evaluates to true (such as Boolean.TRUE
).
Since the expression will evaluate (and without error) to false in the event that the Map lacks a specific key, we need only add granted permissions to the Map. Any denied permissions need not be explicitly added with a value of Boolean.FALSE
as the expression will evaluate to false whenever the named entry cannot be found in the Map.
While this does indeed make access from the JSP page very readable, the signature of the accessor is rather odd-looking when used from Java code (permission checking is also performed quite often within the server-side code). But this need not be an issue since nothing prevents us from leaving the original hasPermission( String permissionName )
method on Member
for use within Java code, and leave the new hasPermission
property accessor for use solely from EL references.
Note on this example:
Even though the Add Recipe button is hidden on pages displayed to visitors that do not have permission to perform the "add recipe" action, this in no way constitutes adequate security against unauthorized attempts to execute this action. The server-side code for performing this operation (in the business layer of the application) performs its own stringent series of checks to make sure that the requested action is allowed by the visitor's granted permissions.
The point of hiding it on the UI is merely so that visitors without adequate permissions are not presented with a UI element that will result in a "permission denied" error upon its usage.
A recent post in the JSP Forum of the JavaRanch Big Moose Saloon brought up another interesting case study.
In the post, the author -- another firm believer in the "goodness and light" of scriptless JSP pages -- lamented the fact that without scriptlets or scriptlet expressions, it was impossible to reference static constants defined in Java classes. Indeed, using the facilities of the Expression Language, this is not possible.
Such references proved quite useful in pre-scriptless applications. Take for example, a common bug introduced by a simple typo naming a form element on a JSP page. Let's say that in servlet SomeServlet
to which a form on a page is to be submitted expects a request parameter named "description":
String description = request.getParameter( "description" );
But on the page, the form element is coded as:
<input type="text" name="decsription"/>
This error causes the request parameter value in the servlet to always be null since no form element with the expected name actually exists. This type of bug is easy enough to fix, but can waste a great deal of time trying to find.
By using a string constant defined on the servlet (or in some other abstraction as we will discuss later):
public static final String KEY_DESCRIPTION = "description";
this type of bug can be avoided by using the constant in the servlet itself:
String description = request.getParameter( KEY_DESCRIPTION );
and within the JSP as:
<input type="text" name="<%= SomeServlet.KEY_DESCRIPTION %>"/>
assuming that SomeServlet
has been imported into the JSP.
While this may be somewhat notationally heavy, it has the distinct advantage of generating a compile-time error should any typo in the key reference be made.
Within a JSP 2.0 scriptless page, such scriptlet expressions are not possible. And while even the power of the Map cannot achieve the invaluable utility of a compile-time check, it can be used to make such errors at least a little easier to spot.
Let's take the well-known example of a simple login form which consists of two entry fields: a username and a password. Knowing that simple string constants exported from a servlet, or any other class for that matter, will have no utility for our scriptless JSP pages, we'd like to use a Map to create references that can be readily and readably used on the JSP pages.
Since we're going to be exporting more information about the form and its elements than a simple set of string constants, it makes sense to formalize an abstraction of our "form" and its elements, decoupling it from any one specific servlet (or page).
In this abstraction we'd like to export information that is usable from any servlet that deals with our "login form", as well as from the JSP page that will implement the UI elements that present the login form to the user.
Here's how it might be done:
public class LoginForm { public static final String KEY_USERNAME = "username"; public static final String KEY_PASSWORD = "password"; public static final Map FORM_KEYS = new HashMap(); static { FORM_KEYS.put( "KEY_PASSWORD", KEY_PASSWORD ); FORM_KEYS.put( "KEY_USERNAME", KEY_USERNAME ); } }
In this class the form keys are exported as string constants that are easily used by any Java code (particularly servlets) that need to reference them.
A Map of the form element keys whose keys are named using the conventional naming format for constants is also exported. When placed onto request scope by code such as:
request.setAttribute( "FormKeys", LoginForm.FORM_KEYS );
the form elements in the scriptless pages can now be coded as:
<input type="text" name="${FormKeys.KEY_USERNAME}"/> <input type="password" name="${FormKeys.KEY_PASSWORD}"/>
Because of the names and naming conventions we used to create the Map and the scoped variable, the purpose of the references is immediately recognizable as mimicking constant values.
Note: it could be argued that even though the spirit of these references is to be used as constant values, that, since they are not, they should not be named like constant references. I'll leave such philosophical discussions for another time.
Operationally, if the FormKeys
scoped variable does not exist or if the key name is mis-typed, no error is thrown, but blank output is rendered.
While this is nowhere near as useful as having a compile-time (or even run-time) error thrown that reaches out and slaps you in the face, a typo in the JSP page now results in a pattern that should be easier to spot by inspection, as well for which automated searches can be created. If, for example, the following error is made:
<input type="text" name="${FormKeys.KEY_USRENAME}"/>
the resulting rendered HTML would be:
<input type="text" name=""/>
which would be easier to spot in the rendered page than a hard-coded typo like:
<input type="text" name="usrename"/>
and for which it is easy to create an automated search that finds instances of the name
attribute with an empty value.
Whether it is worth all this setup for what some might think is marginal gain is up to the individual to decide. I include it here as another example of how a Map can be used in conjunction with JSP scriptless pages in ways that might not be obvious.
However, I will assert that the utility of creating a "form abstraction" as described here is more far-reaching than this example. Imagine that such a class was extended to be able to fetch its known parameters given a request instance, could convert the parameter values from strings to integers, dates or other non-string types as appropriate, and perhaps even perform simple, context-free validations on its elements. This would be a powerful abstraction that not only collects the form elements into a cohesive entity, but decouples the concepts of the "form" from the servlets (or other items) that reference it.
The utility of such an abstraction might not seem evident when thinking of our simple login form that possesses two simple string fields. But imagine a more complex form; say, the myriad fields of various types that go into selling an item on eBay®. Collecting all this information into an abstracted instance could significantly simplify the code that needs to deal with it.
In this article, we've looked at a number of ways to use the Map interface in conjunctions with JSP scriptless pages. Because Map keys can be referenced in an Expression Language expression as if they were properties, the Map provides a clever way to create "property names" that are not, or cannot, be known at compile time in a way that is not possible with formal JavaBean properties.