Most web frameworks are broken
We’ve all been there: fired up the next set of changes in a webapp and navigated through a complex flow, entering fake data for the n-th time only to find a massive exception on the page we’ve been expectantly working on, due to a simple typo:
No such property: ${person.chidl}
Apart from the enormous frustration, the problem with this approach is that it is very fragile. If you fix the one error, then go through the entire process again there are no guarantees you won’t end up with another further down the page:
No such property: ${person.siblign}
This happens all the time, and most programmers seem to accept it as an awful fact of life with web frameworks today. You might say, that’s OK–I’ll trade this any day for the madness that is JSP and scriptlets. Except, there’s a whole new madness seething underneath.
Broken encapsulation
Consider this expression:
Hello, ${person.firstName} ${person.lastName}.
And now consider the class behind it:
public abstract class Person {
public abstract String getName();
}
What’s going on?! The property should be ${person.name} yet it works with these two non-existent properties. What happened was: someone inadvertently exposed properties of a subtype (first + last names) that were never meant to be known. Tightly coupling presentation code to the internals of your app.
This is allowed by most web frameworks because they use a dynamically typed expression language that invokes property getters via reflection. This problem also gets worse.
Expression depression
Imagine how horrible this could get. Some libraries allow method calls and computations:
Total is ${cost + tax.computeGst(cost, 10)}
Does method computeGst() exist? Does it take two integers? Does it return the same type as cost (double), so they can be multiplied? <Insert_web_framework_here> does not care. As far as it’s concerned, you can follow the Rube Goldberg and find out when things blow up.
Weak typing
Can it possibly get worse? Indeed. Let’s dissect the GST example further:
Total is ${cost + tax.computeGst(cost, 10)}
If cost is a String that holds “24.50″, and the computeGst() method returned a double, it is very possible that your web framework will still compute the result! This is because many of them perform implicit type conversion:
"3" + 2 => 5
If you are not a Visual Basic programmer, this should make you shudder.
Warp-widgets
Mike Brock (from MVEL) has been helping me add static typing to warp-widgets. The latest build (on my laptop) now statically type checks every expression in the template and reports errors in a manner very similar to javac:
....warp.widgets.TemplateCompileException: Could not compile template for..
1) unknown or unresolvable property: chidl
21: <ul>
22: <li>${person.chidl}</li>
^
Note that every expression on a page is statically type-checked and verified even if errors are found earlier. Thus eliminating all four of the problems I described earlier.
A big shout out to Mike for his responsiveness in adding obscure features that I request.
Another shout out to “Crazy” Bob Lee for pushing me to do this in the first place.
Filed under: 1 | 6 Comments
Tags: guice, java, type system, type theory, warp-widgets
“If cost is a String that holds “24.50″, and the computeGst() method returned a double, it is very possible that your web framework will still compute the result! This is because many of them perform implicit type conversion:”
I’d like to note, that even in weakly-typed mode, MVEL does not suffer from this problem unlike OGNL. MVEL *never* invokes a target method for type conversion purposes. OGNL 2.7 does though.
I can’t agree more, this is the typical problem of “executable views” or “pull views” (the view pulls data), in the old JSP approach Java code was hard coded and the compiler was very useful to detect coding bugs. Now most of the templating engines interpret the view (the view logic uses interpreted languages) and there is no way to detect errors in a compiler phase.
The alternative is using non-executable views or web frameworks with “push views” (the application pushes any data to the view). Frameworks with “push views” reduce the view logic in the view, in some way the view rendering is “driven” by Java code, one immediate benefit is a cleaner view/view logic separation.
Terence Parr talks about this:
http://www.cs.usfca.edu/~parrt/papers/mvc.templates.pdf
There are no very much examples of web frameworks with push views:
- StringTemplate: a proof of concept of Terence Parr ideas.
In ItsNat the view logic is pure Java using W3C DOM Java APIs and the templates is absolute pure HTML (or SVG).
http://www.stringtemplate.org
- GWT : this is an extreme example because there is no template, view is built programmatically.
http://code.google.com/webtoolkit/
– Wicket: the template is almost pure HTML, most of the logic is defined in Java
http://wicket.apache.org
- ItsNat: my preferred, because I’m the author
htt://www.itsnat.org
Absolutely agree! That is why web frameworks like Wicket exist. But everyone likes standard, so give them JSF. In fact, the whole Java community seem to like hardcoded string so much that they have lost they OOP nature.
I think this article highlights an idea I have been pursuing. The idea of separating programming languages and their associated contexts into two categories, implicit and explicit. I realized when I started on the idea the point I was really trying to get across was that I want to see things made more explicit in that even though I like terse languages (easy on the hands and wrists), I want tooling that in effect makes the language/platform explicit. You want tooling either in the compiler or IDE to be able to at least warn you of potential trouble as much as possible. This to me is the great divide between using so called dynamic versus statically typed languages at the moment. I know dynamic advocates will argue that you need a stronger set of unit tests, but really do you want to have to depend on people writing a bunch of tests when you could have a compiler or IDE tool give them the feedback they need automatically? My ADD afflicted mind and I prefer that the computer do as many automatic checks as possible, since it is likely I will forget, or be to lazy, to do them.
Mr Spiewak had a great blog entry on this:
http://www.codecommit.com/blog/ruby/pipe-dream-static-analysis-for-ruby
Incidentally, I think Daniel inadvertently makes the best synopsis of the argument between using dynamic and statically typed languages in that blog entry that I have ever read.
Well, part of the problem is an IDE that is not up to the task.
For example both IntelliJ IDEA and Red Hat Developer Studio statically check EL expressions.
I agree though that views and backing beans are too tightly coupled in principal.
+1 for Wicket.
Although you still have to get your binding names correct, the framework fails hard and fast if they are mismatched (you can’t bind to a backing object that doesn’t exist). Secondly, there’s an eclipse plugin that will check all your bindings during development anyway. Great!