ThinWire is an amazing Rich Internet Application (RIA) framework that caters to Java developers. The concept is extremely similar to other frameworks out there such as the Google Web Toolkit (GWT), ZK, Echo2, wingS, Flex and some others. Lets just say that most people who use ThinWire and these other frameworks can increase productivity 10-fold for web-based applications – they’re that big of a deal.

After quite a lengthy period of evaluation of all of these frameworks, I chose ThinWire as my current favorite. The reasons why and my selection criteria are worth an entirely different post – there’s a lot to say there. But, the purpose of this entry is to show folks how to integrate Spring (and all that it provides, such as Hibernate utilities, et. al.) with ThinWire. There’s not any real documentation on how to do that cleanly, and ThinWire doesn’t support it directly yet (maybe they will soon?), so I hope to fill the gap here.

So for the remainder of this article, I assume you know what Spring is and have used it in actual application before. I also assume that you have at least seen what ThinWire is and have looked at its API just a little, even if you haven’t used it yet. Anyway, lets get started.

ThinWire’s biggest appeal is that you program the full web-app in 100% Java, just like Swing (but its not Swing), and never worry about JSPs, CSS, template frameworks, tag libraries, or JavaScript and all the labor overhead they imply. As such, here’s a snippet of what a Hello World application might look like:

import thinwire.ui.*;
import thinwire.ui.event.*;

public class Hello {

public static void main( String[] args ) {
//Create and set initial position for components
final Dialog dialog = new Dialog( "Hello World, ThinWire Style!" );
dialog.setBounds( 25, 25, 215, 120 );
Label label = new Label( "Hello, what is your name?" );
label.setBounds( 5, 5, 200, 25 );
final TextField input = new TextField();
input.setBounds( 5, 35, 200, 25 );
Button button = new Button( "Ok" );
button.setBounds( 55, 65, 100, 25 );

//When button is clicked, close modal dialog and say hello
button.addActionListener( "click", new ActionListener() {
public void actionPerformed( ActionEvent ev ) {
MessageBox.confirm( "Hello " + input.getText() + "!" );
dialog.setVisible( false );
}
} );

//Add components to dialog
dialog.getChildren().add( label );
dialog.getChildren().add( input );
dialog.getChildren().add( button );

//Show dialog and wait for "OK" press
dialog.setVisible( true );
}
}

Looks almost exactly like a Swing app huh? Pretty sweet – no HTML/JavaScript/CSS/DHTML necessary.

So, given that it looks like a normal ‘standalone’ application, you might think to integrate Spring by just manually instantiating a ClassPathXmlApplicationContext with your spring.xml files on the claspath, and be on your way.

Unfortunately, this isn’t a good idea.

You see, because this is a web application, we only really want one ApplicationContext for the entire application, which is standard operating procedure for Spring webapps. But, without delving into the ThinWire source code, you wouldn’t know that there is a ThinWire Application instance per user HttpSession. Since I’ve already ‘delved’ for you, you can read on comfortable knowing that we won’t instantiate a full Spring ApplicationContext (including connection pools, and Hibernate SessionFactories and everything else) per user. That would be bad.

So, how do all of the per-user ThinWire Application instances access the single Spring ApplicationContext? Well I thought it might be as easy as something like

WebApplicationContext appCtx = WebApplicationContextUtils.getWebApplicationContext( servletContext );

This is after all how most other frameworks access the ApplicationContext when there isn’t native Spring integration code. I thought it would be just as easy. But, unfortunately it isn’t (don’t worry, a good, fairly clean solution is below – keep reading).

My initial idea was to acquire the ServletContext somehow, either via a ServletFilter or ContextListener. I thought I could create a ServletFilter that wraps the ThinWire servlet, then during the doFilter( request, response ) method, I could just just get the WebApplication context and bind it to the thread via a ThreadLocal so it would be accessible to any execution code in my ThinWire Application. Then, I could have done this anywhere within my ThinWire application code:


//after receiving a gui event, say after a 'delete user' button was clicked:
Integer userId = //get user id from button event
WebApplicationContext appCtx = (WebApplicationContext)myThreadLocal.get();
UserManager userManager = appCtx.get( "userManager" );
userManager.delete( userId );

Unfortunately, as you might have expected, this won’t work.

