Tuesday, May 18, 2010

Conversation scope: CDI (JSR-299) vs Seam 2

CDI (JSR-299) is the new Java standard for contextual type-safe dependency injection and as such it is an integral part of Java EE 6 platform. Still, it will also work in any other environment, be it a servlet container or simple Java SE. It was insipered by Seam 2 (and what is therein known as 'bijection') and Guice.

The Naming Game


Initially, CDI was known as WebBeans but the name had been changed before the spec was finalized. The reference implementation is called Weld but was also at some point called WebBeans. Yet again, that name is now obsolete as should not be used. Thus, WebBeans now refers to... well... nothing, so you're free to forget all about it. You will still see the old name popping up around the Web occasionally but it should fade into oblivion soon enough. More info on this story can be found at the very beginning of the reference documentation.

Conversations


The existance of a built-in conversation scope in CDI will immediately lure most of the Seam 2 developers into a wrong assumption of familiarity. Wrong because although they serve the same purpose conversations in CDI and Seam 2 do not always behave identically.
While all Seam's conversations, both transient and long-running, invariantly survive redirects (unless specifically instructed not to through endBeforeRedirect=true), conversations in CDI will survive only if they are promoted to long-running (through conversation.begin() ) during the original request. A transient conversation will be destroyed at the end of the original request and a new one will be spawned for each redirect. In other words, without a long-running conversation, conversation scoped components in Seam 2 will behave (by default) as if they were in what you might know as flash scope from other frameworks and languages (this scope will also exist in Seam 3 with the related annotation @FlashScoped). Under the same conditions, @ConversationScoped beans in CDI will behave as if they were @RequestScoped. In case a long-running conversation is present, the behavior will be the same in both Seam 2 and CDI.

Reference documentation for some reason never states this explicitly so it can be very misleading for Seam 2 developers. Luckily though, there's a table on the official site mapping Seam 2 scopes to their CDI equivalents where possible and gives brief description of the differences.

Example


To further clarify this (probably needlessly), let's illustrate with a simple example implemented both in Seam 2 and CDI.
In order to keep everything at a bare minimum, we'll just create a single component (or bean if you prefer), named MessageBean, and a single Facelets view that accepts the new input and attempts to display the previous one after a redirect.
Note: Import and package statements are omitted from the following code listings.

Seam 2 version

MessageBean class:

@Name("msg")
@Scope(ScopeType.CONVERSATION)
public class MessageBean implements Serializable {

private String message;

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String redir() {
return "redir";
}
}

CDI version


You'll notice a striking resemblance to the Seam 2 version.

MesageBean class:

@Named("msg")
@ConversationScoped
public class MessageBean implements Serializable {

// @Inject Conversation conversation;
private String message;

public String getMessage() {
return message;
}

public void setMessage(String message) {
this.message = message;
}

public String redir() {
//if (conversation.isTransient())
// conversation.begin();
return "redir";
}
}

Facelets view (the same in both cases):

Note: Again, extra headers are omitted.

input.xhtml:

<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:ui="http://java.sun.com/jsf/facelets"
xmlns:h="http://java.sun.com/jsf/html"
xmlns:f="http://java.sun.com/jsf/core">
<div>
Previous input: #{msg.message}
</div>

<h:form>
<h:inputText id="messageText" value="#{msg.message}" required="true"/>
<h:commandButton id="redirectButton" value="Redirect" action="#{msg.redir}"/>
</h:form>
</html>
Configuration (the same in both cases):

faces-config.xml:

<navigation-rule>
<navigation-case>
<from-outcome>redir</from-outcome>
<to-view-id>/input.xhtml</to-view-id>
<redirect />
</navigation-case>
</navigation-rule>

If you were to execute both these examples, you'd soon realize the difference between the frameworks. While Seam 2 version keeps your input across the redirect and displays it on the next rendering, even without promoting the conversation, CDI will destroy the context on redirect leaving you with no message to display. Note that if the commented lines in the CDI version of MessageBean were to be uncommented, thus promoting the conversation to a long-running one, the message would survive, together with the whole conversation context (which would afterwards be reused in the next cycle in this case). Normally, you'd also want a way to end the conversation at some point through conversation.end() instead of waiting for the timeout (which is ultimately decided by the server).

Download


Below you can find both implementations as war files ready for deployment. The examples are meant to be deployed to JBoss AS so some tweaks (like adding Weld or any other CDI implementation jar to the classpath) might be needed in order to get it running on Tomcat or other servers/containers.

conversationCDI.war (tested on JBoss AS 6)
conversationsSeam.war (tested on JBoss AS 5, does not work on JBoss AS 6 for some reason)

References


Weld main site
Seam 2 tutorial

Sunday, March 14, 2010

Global transactions in Seam

Transactions grew to be a necessity in most today's applications, and managing them grew to be a demanding task. If the application is complex enough it can evolve into a nightmare in its own right. Thus, offloading this task to a framework whenever possible is a very desirable approach.
Being a powerful feature-packed framework that it is, Seam offers exceptional support for transactional behavior that in many instances surpasses all others. One feature not seen in other frameworks are global transactions.

The confusion


This topic is regarded as somewhat arcane, despite being essentially simple. The reason for this is twofold. Firstly, the documentation regarding global transactions is relatively poor. Secondly, and more importantly, the exiting documentation does not call this feature global transactions, and this is the term used most widely over the internet (it was originally coined by Dan Allen is his book Seam in Action, often regarded as the "default" book for Seam).

Clearing it up


In essence, global transactions refer to the two or three transactions that wrap each request in Seam. The first one wraps JSF phases beginning with Restore View (for JTA transactions) or Invoke Application (for resource-local transactions), and ending just before Render Response. The second wraps all page actions (if they are used), and the third wraps the Render Response phase. During this final transaction, context flushing is disabled, effectively rendering the whole transaction read-only.
In this fashion, Seam accomplishes a couple of things:
  • an error in page preparation or rendering will never role back a successfully executed business logic,

  • view rendering is executed atomically, which not only avoids opening and closing a transaction on every lazy initialization, but also guarantees isolation and guards against accidental changes of the state during this phase,

  • page actions are executed atomically (with benefits similar to those mentioned above sans disabled context flushing).

This way, Seam effectively does away with a good portion of the most common problems of transaction management.
To effectively use global transactions, one should keep in mind that Seam turns them on by default. To turn them off, you'll need to disable Seam's transaction management all together. This is done by adding
<core:init transaction-management-enabled="false"/>

to the component descriptor file.

Notes


It should also be noted that, despite what can be read in some sources, @Transactional annotation is not unusable in conjunction with global transactions. The point here is that since there is an overarching transaction, no new transactions will be spawned to encompass annotated business methods, effectively ignoring @Transactional. Of course, an overarching transaction will only be present during a JSF postback. On non-JSF calls, @Transactional will still do it's job normally. This misunderstanding was caused by a somewhat ambiguous statement in Seam in Action.
To recap, global translations is just another name for (as Dan Allen would put it) "automatic transaction wrapped around the request". A useful feature which one should always keep in mind when developing applications in Seam.