[image]
the kdonald blog
thoughts on java development posted by keith, the guy with three first names and a dog named Macy.
Macy Grayspots the Whippet

Spring Hidden Gem: Support for Setting Enum Values
Sunday March 05, 2006

A neat "hidden gem" of Spring's container is the ability, from a XML-based bean definition, to automatically populate a bean property of type enum with an enum value.

I've seen folks using custom approaches to try and do this--from using a FieldRetrievingFactoryBean to a PropertyEditor to perform manual string conversion. Both are quite verbose. There is a much simpler way that works for both JDK 5 enums as well as classic type-safe enums implemented on JDK 1.4 or <.

Consider this JDK 5 enum:

package javax.persistence;
        
public enum PersistenceContextType {
    TRANSACTION,
    EXTENDED
}

Now consider a setter of type PersistenceContextType:

package example;

public class Client {
    public void setPersistenceContextType(PersistenceContextType type) { 
        this.persistenceContextType = type;
    }
}

... and the corresponding bean definition:

<bean class="example.Client">
    <property name="persistenceContextType" value="TRANSACTION"/>
</bean>

This works for classic type-safe enums implemented on JDK 1.4 or < as well -- Spring will automatically attempt to match the string property value to a constant on the enum class.

This feature is available on Spring 1.2.2 or >. These days you should really treat any use of FieldRetrievingFactoryBean or MethodInvokingFactoryBean with suspicion--chances are there is a simpler way.

( Mar 05 2006, 02:28:41 PM EST ) Permalink Comments [3] 20060305

The Essence of Spring
Tuesday January 31, 2006

There was a comment made today on TSS in regards to the ongoing "I don't get Spring" discussion, initiated by Bob Lee. The comment hit the nail on the head for me, communicating quite concisely the essence of what Spring has always meant to me and the primary reason for which I got involved with the project.

Here's the original comment, quoted in-line:

"The value Spring provides developers of applications is that the sum is far greater than the value of the individual parts.

Are there better IoC containers? Perhaps, there are certainly lighter weight IoC containers.

Are there better AOP implementations? There are certainly more complete ones and as acknowledgment of this fact AspectJ is integrated.

Are there better frameworks for ORM? Spring, for the most part, simply integrates several ORM frameworks providing a consistent interface where possible.

Are there better frameworks for MVC? There will always be a 'better' one launched next week. Apart from SpringMVC, they integrate several others giving developers a choice of what to use.

Are there better transaction managers? Spring gives developers a consistent interface across several implementations.

The real value of Spring is that they have not tried to specialize in any one area. It glues together a variety of frameworks in order to provide consistent interfaces for them and make application developers more productive.

Sure, an application developer could choose their personal favorites in each area and glue them together by hand. But that is not what they are paid to do. Every piece of code not providing business value is just overhead.

The Spring folks have taken on the unglamorous work of integrating disparate frameworks into a whole that is much greater than the sum of its parts. It is great to see them getting credit. They have made the lives of many application developers less tedious and allowed them to provide more business value for their customers."

( Jan 31 2006, 05:42:06 PM EST ) Permalink Comments [2] 20060131

Spring Web Flow: State Exception Handling
Thursday October 27, 2005

One of the most important features coming in Spring Web Flow (SWF) 1.0 is the new state exception handling abstraction. This abstraction serves one purpose: to allow you to program what the flow should do when something goes wrong.

Prior to 1.0 RC1 (due out November 7th), previous SWF releases simply did not address this problem at all. Instead, they relied on the calling front controller (like Struts or Spring MVC) to care for exceptions generated by executing flows. For example, with Spring MVC, this meant an Exception thrown in an executing flow state would trickle up the stack and be caught by a HandlerExceptionResolver, which would then select an error view to display. That behavior was "good enough" for PR5, but not for 1.0. A Flow is a very powerful controller, and absolutely must be able to handle exceptions natively for us to consider the system feature complete.

So with that said, we are very happy with the new exception handling system, and this blog is here to tell you about it, as well as get some feedback on the approach. Here's the scoop:

Any Flow definition may now be configured with an ordered set of one or more StateExceptionHandler objects.

Here's the handler interface:

/**
 * A strategy for handling an exception that occurs in a state of an executing flow definition.
 *
 * @author Keith Donald
 * @author Erwin Vervaet
 */
public interface StateExceptionHandler {

        /**
         * Can this handler handle the given state exception?  Implementations 
         * typically traverse the exception's "root cause" graph to match on
         * nested exceptions.
         * @param e the exception that occured
         * @return true if yes, false if no
         */
        public boolean handles(StateException e);

        /**
         * Handle this exception in the state context of the current request.
         * Optionally select an error view that should be displayed.
         * @param e the exception that occured
         * @param context the current state context
         * @return the selected error view that should be displayed (may be null if
         * the handler chooses not to select a view)
         */
        public ViewDescriptor handle(StateException e, StateContext context);
}    

