I would appreciate comments on the following draft of an article I've
just added to the Web Application site. Go to
http://virtualschool.edu/wap and click "The Problem" in the left
column for the original.
I plan to change the name WAP to reduce confusion with the WAP
wireless protocol. I'm still struggling for the right descriptor for
the anti-pattern I complain of below. The current name, "Misusing
dynamic binding..."
is in the right ballpark but doesn't really hit the nail on the head.
I'd appreciate any suggestions.
Although the WAP approach is not standards-focused, it compliments
the dynamic options of web app environments like Tomcat, JSP or
Velocity. I believe should be available there. The effort is
negligble and I'd be more than happy to do it.
I'd appreciate suggestions as to whether and particularly how to do
so. WAP requires no changes to the substrate; it is strictly a 70kb
addon to the servlet engine substrate.
The Problem
Misusing dynamic binding
when static binding would do
Html-based languages treat web applications as collections of files
linked to each another via <a href> and <form> commands coded inside
the files. Dynamic (procedural) capabilities are bolted onto this
data-oriented file-based framework via html extensions such as JSP's
<% escape sequences %>.
This is one of many examples of an anti-pattern that pervades the web
application frameworks and libraries I've used to date, namely
misusing dynamic binding in situations where static binding could do
the job better.
In the above example, the JSP page hard wires a dynamically bound
link to the target page in its <a href> command instead of calling a
statically bound, type-checkable method of the target object, since
JSP doesn't even support the latter option. JSP pages reference other
jsp as files, not objects, simply because web browsers use html which
does it this way. Although html's restrictions do apply to browsers,
they do not apply at all to the server side of a web application. Web
apps after all are programs that send html text to a client. They
should be built as networks of objects, not collections of files as
in JSP.
I'll demonstrate how very similar anti-pattern are common in other
environments in what follows. In general, WAP provides statically
bound type-checkable substitutes for each example of this
anti-pattern I've encountered in my work. Although I've not done this
myself, WAP's statically bound solutions can and I believe should be
used within any Java web application environment, including JSP.
Web applications are not files
But html-based language treat them as such. For example you write <a
href="filename.jsp"> to link to another page in JSP. Regardless of
what goes on thereafter (for example, when JSP pages are compiled
into servlet objects), the objects identify each other as members of
a collection of files and not as a network of first-class objects. If
filename.jsp does not exist, the error won't be noticed until it
causes an Error 404. Maybe you catch it during testing, but often it
goes unnoticed until its reported by an unfortunate end-user.
The solution is so obvious and the advantages of a solution so large
that it is surprising that the solution is not provided by any of the
web application environments I've examined to date. WAP's solution is
to provide an AbstractPage class that applications subclass to define
each and every web page. Each page initializes a final static public
ClassName instance variable with a singleton instance of that class.
When another page needs to emit a <a href>, it does so by calling
ClassName.instance.emitLink(ctx). A similar method, emitForm(), emits
<form> commands.
Presto, invalid links will now be reported at compile-time. If the
application compiles, its links to dynamic pages are all valid.
Extending on this approach, WAP also provides an AbstractLink class
that applications subclass to specify links to legacy html files,
graphic files, external servers and so forth. The subclass can
provide warnings of files that don't exist when the application is
starting up, and another common source of server errors is gone.
The pattern demonstrated here is followed throughout WAP. WAP simply
provides a statically bound and type checked alternative to
inappropriate use of dynamic binding. The dynamically bound
construct, <a href="filename.jsp"> is simply replaced by the
statically bound equivalent, OtherPage.instance.emitLink(ctx), This
does exactly the same thing as far as the browser is concerned, but
invalid links will be reported at compile time and this imposes no
run-time look up or compilation overhead at all.
There is a trade off operating here. The static alternative trades
the ability to revise html files underneath a running application for
the ability to check all links at compile time. You will need to run
make to change the running image and restart the server if you
haven't configured tomcat to auto-reload changed classes. Fortunately
Jikes makes this nearly instantaneous. Unlike JSP, there is no need
for a Java SDK inside the running image nor even to have the Java SDK
on the deployment machine for significant savings in space, speed and
security.
Fields are not Strings
Yet servlet engines treat them as such. For example, the servlet
engine's request -> getParameter("name") method returns the String
value of the named parameter if it exists or null under various error
conditions. Template engines exhibit the same anti-pattern by using a
Hashtable to pass named values between Java code and the template
(presentation) layer.
String is a final class in Java, so an application cannot define
application-specific methods by defining application-specific String
subclasses for functions like input validation. So this logic winds
up in the application itself.
Fields are not Strings. In an application that manages addresses,
they should application-specific objects like Street, Zipcode,
USState, Country and Zipcode. They aren't simply used only during
input validation and converted back to Strings. They displace Strings
and their evil consort, the Java null object, from the internal API.
These application-specific field objects are the very backbone of an
application... fields are to beans as atoms are to molecules.
The solution is again straightforward and easily added to any
environment. The servlet passes each WAP application a servlet
context object (named Ctx for brevity) that contains the
request-specific state information applications use to communicate
with their environment. The Ctx object provides a getField method
that generates validated instances of application-specific objects
like this:
Email email = (Email)ctx.getField("email", addressBean.getEmail());
The first argument is the request parameter name. The second is the
default value which getField will clone (copy) and return if the
requested parameter is empty ("") or null. In this example, the
default value is obtained from addressBean. In an application where
the default should be empty, the application would specify Email.Null
as the second argument. By convention all field classes provide a
Null instance initialized with an empty string ("").
The getField method is defined in terms of the Validatable interface
provided by the WAP package. WAP provides an abstract implementation
of Validatable, AbstractField, to server as the abstract superclass
of all application-specific field classes.
Briefly email.ok() reports whether the email address complies with
the syntactic validity requirements of the Email class. If not,
email.getMessage() returns a string that explains the problems in the
user's frame of reference. When the Email field is assigned a value
via its setValue(Object aValue) method, it uses a regular expression
to set the boolean returned by email.ok() as well as the String
returned by getMessage() (too long, too short, not in [EMAIL PROTECTED]
format). A simple user interface for editing an email address with
validation then becomes as simple as this:
ctx.send({{
{{ ctx.emitForm(this) }}
Email Address: {{ email.asField("email", 32) }} {{
fontRed(email.getMessage()) }}
<input type=submit value="OK"> prompt(op, new Validatable[] { email });
<form>
This relies on application-specific methods in the application's Page
class to display the getMessage() text in a red font, and to return a
standard message alongside the submit button ("Click OK to proceed"
vs "Correct the errors marked with red text"). Notice that Strings
and nulls don't appear in the application, and that the traditional
checking for the null values has completely disappeared.
Beans are made of Fields, not Strings
Fields are to beans as atoms are to molecules. But JDBC provides only
get and set methods that encourage applications to declare bean
fields as Strings and other generic Java types like int or long.
WAP's solution is the same as the above. A bean's API to the
application should be defined to accept application-specific field
data types like Email instead of generic data types like String. The
bean's load and save methods simply use AbstractField methods to
convert between the generic data types used by JDBC and the field
data types used by the application.
Strings might have more than one line
Yet Java imposes such limitations what can go in a String that it is
all but impossible to handle multi-line strings such as html text
within Java.
JSP at its best provides one solution to this problem. One write html
text as a a .jsp file and embedded Java code in any of several kinds
of <% Java escape sequences %>. The .jsp files are compiled into Java
servlets at run-time, with the original multi-line strings converted
to plain Java strings.
WAP takes exactly the same approach except that the conversion is
done at compile time. The MLS prepropossor converts long multi-line
strings (html text) into ordinary .java files that Jikes compiles
into .class files. Everything is done at compile time, so MLS
eliminates the space and time overhead of JSP's run-time approach.
Template languages like WebMacro and Velocity take the anti-pattern
one step further. In addition to the inappropriate dynamic couplings
mentioned above, template languages add two more.
Code to template
The java code in an object's controller binds to the presentation
template by naming a template file.
Template to code
Template languages provide varying degrees of support for executable
inclusions, conditionals, loops and so forth. The template language
provides ways for this to access a Hashtable that is built by the
object's controller and through which the template language interacts
with the application.
Caution: Dynamic binding is harmful only when abused. The user
interface designers (e.g. html coders) in your shop might well find
the ability to tweak the template/html files and reload the page to
see them absolutely indispensable, and their html editing tools will
insist on the traditional file-based approach.
Such data-focused biases don't exist in my shop, and I'm so used to
typing make after a change that I do it automatically. Also I don't
find Java class declaration boilerplate objectionable as an html
specialists probably would, certainly not in comparison with the
boilerplate that they tolerate with JSP.
Here is a comparative example. This is the permission-denied page
from the WAP demo.
package edu.virtualschool.page;
import edu.virtualschool.wap.*;
public class RefusePage extends Page
{
public final static RefusePage instance = new RefusePage();
private RefusePage()
{
super( "DemoRefusePage", Role.Everybody, "RefusePage",
"Permission Denied");
}
public final void controller(Ctx ctx) throws Exception
{
ctx.send({{
{{ asTitle(this) }}
Account {{ ctx.getViewer() }} is not authorized to do that.
}});
}
}
Compare that with the permission denied page from a comparable JSP application.
<%@ page
extends="edu.virtualschool.states.Refuse"
language="java"
import="edu.virtualschool.exception.*,edu.virtualschool.beans.*,edu.virtualschool.franchisee.*,java.sql.*,edu.virtualschool.types.*"
info="Access Denied"
session="true"
errorPage="error.jsp"
%>
<jsp:useBean id="thisAccount" scope="session"
class="edu.virtualschool.beans.Account" />
<jsp:include page="bodyOpen.jsp" flush="true" />
<h2>Permission Denied</h2>
Account <%= thisAccount %> is not authorized to do that.
<jsp:include page="bodyClose.jsp" flush="true" />
Configurations are not files
This is probably the most common example of the anti-pattern of all,
and the very worst offenders I know are Apache and Tomcat.
Apache can be excused for its baroque configuration files because its
written in C which many users won't know. But with Tomcat, there is
no excuse for requiring its users to learn the latest new language
fad (XML) when every Tomcat user already has a perfectly serviceable
configuration language that reports errors properly, namely Java.
Tomcat compounds the problem by simply checking the XML configuration
against the DTD for syntactic validity at start up and blithely
assumes semantic validity at run time . If the configuration is
wrong, Tomcat does nothing to diagnose the likely cause and present
it in terms the user can understand. The best you can hope for is a
stack trace from some part of the run time that you know nothing
about and may not even have the source for. The only alternative I
know at that point is to download the source and debug the problem
with the (excellent!) VisualAge debugger. Debugging an application's
source code to fix a configuration mistake is simply unacceptable.
Sigh. There, I feel much better now.
WAP solves this in the obvious manner, by using Java as the
configuration language and eschewing dynamic substitutes such as XML,
properties files, resource files and the like, reserving dynamic
binding for those rare case where only dynamic binding will do. For
example, to configure an application's servlet in WAP, instead of
editing a configuration file, you define an AbstractServlet subclass
like this:
public class PageServlet extends AbstractServlet
{
private final static Category log =
Category.getInstance(PageServlet.class.getName());
public final static String contextName = "demosvlt";
public final static String servletName = "page";
public final static String serverName = "virtualschool.edu";
public PageServlet() { super(); }
public final String getContextName() { return contextName; }
public final String getServerName() { return serverName; }
public final String getServletName() { return servletName; }
public final AbstractPage getDefaultPage() { return HomePage.instance; }
public final AbstractPage getLoginPage() { return LoginPage.instance; }
public final AbstractPage getRefusePage() { return RefusePage.instance; }
public final Connection getConnection() throws PoolFault
{ return PoolBuilder.getConnection(); }
public final void freeConnection(Connection connection) throws PoolFault
{ PoolBuilder.freeConnection(connection); }
}
Dynamic binding is harmful only when used inappropriately
Object-oriented old-timers will be familiar with my early work with
Objective-C, and will probably be surprised at what may seem a
condemnation of dynamic binding from me. After all, Objective-C is a
language that simply adds Smalltalk-style dynamic binding to C.
This misinterprets the point of this rant. This is not a condemnation
of dynamic binding, which can be invaluable when used appropriately,
for problems that can be solved no other way. Rather this is a
condemnation of those who use it inappropriately for problems that
can be solved more straightforwardly, productively and efficiently
with static alternatives such as those that WAP provides.
Summary
WAP provides statically bound alternatives that the careful designer
can use to substitute for the inappropriately dynamically bound
solutions that one finds at every turn within web application
environments such as JSP.
In shops such as mine where the html designers are also programmers,
WAP replaces JSP and/or template languages altogether because it
accomplishes the same thing with considerably less overhead in both
space and time. WAP even improves deployment server security by
eliminating the need to have a SDK installed on the server since WAP
applications only need the JRE.
--
---
For industrial age goods there were checks and credit cards.
For everything else there is mybank.dom at http://virtualschool.edu/mybank
Brad Cox, PhD; [EMAIL PROTECTED] 703 361 4751