When the ThinWire servlet creates a new Application, that Application object creates an Event thread for the Application – exactly the same way Swing operates. When a request comes in, ThinWire creates an Event based on the request, and hands it off the the Application’s Event queue (which is processed by this separate Event thread). So, this means the incoming HttpServletRequest thread is not the same thread that the processes the Event. So, binding the WebApplicationContext to the ThreadLocal in a servlet filter will not work, since that thread is not the one that calls the gui. Bummer :(

So, my next step was to see if the ServletContext can be attached to that seperate Event thread the first time the user’s Application instance is created. Nope – that won’t work either, since ThinWire doesn’t pass it along during creation of the event thread, and its not something I can ovverride (the ThinWire WebApplication class is final – I can’t subclass it to add this behavior – I’ll have to talk to the TW guys about that or at least seeing if I can contribute a revised version using the Template design pattern allowing subclass hooks for special behavior. But anyway, I digress…).

So, I can’t attach the WebApplicationContext to the request thread, and I can’t pass it off to the Event thread. So, the only thing I’m stuck with (again, without changing the ThinWire API) is to use a static class attribute and store the appCtx in static memory. Ugh, I hate doing that (I hate statics), but its the only option at the moment. So we’ll do that.

But, where does should this static WebAppliationContext instance reside?

As I mentioned previously, one can manually create a ClassPathXmlApplicationContext instance inside your ThinWire application’s main method, and assign to a static class attribute, accessible by any other of your application classes.

But, I don’t like this approach. Manually creating instances is less desirable when Spring has already gone through the trouble (and done quite a nice job I might add) in setting up a Spring WebApplicationContext for me just by web.xml configuration. So, I’d much rather use this already-existing, easier-to-configure mechanism, than doing it myself.

So, the following web.xml config is how Spring does this for standard webapps:

xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">

log4jConfigLocation /WEB-INF/log4j.properties

org.springframework.web.util.Log4jConfigListener

org.springframework.web.context.ContextLoaderListener

]]>

But, we need to make the WebApplicationContext instance accessible statically. So, I feel the best way to do this is to subclass Spring’s ContextLoaderListener. The subclass assigns it to a static variable:

package com.domain.myapp.web.servlet;

import org.springframework.web.context.ContextLoaderListener;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;

import javax.servlet.ServletContextEvent;

/**
* @author Les Hazlewood
*/
public class StaticContextLoaderListener extends ContextLoaderListener {

private static WebApplicationContext webAppCtx = null;

public void contextInitialized( ServletContextEvent event ) {
super.contextInitialized( event );
webAppCtx = WebApplicationContextUtils.getRequiredWebApplicationContext( event.getServletContext() );
}

public void contextDestroyed( ServletContextEvent event ) {
webAppCtx = null;
super.contextDestroyed( event );
}

public static WebApplicationContext getWebApplicationContext() {
return webAppCtx;
}
}

Then we have to change the appropriate snippet in web.xml to use this class instead of the parent:

com.domain.myapp.web.servlet.StaticContextLoaderListener

There! Now the WebApplicationContext will be accessible to any of my ThinWire Application’s code whenever I need it by calling StaticContextLoaderListener.getWebApplicationContext();

But – we’re not quite done – one last step to go. Its kind of ugly to call the StaticContextLoaderListener directly in your code, and, it doesn’t have any other methods to be of use in your ThinWire application. So, I add one more class to wrap this call and provide other utility methods:

package com.domain.myapp.util;

import org.springframework.beans.BeansException;
import org.springframework.web.context.WebApplicationContext;
import com.domain.myapp.party.UserManager;
import com.domain.myapp.web.servlet.StaticContextLoaderListener;

/**
* This is really a ServiceLocator, using the Spring WebApplicationContext for location. Not ideal (I would rather have
* ThinWire support Dependency Injection so I don't need to use the ServiceLocator pattern), but it suffices until that (if ever) is implemented.
*
* @author Les Hazlewood
*/
public final class Spring {

//wraps the StaticContextLoaderListener call so the rest of your app code
//doesn't have to 'know' about that class:
public static WebApplicationContext getAppCtx(){
return StaticContextLoaderListener.getWebApplicationContext();
}

//returns any bean from the Spring appctx:
public static Object getBean( String name ) throws BeansException {
return getWebApplicationContext().getBean( name );
}

//more convenient typesafe call that should be used by app code directly:
public static UserManager getUserManager() {
return (UserManager)getBean( "userManager" );
}
}

Finally. Spring support is finished.

Now, if you compare the previous button event code above, it would look like this now:


//after receiving a gui event, say after a 'delete user' button was clicked:
Integer userId = //get user id from button event
UserManager userManager = Spring.getUserManager();
userManager.delete( userId );

Much cleaner! You can now use these Spring.* utility methods in any part of your ThinWire application code.

Happy coding w/ ThinWire and Spring!