Once your exception handlers are configured, when a StateException is thrown within a State at runtime (for example, as the result of an ActionState calling an Action that calls one of your POJO methods that throws a checked business exception), that exception is passed to the errant Flow for handling. The Flow then iterates over its handler set until it finds a suitable handler. The matched handler may then execute arbitrary behaivior, perhaps even transitioning the flow to a new State (to send an email and then display an error page, for example). All contextual information about how the exception occured and what state it occured in is preserved to facilitate rich exception handling behavior.

Of course, an interface doesn't do you much good without a supporting implementation. So a StateMapperExceptionHandler is shipped out-of-the-box that given a class of exception that may occur will transition the flow to a specific state. This implementation is now fully supported by the XmlFlowBuilder for convenient use when defining flow definitions in XML.

Here is an example of this from the sellItem sample application:

sellItem-flow.xml


<webflow id="sellItem" start-state="enterPriceAndItemCount">
    ...
    <end-state id="error" view="error"/>

    <-- on "requestNotInTransaction" (preventing duplicate submit), transition to "error" -->     
    <exception-handler class="org.springframework.webflow.RequestNotInTransactionException" state="error"/>
    ...
</webflow>

You may configure any number of exception handlers, mapping any exception type to any state. You may also specify your own custom exception-handler "bean" as follows:

<webflow>
    ...
    <exception-handler bean="myCustomHandler"/>

    <bean id="myCustomHandler" class="example.MyCustomHandler";>
        <property name="businessService" ref="myBusinessService"/>
    </bean>
    ...
</webflow>

Notice the dependency injection, allowing handlers to delegate to middle-tier services for exception processing.

This capability is available now in CVS awaiting release with 1.0 RC1 on 07.11.2005. Give it an early try now and let us know what you think. The sellItem sample that illustrates this is deployable in like three steps (even from CVS Head!) (and also shows many other goodies too, like the ability to change a flow definition at runtime from jConsole).

To 1.0!

( Oct 27 2005, 03:26:15 AM EDT ) Permalink Comments [7] 20051027

Be a TCFTC block for Halloween this year
Thursday October 20, 2005

A very strange, very scary news story made TSS today. On the surface it looked completely innocent, though admittedly out of place: the topic was a support question about how to properly handle transaction rollbacks for pete's sake! (On a tech news portal?? Talk about a great strategy to get your question answered! Why haven't I thought of that? :-))

Anyway, beneath this seemingly innocent question lurked a creature so foul, so sinister ... you can't quite look directly at it without turning green, or to stone, or maybe to Ruby. What was it? Some data access code from one of Sun's J2EE tutorials no less:

-- WARNING: THE FOLLOWING MAY CAUSE DEMENTIA, INSANITY, RASH, ARM WAVING, AND GENERAL OTHER BAD THINGS -- (hide your kids!)

import java.sql.*;
     
public class TransactionPairs {

public static void main(String args[]) {

String url = "jdbc:mySubprotocol:myDataSource";
Connection con = null;
Statement stmt;
PreparedStatement updateSales;
PreparedStatement updateTotal;
String updateString = "update COFFEES " +
"set SALES = ? where COF_NAME like ?";

String updateStatement = "update COFFEES " +
"set TOTAL = TOTAL + ? where COF_NAME like ?";
String query = "select COF_NAME, SALES, TOTAL from COFFEES";

try {
Class.forName("myDriver.ClassName");

} catch(java.lang.ClassNotFoundException e) {
System.err.print("ClassNotFoundException: ");
System.err.println(e.getMessage());
}

try {

con = DriverManager.getConnection(url,
"myLogin", "myPassword");

updateSales = con.prepareStatement(updateString);
updateTotal = con.prepareStatement(updateStatement);
int [] salesForWeek = {175, 150, 60, 155, 90};
String [] coffees = {"Colombian", "French_Roast",
"Espresso", "Colombian_Decaf",
"French_Roast_Decaf"};
int len = coffees.length;
con.setAutoCommit(false);
for (int i = 0; i < len; i++) {
updateSales.setInt(1, salesForWeek[i]);
updateSales.setString(2, coffees[i]);
updateSales.executeUpdate();

updateTotal.setInt(1, salesForWeek[i]);
updateTotal.setString(2, coffees[i]);
updateTotal.executeUpdate();
con.commit();
}

con.setAutoCommit(true);

updateSales.close();
updateTotal.close();

stmt = con.createStatement();
ResultSet rs = stmt.executeQuery(query);

while (rs.next()) {
String c = rs.getString("COF_NAME");
int s = rs.getInt("SALES");
int t = rs.getInt("TOTAL");
System.out.println(c + " " + s + " " + t);
}

stmt.close();
con.close();

} catch(SQLException ex) {
System.err.println("SQLException: " + ex.getMessage());
if (con != null) {
try {
System.err.print("Transaction is being ");
System.err.println("rolled back");
con.rollback();
} catch(SQLException excep) {
System.err.print("SQLException: ");
System.err.println(excep.getMessage());
}
}
}
}
}

