tomcat-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Brad Cox <b...@virtualschool.edu>
Subject Misusing dynamic binding when static binding would do.
Date Thu, 31 May 2001 12:46:32 GMT
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 user@host.dom 
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; bcox@virtualschool.edu 703 361 4751


Mime
View raw message