When JBoss Seam Fails

seamYesterday I wrote about Ruby on Rails and what happens when programmers make common (typing) mistakes in their programs. It seems to have hit a nerve.

A number of Java and C# programmers sat back and enjoyed the show. "See what happens when you don't have static typing in your language? In your face, smug Ruby on Rails sons of guns!"

Indeed, statically typed language fans often argue that many such common mistakes, such as misspelled variable, method, type and field names are found much faster: at compile time -- hell, with Eclipse almost as you type! And this is definitely true. However, errors are only found within Java code. Modern web applications are built using a number of domain-specific languages that interact with Java in various ways -- JSF, regular expressions, SQL or HQL and various sorts of XML configuration files. Once Java start interacting with other languages, things start going wrong and static typing is no longer helpful.

In this article I demonstrate this problem by showing examples of code built using JBoss Seam, a modern Java framework for developing web applications. Seam is essentially glue to put a number of popular Java frameworks together in a developer-friendly manner. It relies on frameworks and technologies such as Hibernate, JSF and EJBs. Interaction between the languages are very error prone, it turns out. Other Java frameworks, such as the Spring framework have similar issues. People say the Play Framework does a better job. So play framework people, if you're listening (and I know you are), if you're still so confident about yourself after reading this article, let me know, I'll have a good look at Play. But for now, let's focus on JBoss Seam.

Like with Ruby on Rails, I will consider a number of easy to make mistakes, and see how the framework responds to it. These mistakes are exemplary of problems that Java frameworks like Seam have, even though they are based on a statically typed language. They cannot be used as a fair comparison to e.g. Ruby on Rails.

Mistake #1: Use non-existent property of a variable in a JSF page

Welcome #{user.nam}

The user object does not have a nam property, only a name property. As we compile our code, the compiler reports no errors. However, when we deploy and load the page we see:

seam-fail-1

Although the error is clear, it is left for us to figure out where this nam property is actually used. Apparently there is no compile-time checking of JSF pages against the backing Java beans they communicate with.

Mistake #2: Non-existent references to pages

Seam has a pages.xml file to control flow between pages under certain conditions. The page contains references to Java code (in #{...} expressions) and to views (.xhtml pages). However, neither of these are checked at compile time, so if we mistype a page name:

<page view-id="/home.xhtml" action=
"#{identity.isLoggedIn}">
    <navigation>
        <rule if="#{identity.loggedIn}">
            <redirect view-id="/man.xhtml"/>
        </rule>
    </navigation>
</page>

We are get a 404 Not found error.

seam-fail-2

Mistake #3: Faulty validation regular expression

Validation of data model properties based on regular expression can be defined using the @Pattern annotation. But what happens when the programmer puts in a syntactically invalid regular expression?

@Pattern(regex="^[\\w*$", message="not a valid username")
public String getUsername() {
   return username;
}

There is a '[' too many there. When the code is compiled, no error is found. However, when it is deployed to the application server, an exception with an enormous stack trace appears:

seam-fail-3

It's clear there is a regex mistake here. Somewhere. But where exactly is very hard to pinpoint drowning in the trace.

Mistake #4: Making a mistake in a HQL query

The Java Hibernate framework uses its own variant of SQL, that is slightly higher-level than SQL. Queries are represented as strings, and only checked at runtime.

List existing = em.createQuery("select u.username from User like u where u.username=#{user.username}")
     .getResultList();

There is a mistake here because there should not have been a "like" there in that query. When the action is invoked, an exception is thrown with an even longer stack trace than before:

seam-fail-4

A similar exception occurs 7 times in the stack trace, only on a few location there is actually a reference to the location in the code where the problem occurred:

at org.jboss.seam.example.booking.RegisterAction.register(RegisterAction.java:40)

Bad.

Conclusion

Compared to Ruby on Rails, the ability to type check Java code is a nice feature and lets you find a certain kind of mistake very quickly -- in an IDE such as Eclipse even as you type. This support rapidly degrades when moving outside the statically typed world of Java. As soon as the programmer has to jump to using strings, as is the case when using e.g. regular expression, SQL and HQL, the safe, checked world of Java comes to an end.

The only kind of checks that are performed in languages like Java are type checks. This also means that the only mistakes are found that are related to types and can be expressed in types. This gets you only up to a certain point in finding programming errors. In order to use regular expressions in Java, programmers have to encode them in strings; SQL queries are encoded in strings -- compilers do not check strings. Strings are parsed and interpreted only at runtime, meaning problems only appear at runtime typically buried in an enormous stack trace. And that's a big shame.

Next part in this series: When Scala DSLs Fail.