Yikes! I puked in my trash can when I saw this. Besides the lack of formatting, once you add in the TCFTC blocks (the FTC is actually lacking here!), the manual (and improper) connection/statement/transaction management, the non-existant exception handling, the coupling of hard coded configuration with use/intent, the general verbosity... dang man you've got one scary monster! Spread this kind of stuff across your company and you have literally got your own fright factory, a Sloss Furnance no less (for any of yall from Birmingham, Alabama).

So after I pulled my head out of the trash can, I could not help but turn martyr and go back to my maintenance roots (ah yes, those were the days, cleaning up nasty c/c++ code day-in, day out). To give you an idea of how better we can do, here is the functional equivalent of the nightmare above done right, leveraging Spring's Transaction management and JDBC abstraction libraries:

@Transactional
public void doUnitOfWork() {
    String sql = "update COFFEES set SALES = ?, TOTAL = TOTAL + ? where COF_NAME like ?";
    BatchSqlUpdate updater = new BatchSqlUpdate(getDataSource(), sql);
    int [] salesForWeek = { 175, 150, 60, 155, 90 };
    String [] coffees = { "Colombian", "French_Roast", "Espresso", "Colombian_Decaf", "French_Roast_Decaf"};
    for (int i = 0; i < coffess.length; i++) {
        updater.update(new Object[] { salesForWeek[i], salesForWeek[i], coffees[i] });
    }
    updater.flush();
    
    getJdbcTemplate().query("select COF_NAME, SALES, TOTAL from COFFEES", new RowCallbackHandler() {
        public void processRow(ResultSet rs) {
            String c = rs.getString("COF_NAME");
            int s = rs.getInt("SALES");
            int t = rs.getInt("TOTAL");
            System.out.println(c + " " + s + " " + t);
        }
    });
}

Woo! Much better. Intent is clear, configuration is externalized, proper DataSource connection management is handled automatically (no more resource leaks!), database-portable SQL exception translation and root cause analysis is automatic as well, transaction management is clean, with fully @declarative transaction demarcation ... (no more "oops I forgot to rollback on THAT exception!"). I went ahead and added some batching in here for good measure, what the hell.

Seriously, anyone using the raw JDBC API directly these days should seriously consider why they are doing so, Sun tutorial writers included. Use a JDBC abstraction framework instead. Anyone looking for a good halloween idea for next week, consider going as one of the TCFTC blocks in the goulish code snippet far above... I'm not sure what that costume would look like, but if you pull it off you're guaranteed to scare the shit out of everyone you come in contact with.

( Oct 20 2005, 01:38:32 AM EDT ) Permalink Comments [6] 20051020

Hot Reloadable Web Flows
Monday October 10, 2005

Work towards Spring Web Flow 1.0 RC1 continues to roll like the Alabama Crimson Tide. This entry is about a significant 1.0 feature that delivers on development agility: hot reloadable web flows.

Since a Flow is a first-class object (effectively an mini application module), you can interact with it, message it, store it, observe it, intercept it, etc. This is very much a contrast to traditional controller frameworks where there is simply no first-class concept of a "Flow"; rather, there are only a bunch of independent actions (or pages) whose executions are driven by the browser.

This has a number of positive impacts. Consider the impact on testing. Since a Flow is a Object with behaivior, you can naturally create an out-of-container Flow execution test that catches any runtime logic errors in the flow, and allows you to exercise all paths through the flow. The TDD cycle we advocate goes something like this:

You code your flow, run the test, see it fail, revise, run the test, see it fail again, revise, run the test, see the green bar. Only with the green bar, do you then deploy the flow for execution in container.


Since a Flow represents a self-contained module that drives the execution of a use case from the web-tier down, you'll find you'll end up testing 90% or > of all use case logic fully within JUnit--in a matter of seconds. The only remaining bit left to test is presentation logic (JSPs, etc).

Extending beyond out-of-container testing, there are all kinds of other things you can do. You can store snapshots of a FlowExecution to support restoration at a later point in time, to restart long-lived flows. Now, in the upcoming 1.0 RC1, you may refresh modified Flow definitions at runtime, without an application redeploy. Specifically, a Flow definition registry may now be exposed for management over the JMX protocol, and any Flow managed in that registry may be refreshed via a standard JMX client, such as JDK 1.5's jConsole.

This is a good win as you may now deploy green bar updates to a Flow definition without having to restart the container. Simply fire up jConsole and tell that Flow definition to refresh() (support for polling for changes in Flow definition resources may be added in the future).

As always, a feature is no use without a proper example to illustrate it. The SellItem sample in CVS, already JMX-enabled for collecting and reporting global FlowExecution statistics, now demonstrates this feature. Enabling a JMX-managed Flow Registry is as simple as this:

    <bean name="spring-webflow:name=flowRegistry" class="org.springframework.webflow.config.XmlFlowRegistry">
        <property name="definitionLocations" value="classpath:org/springframework/webflow/samples/sellitem/sellItem-flow.xml"/>
    </bean>

    <bean id="mbeanExporter" class="org.springframework.jmx.export.MBeanExporter">
        <property name="autodetect" value="true"/>
    </bean>

When the container starts up, the mbeanExporter will autodetect that the XmlFlowRegistry class is a MBean (since it implements FlowRegistryMBean). When that happens, the exporter will register the XmlFlowRegistry instance in the MBeanServer for the application--automatically. The ObjectName assigned will be the value of the name attribute ("spring-webflow:name=flowRegistry").

From there, just fire up jConsole and point it at the JVM PID (be sure to first make sure the JVM is enabled for management by passing in the -Dcom.sun.management.jmxremote VM argument) and you can now refresh Flow definitions on the fly.

( Oct 10 2005, 11:08:28 PM EDT ) Permalink Comments [3] 20051010

Keri can code code, too
Saturday October 08, 2005

My wife Keri loves puzzle games--tetris, scrabble, crosswords, text twist--any game where you have to figure something out, she's on it. She has a degree in CS, and is employed as a UI specialist, so she can code, too. And she's a heck of a lot better at math than me (I actually have more of a business and art background than a engineering one, and I'd say I think of software more as art than hard science...)

That being said, when I told her about Spring Web Flow's newest sample application -- called "number guess" -- it peaked her interest. The sample is really simple: you guess a number, and you get a answer back whether your guess was "too high", "too low", or "correct." The system remembers what your last guess was, how long you've been playing, and how many guesses you've entered so far. The game ends when you guess the correct number. Very simple, indeed.

The reason we put this in is it illustrates a major feature of webflow: that is, the ability to collect and remember history over the life of a flow, where the system manages all the state for us in a manner that is completely pluggable. It also serves to compare our framework vis-a-vis other solutions in this space, like RIFE, Cocoon, and Struts Flow, as they all have the same sample app.

Well, Keri played numberguess and frankly found it boring as hell. She said we needed to create a better game that actually required some skill and thought. So she proposed a variant on the game called Mastermind. In Mastermind, you guess a four digit number, where each of the four digits should be unique, and you get a hint back telling you how close your guess is to the answer. The hint will tell you how many of the digits in your guess are present in the answer and in the right position, and also how many are present but are in the wrong position. Given this information, you try again and see how many tries it takes to you to figure out what the answer is; meaning, your guess has all 4 matching digits all in the right position.

Besides being more fun, this game is actually better for illustrating the point of webflow, too, as it requires you to build a history table and present that history to the user as its collected. In MasterMind, seeing the history of your guesses, and how many digits they matched is essential to figuring out the answer.

So Keri coded it up, and I showed her how to hook in with the web flow system. She was done in one hour, with unit test cases and everything. Not too bad, given all she knew about Spring Web Flow before this weekend was that it took up too much of my time! :-)

It's in CVS now, part of the 'numberguess' sample, where the "lower/higher" and "mastermind" game variants are two different flows launchable from the app welcome page. Mastermind will become part of PR3, and my wife Keri is now credited as an @author on some Spring source. :-) Check it out, it's actually kind of addicting.

Here's a screenshot:

mastermind

( Oct 08 2005, 11:16:12 AM EDT ) Permalink Comments [33] 20051008

Transactional Web Flows
Friday October 07, 2005

A typical problem in a web application is how to prevent duplicate submits of a logical transaction properly. Some frameworks rely on a token-based strategy to solve this problem, others rely on the redirect after post "pattern" (or a combination of the two). Each of these traditional approaches has its limitations. Managing a synchronized token is often somewhat a manual process, relying on custom code scattered accross your controllers. Redirect after post works ok, but somewhat limits you in terms of the data you can send via the redirect, and by itself still relies on managing a token to handle the case where another browser window is opened in the middle of the transaction and you need to make sure only one window "wins".

In Spring Web Flow, if you are using a single session-scoped FlowExecution you get duplicate submission prevention for free as the conversation itself represents a single application transaction, and when the conversation ends it's gone and you can't "go back": you must start a new conversation.

However, if you're working with flow conversation snapshots (continuations) to support proper browser button user (back, forward, new window), things get a little more interesting. Instead of having one copy of a FlowExecution (the state machine representing a user conversation), you have multiple copies (snapshots) at each ViewState, to support restoration at any point of previous user interaction if the user chooses to go "back" to a certain page and continue from there. As any copy could "win" as the one that completes the logical transaction, you still need make sure it is still only possible to win once.

So to support this, we've added a transaction synchronization facility to SWF, one that is implemented in an aspect like manner. By marking a flow "transactional" like this:

   <webflow id="sellItem" start-state="enterPriceAndItemCount">
       <property name="transactional" value="true" type="boolean"/>
       ...
   </webflow>

... a logical 'transaction' will be created when a flow execution is created, and the id of that transaction will be exposed to each view that participates in the flow. Views can then submit the transactionId back to the server, which verifies the transaction is active and throws an exception if it is not. The verification process is pluggable, but the default strategy compares the client-submitted transactionId with a session-scoped transactionId. When a transactional FlowExecution snapshot "wins" by reaching an end-state, the transaction is ended and it becomes impossible for any other copy (open in other windows or via 'back button' access, for example) to continue -- exactly the functionality you want. This is all transparent, with no custom coding other than the declarative "transactional" annotation above.

Such transactional web flows are significant feature that go well beyond what most frameworks can do in this area, particularly when combined with the rest of what SWF can do. Expect to see it fully documented for use in the next release of SWF, 1.0 RC1. If you'd like to use it now, feel free to use a snapshot from CVS. Checkout the "sellItem" sample which demonstrates this feature in action (the project is directly importable into Eclipse, like all spring projects).

( Oct 07 2005, 10:48:42 AM EDT ) Permalink Comments [6] 20051007
Another Spring Web Flow Success
Monday October 03, 2005

Erwin and I received another positive e-mail from a Spring Web Flow user today.

----- Original Message ----- 
From: Manu
...

Hello there,
 
This is to inform you that we implemented a project "successfully" in Spring WebFlow.
The project uses Spring WebFlow, Spring & Hibernate frameworks.

All the effort started after finding the article at 
http://opensource2.atlassian.com/confluence/spring/display/WEBFLOW/Home

Thanks for this wonderful framework.

In the land of hype that is often today's web framework space, nothing demonstrates quality more than project successes from end users.

( Oct 03 2005, 09:07:45 AM EDT ) Permalink Comments [5] 20051003
Good read from a user of Spring Web Flow
Sunday October 02, 2005

Colin Yates, one of Spring's most active forum users, writes about his experiences using Spring Web Flow on his current project in this article. Besides a warm and fuzzy "SWF is really nice", Colin provides some valuable insight into how the product has helped him with his application.

It is great to read that Colin is benefiting from the strength and pluggability of the architecture. SWF really sets itself a part there: in SWF simple things are simple, complex things are possible, and consistency reigns.

The upcoming SWF 1.0 release will stabilize the public API, and like all software products managed by Spring, will ship proper reference documentation (which will also describe the long awaited JSF integration).

There will be only minor API changes between the current PR5 release and 1.0 final. To follow the ongoing 1.0 development and review those changes, you can monitor the changelog in CVS.

( Oct 02 2005, 12:08:18 AM EDT ) Permalink Comments [38] 20051002
Back from Europe, JavaZone
Saturday October 01, 2005

31 days, 10 countries, 8 languages, 31 trains, and at least several pounds of chocolate later, my vacation across Europe has officially ended. I'm back in the office.

Keri kept a diary of our travels from country to country here. Our pictures are on-line here!

JavaZone in Oslo, Norway was a very impressive experience. 1000 Java developers turned out. The quality of the sessions I attended was down-right excellent.

Presentation slides for my session on Managing Conversations with Spring Web Flow are here. In my session I addressed Spring's positioning in the web framework market and the architecture of Spring Web Flow, a breakthrough technology for the definition and execution of web conversations. My audience was particularly sharp, directly contributing to an important new feature which I will detail in a forthcoming entry.

I am really looking forward to two upcoming events this week: I will be speaking at the Orlando and Gainsville JUGs, both on developing web applications with Spring (hands on!). Next week I'm off to San Jose, California for a week Core Spring Training and BEA World.

It's good to be back!

( Oct 01 2005, 12:38:34 AM EDT ) Permalink Comments [1] 20051001
Spring Web Flows as Application Modules
Monday September 19, 2005

In Spring Web Flow (SWF), one good way to think of a Web Flow is as a mini application module that implements and encapsulates a potentially long-running business process requiring user input at various points within its execution.

In SWF, to program such a process, you define a Flow Definition. You can do this declaratively in XML, Java, or using a custom FlowBuilder. Essentially, a Flow Definition is like a process blueprint, and in SWF this blueprint serves as instructions to the Web Flow "virtual machine", which is based on finite state machine.

At application "use time" when a user wishes to execute a new instance of your process, a new Flow Execution is launched. Once active, the execution carries out process instructions until user input is needed. When user input is needed, the execution pauses and waits until the user provides it. Once input is provided, the process resumes and continues on until additional input is needed or the entire process ends.

This behaivior is illustrated visually on my Managing Web Conversations with Spring Web Flow presentation. See slide 17.

One compelling benefit of this approach is webflows are indeed declaratively programmable modules. They can encapsulate arbitrary complex processing without exposing their inner workings. They can embed other flows as subflows to scale manageably as an application grows more complex. In addition, their executions can be snapshotted and restored at a later point to support the resuming of long-running processes and in the case of a web application, proper browser navigation button behavior.

With all this in mind, track back in time to my JavaZone presentation last Thursday in Oslo, Norway. I presented this architecture and the keen Norwegians noticed that something in its implementation didn't quite feel right. Specifically, they proposed: "If a Web Flow definition defines an application module, why do I have to define that module's configuration in two places?", referring specifically to the sellItem sample app where the Flow definition is supplimented by some additional configuration somewhere else.

Damn good point.

After further discussion and some protoyping, a clear, elegant solution emerged: webflow scoped bean definitions. Essentially, a local BeanFactory scoped at a flow definition level could provide all supplimental configuration for the flow, defined using Spring's standard bean definition format. This would centralize all configuration for the flow in one place.

Of course a feature is no use without a proper example, so thanks to the observant people of Norway, I give you the improved sellItem flow definition with 100% of the controller code defined in one place:

<webflow id="sellItem" start-state="enterPriceAndItemCount">

        <view-state id="enterPriceAndItemCount" view="priceAndItemCountForm">
                <entry>
                        <action bean="sellItemAction" method="setupForm"/>
                </entry>
                <transition on="submit" to="enterCategory">
                        <action bean="sellItemAction" method="bindAndValidate">
                                <property name="validatorMethod" value="validatePriceAndItemCount"/>
                        </action>
                </transition>
        </view-state>

        <view-state id="enterCategory" view="categoryForm">
                <transition on="submit" to="requiresShipping">
                        <action bean="sellItemAction" method="bindAndValidate"/>
                </transition>
        </view-state>

        <decision-state id="requiresShipping">
                <if test="${flowScope.sale.shipping}" then="enterShippingDetails" else="processSale"/>
        </decision-state>

        <view-state id="enterShippingDetails" view="shippingDetailsForm">
                <transition on="submit" to="processSale">
                        <action bean="sellItemAction" method="bindAndValidate"/>
                </transition>
        </view-state>

        <action-state id="processSale">
                <action bean="saleProcessor" method="process(sale)"/>
                <transition on="success" to="showCostOverview"/>
        </action-state>
        
        <end-state id="showCostOverview" view="costOverview"/>
        
        <bean id="sellItemAction" class="org.springframework.webflow.action.FormAction">
                <property name="formObjectName" value="sale"/>
                <property name="formObjectClass" value="org.springframework.webflow.samples.sellitem.Sale"/>
                <property name="validator">
                        <bean class="org.springframework.webflow.samples.sellitem.SaleValidator"/>
                </property>
        </bean>

</webflow>

That's it. This 40 or so lines of code is literally all the controller code needed to model this "sell item" process flow in its entireity. The "saleProcessor" above is an existing transactional POJO business object this flow binds directly to when the "processSale" state is entered.

Thank you Oslo! I *really* appreciated the quality feedback and the warm reception I received in Norway. Expect this important feature to be included in the upcoming SWF 1.0 RC1 release.

( Sep 19 2005, 08:34:23 PM EDT ) Permalink Comments [5] 20050919
Bye Florida, Hello Europe
Wednesday August 17, 2005

Welp, it's 2:00 AM and I'm still up -- busy tieing up loose ends, packing (well, my wife is doing the packing, I'm doing more watching), and yes, now blogging. :-)

Tommorrow morning Keri and I will embark upon a 31 day adventure through Europe, starting in Amsterdam, heading to Brussels/Leuven, on to Paris, down to Barcelona, over to Rome, accross to Vienna, straight up to Stockholm, and ending in Oslo (JavaZone!).

Along the way we're fortunate enough to get to care for Bram's cats (thanks for the place to crash!), drink some Belgium beer with Erwin (been warming up with Chimay), and see the sites of Salzkammergut with Juergen. I'm really excited about experiencing the places where my colleagues live and work everyday, and making a vacation out of it, too! :-) I'm also really looking forward to returning to Norway (such energy there, last time...).

This will be my first real vacation in a long time: it's certainly due in that respect, but in another respect as well: Keri and I are expecting our "first" on February 21st, and this is the way we celebrate in our family. :-)

For you guys making it to JavaZone in Oslo, I'll be speaking there on Spring Web Flow, so come check it out! Erwin, Colin, and the rest of the Spring team will be continuing our push towards 1.0, aligned with Spring 1.3 RC1.

( Aug 17 2005, 02:24:19 AM EDT ) Permalink Comments [8] 20050817
Significant New Spring Web Flow Features
Tuesday August 16, 2005

Since Spring Web Flow PR5, we've added two significant features in preparation for our first official release candidate. These features are what this entry is about.

Lets get to it, with the first of the two:

Support for bean invoking actions, with a rich bean method binding capability. This means you can now have any public method on any bean invoked directly from within a Web Flow, treated as a standard Web Flow action. And not just no-arg methods, either: Web Flow, leveraging Spring's method binding machinery, can call any method on any bean, and pass it argument values. It can even apply type conversion to those argument values before invoking the target method.

What benefits does this bring to the table? Well, since you can now bind directly to a business object operation from a Web Flow action state definition, you no longer have to code a specific Action class or method in many cases. The amount of controller code you write is literally reduced to:

        <action-state id="executeSearch">
                <action bean="phonebook" method="search(searchCriteria)"/>
                <transition on="success" to="displayResults"/>
        </action-state>   

This example pulls from our recently updated "Phonebook" sample, where the above definition says: When the "executeSearch" state is entered, call the "search" method on the "phonebook" bean, passing in the "searchCriteria" argument. Previously, a "SearchPhonebookAction" we coded did this "glue work". Suffice to say, that class is now in the CVS Attic :-).

Where does SWF get the value for the searchCriteria argument? It's configurable, but Web Flow will try Flow Scope, Request Scope, and the most recent event parameter map in that order until it finds a match. In the Phonebook example, it pulls the searchCriteria argument from Flow Scope after form data binding and validation occurs.

Note you may optionally specify the parameter type here (e.g method="search(SearchCriteria searchCriteria)"). When you do that, if SWF sees that the resolved argument type is different from the target method's parameter type it performs a type conversion.

With this addition, SWF joins a few other frameworks (Tapestry 4) capable of performing this smart method binding. SWF goes a step further by leveraging this base capability within a state machine that makes it very easy to step from one state to another and chain behavior. And it binds directly to POJOs! Essentially, you tell Web Flow declaratively to orchestrate calls on POJO business methods as part of a web conversation, and it just does it, without the controller glue code hassel. In addition, SWF sources method arguments for you from the right scope (flow, request, or event) seamlessly, all the while your code stays fully decoupled from Web Flow APIs. This is a very significant feature.

The next feature is just as significant:

Support for stateful actions, whose state is managed in flow scope transparently. We support two strategies out of the box: a StatefulActionProxy, which simply persists the entire Action instance and relies on standard Java serialization, and a @Stateful annotation-based mechanism which uses a memento to save and restore the state of any @Stateful bean.

The latter is of particular interest and deservant of elaboration with an example:

Let's say you have a simple flow to transfer funds from one bank account to another. To accomplish, this you might have a TransferCommand that actually performs the transfer:

public class TransferCommand {
        private Customer customer;

        private AccountNumber sourceAccountNumber;
        
        private AccountNumber targetAcountNumber;

        private MonetaryAmount amount;

        private AccountDao accountDao;

        private TransferConfirmation confirmation;

        public TransferCommand(Customer customer, AccountDao accountDao) {
                this.customer = customer;
                this.accountDao = accountDao;
        }
        
        // getter/setters omitted

        @Transactional
        public void execute() throws NotEnoughFundsException {
                Account sourceAccount = accountDao.loadCustomerAccount(
                                customer.getId(), getSourceAccountNumber());
                Account targetAccount = accountDao.loadCustomerAccount(
                                customer.getId(), getTargetAccountNumber());
                if (getAmount().gt(sourceAccount.getAvailableBalance())) {
                        throw new NotEnoughFundsException(
                                        getSourceAccountNumber(), sourceAccount
                                                        .getAvailableBalance(), getAmount()));
                }
                sourceAccount.decrementBalance(getAmount());
                targetAccount.incrementBalance(getAmount());
                this.confirmation = new TransferConfirmation(this);
                accountDao.insertTransferConfirmation(confirmation);
        }
}

This is an example of the classic GOF command pattern, where the command encapsulates both the state and behaivior to perform some action. Note this class does not depend on any Web Flow API.

Now, what you can do is annotate this TransferCommand as @Stateful, and all non @Transient fields will automatically be managed by the executing flow (conversation) this command participates in. Specifically, before the command is invoked by SWF its state will be restored from flow Scope (if previously saved), and after invocation it's state will be saved out to flow Scope. This happens externally using a memento, so the command itself is oblivious to state management concerns. It doesn't even have to be serializable.

@Stateful
public class TransferCommand {
    ...

    @Transient
    private AccountDao accountDao;

Some of you might note this looks a little like @Stateful EJBs proposed for EJB 3. Perhaps, it's also similiar to what Tapestry does with its notion of page properties. In any case, if you look at what Web Flow does -- manage the state and orchestration of a web conversation -- it just fits. You need to get the data captured in a conversation into the hands of worker objects that own the data and can do the processing. This mechanism provides a near transparent way of doing this, without the hassel of dealing with Java serialization and transient reference restoration.

In conclusion, these two new features add significantly to what is already a very, very capable web controller framework. The best thing for me, personally, when implementing these was how they just "fit". Creating good software is hard, but when you get fortunate and get it right adding new features becomes easy, natural, and happens with no fundamental changes in architecture.

Enjoy! Let us know what you think at forum.springframework.org.

( Aug 16 2005, 02:09:30 PM EDT ) Permalink Comments [9] 20050816
Think Big: The Spring Experience 2005
Friday August 12, 2005

Think Big


( Aug 12 2005, 12:00:56 AM EDT ) Permalink Comments [0] 20050812
To: Ted, From: Keith
Wednesday August 03, 2005

Hi out there Ted,

I'd like to respond to some of your counterpoints.

Ted said...
"The fact that Spring chooses to express its application code via POJOs is dangerous"

How are you suggesting we express our application code, then? Using the servlet API, with low level doGet and doPost operations and javax.servlet.* imports? Something else entirely? We as agile Java developers want portable application code reusable in a variety of environments (from test, to web, to EJB, to standalone app as needed): simple J2SE-powered POJOs give us that leverage.

"So, then, what I hear you saying is that Spring doesn't directly enable easier building and testing, because the focus here is for people to build POJOs and front it with whatever"

Right, Spring does not directly help with unit testing, it assists indirectly by enabling a POJO based design model. Spring does directly help with integration testing, handling system configuration and decoupling the process of system configuration from the execution of system integration test logic.

"And thus the actual container into which the POJO is deployed is more or less irrelevant, yah?"

Irrelevant from an API perspective typically, yes, as most application code can be written without a dependency on the container. Relevant from other perspectives. The quality of the container implementation and its capabilties towards applying declarative services to POJOs are both very important and very relavent. And Spring has both a high quality container implementation and a comprehensive set of service abstractions that build on standard J2EE APIs (whose benefits are well documented and leveraged by thousands of Spring users).

"we both agree that your object model [belongs] [in-process]"

Yes, we both agree. I've read Fowler's EAA patterns three times myself. :-)

"Dependency injection *only* saves a single function call, that of the JNDI lookup, and unfortunately you cannot control when the dependency gets injected, unlike in the JNDI case where you control when the dependency gets resolved."

This is not the case: DI saves a lot more than a single function call. Proper use of JNDI entails proper creation of an InitialContext and proper exception handling. That's fairly verbose and pretty easy to do inconsistently. With Spring, you get modularization of the proper way to do a JNDI lookup in once place (a JNDIObjectFactoryBean), and if you need a JNDI-located object you have a factory bean locate it and the container inject it for you. Bottom line: with this approach, you never code a redundant JNDI lookup again yet still source objects from JNDI where it makes sense.

Going further, if you need a refreshable JNDI object, you can inject a strongly-typed locator interface whose implementation delegates to a JNDIObjectFactoryBean on each invocation (or according to some other refresh policy--perhaps one thats non trivial!). Spring supports dynamic ways of generating such locator implementations, again using proxies. Alternatively, you could even inject a proxy that implements the object's business interface, masquerading as the "target object" and delegating to JNDI for target object resolution! The net benefit is your POJOs stay decoupled from specific service lookup strategy, or even knowledge of a locator at all! Ask anyone who uses Spring: Spring makes this very easy to do, and the benefits lead to simpler, more reusable application code.

"Proxy-based AOP is essentially what CORBA, RMI, DCOM, and other remoting tools do already--Spring simply joins a handful of other technologies (unfortunately NOT EJB prior to 3.0) that expose that interceptor stack to you."

Do these other technologies have a pointcut model? I don't believe so. Spring AOP is proxy-based AOP with a pointcut model for selectively applying aspects at certain points of program execution. It's more than just interception. It is not remoting.

"Now you've lost me--an interface is good, but EJB is somehow a "first class dependency"?

EJB is not the best choice for fine-grained, in-process OO. A 2.x EJB is more expensive to code and more expensive to deploy than a POJO business interface and implementation. Everyone these days knows this, you of course know this. An EJB calling another EJB mandates use of JNDI and is arguably an anti-pattern. Mocking out JNDI is not that easy to do. All of this adds up--you simply can't deny POJOs are simpler. With a POJO-based container it's considerably easier to have a fine-grained set of objects coordinating and messaging each other in process, it's easier to plug in different object implementations. And there is none of this extending EJBLocalObject or EJBRemoteObject, baking in "first-class" assumptions about whether or not a business component is distributed and how it is distributed (NOW THAT'S DANGEROUS!)

... All I was trying to say here was we Spring users prefer implementing an interface when said interface describes a contract (or role) that is directly realized by the object implementation in question... and we have no problem with that. This is unlike some of the annotation happy stuff I've seen lately... it was a general aside, really.

"Sorry, no. Spring is a reinvention of the EJB concept designed to run in-process, more or less a reinvention of the old JavaBeans model."

You go from calling Spring "entirely useful" to "a reinvention". I don't get it. Spring doesn't reinvent JavaBeans, it reuses the JavaBeans-model for configuration. Spring doesn't replace EJB: objects managed by Spring containers are accessible from EJBs for cases where EJB is appropriate. Spring is not just a container technology, it's a full-stack application framework that integrates a number of technologies into a cohesive platform based on POJO programming model. Spring is far from a reinvention, and I stand by my bottom line:

"Spring enables a non-invasive, scalable up+down POJO-based design model and that is the major innovation that has brought agile J2EE into the mainstream."

( Aug 03 2005, 01:01:14 AM EDT ) Permalink Comments [4] 20050803
Me in St. Augustine, Fl.
Copyright 2004-2005 Keith Preston Donald


You are viewing a mobilized version of this site...
View original page here

Mobilized by Mowser Mowser