« February 2008 | Main | April 2008 »
March 27, 2008
Injecting Spring 2.5 beans into Stripes Actions
I've been playing around with Stripes, a light-weight, well-designed simple Java web MVC framework recently. I haven't had the pleasure of working with it on a production application yet, but hope to sometime soon. Meanwhile, I've been tinkering on a pet project.
As you may know, we often use Spring for lifecycle management of our services and dependency injection (among other uses). In a Stripes + Spring application, you can imagine Spring-managed services being used by Stripes Actions (i.e. controllers). Every incoming HTTP request results in a new instance of a Stripes Action, thus the newly created actions must have their dependencies injected for every request.
Stripes ships with a SpringInterceptor that supports annotating fields and methods on your actions with @SpringBean. While this works fine, I was pretty interested in being able to use Spring 2.5's annotations for marking what should be injected on my actions, so I created a Spring25Interceptor (see code below).
The Spring25Interceptor is configured the same way as the out-of-the-box SpringInterceptor. In your web.xml:
...
<filter>
<display-name>Stripes Filter</display-name>
<filter-name>StripesFilter</filter-name>
<filter-class>net.sourceforge.stripes.controller.StripesFilter</filter-class>
<init-param>
<param-name>Interceptor.Classes</param-name>
<param-value>
net.sourceforge.stripes.integration.spring.Spring25Interceptor
</param-value>
</init-param>
...
</filter>
...
Since it uses the same annotations that Spring 2.5's supports (@Autowired, @Resource, and @Qualifier), annotating your Stripes Actions is easy and should look pretty familiar (see Juergen's blog for a comprehensive overview). Here are a few hypothetical examples:
public class LandingActionBean extends AbstractActionBean { // Autowire by type (looks for a bean in the application context of type ServiceA) @Autowired ServiceA serviceA; // Autowire by name (looks for a bean with the name 'serviceB') @Resource ServiceB serviceB; // Autowire by name (looks for a bean with the name 'serviceC') @Autowired @Qualifier("serviceC") ServiceC serviceCee; // Method injection examples (all of the above can be applied to methods as well) @Autowired public void setServiceA(ServiceA a) { this.serviceA = a; } @Autowired public void setServices(ServiceA a, ServiceB b, ServiceC c) { this.serviceA = a; this.serviceB = b; this.serviceC = c; } @DefaultHandler public Resolution execute() { return new ForwardResolution("/landing.jsp"); } }
While it may be considered a subtle improvement, I really like the fact that with this new interceptor, my application has a consistent syntax for dependency injection across the tiers. Additionally, the semantics of the Spring annotations are also consistent and shared throughout. An added bonus is that the new Interceptor is significantly smaller than the existing one.
Here's the Interceptor code that does the dependency injection into actions:
import net.sourceforge.stripes.action.*; import net.sourceforge.stripes.controller.*; import net.sourceforge.stripes.util.*; import org.springframework.beans.factory.config.*; import org.springframework.context.*; import org.springframework.util.*; import org.springframework.web.context.support.*; import javax.servlet.*; @Intercepts(LifecycleStage.ActionBeanResolution) public class Spring25Interceptor implements Interceptor { private static final Log log = Log.getInstance(Spring25Interceptor.class); public Resolution intercept(ExecutionContext context) throws Exception { Resolution resolution = context.proceed(); log.debug("Running Spring dependency injection for instance of ", context.getActionBean().getClass().getSimpleName()); ServletContext servletContext = StripesFilter.getConfiguration().getServletContext(); ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext); AutowireCapableBeanFactory beanFactory = applicationContext.getAutowireCapableBeanFactory(); beanFactory.autowireBeanProperties(context.getActionBean(), AutowireCapableBeanFactory.AUTOWIRE_NO, false); beanFactory.initializeBean(context.getActionBean(), StringUtils.uncapitalize(context.getActionBean().getClass().getSimpleName())); return resolution; } }
I've created a enhancement request for this feature, though the comment-thread is a little bit all over the place. You can find the latest version of the code and Javadoc as an attachment on the issue.
Posted by Christian Nelson at 9:52 AM | Comments (0)
March 26, 2008
Alan Cooper presented by IxDA at Hot Studio
Alan Cooper spoke last night around the corner at Hot Studio at an event organized by the San Francisco chapter of IxDA, the Interaction Design Association. His talk was titled "An Insurgence of Quality" and was similar to the talk he gave at an IxDA event in Savannah in February.
Cooper talked for a while about the concept of craftsmanship as it relates to designers and developers. He emphasized that craftsmanship prioritizes quality over cost and time. While keeping costs down and accomplishing tasks in a timely matter are important, craftspeople do not sacrifice quality for either. They take the time and spend the money that it takes to do it right.
The Triad
He proposes that interaction designers and programmers are craftspeople who should unite around their common commitment to quality and demand that business take the time and spend the money to do things right. Doing things right in his proposal means a three-stage process of:
Interaction Design » Design Engineering » Production Engineering
Interaction Design starts with interaction designers talking to stakeholders, both customers and users, to understand the domain and behavioral requirements for software. They then design behaviors for the software based on this primary research.
Design Engineering starts with programmers working with interaction designers to evaluate the feasibility of the designed behavior, iterating over the designs in collaboration with the interaction designers. In this phase, programmers also hash out how the final system will work through a series of short iterations of development focused on identifying and solving technical problems. This product of this work is not production code, it is the definition of how the final system should be built. He feels that Agile methods are very appropriate for this phase.
Production Engineering is the process of cranking out the final product based on detailed written specifications produced in the prior two phases. Cooper claims that since all the details have been worked out, both in behavior and technology, this phase will be maximally efficient — a straight line of productive software development from start to delivery. He feels that a process like RUP is appropriate for this phase.
Cooper calls this process the Triad and elaborates on his web site.
His split between Design Engineering and Production Engineering is based partly on his claim that programmers can fundamentally be divided into two types — those who above all else want to do it right and those who want to get it done and out the door. The ones who want to do it right are the craftsmen and are best suited to be Design Engineering practitioners. The ones who want to get it done are best suited to be Production Engineering practitioners.
Hmmm
Cooper seems to have come a long way in his understanding and appreciation of Agile practices since his notorious 2002 conversation with Kent Beck. Iteration, collaboration and feedback are all important characteristics of his Design Engineering phase.
However, it's not clear why Production Engineering would not benefit from an Agile process focused on frequent delivery of working software and tracking toward an end goal that has good definition. It's also not clear why Design Engineering shouldn't produce early versions of working software that can be tested with users and be the foundation of the final delivered software.
Some projects may benefit from his phasing. I could see his process benefiting a year long initiative to build shrink wrapped software where there is a desire to offshore a portion of the development.
But web software development does not work like that. You can release web software early and often without compromising quality. Yes, heed Cooper's warning not to rush prematurely to market with ill-conceived products. Stay focused on what matters to users and let them use it as early as you can. Cooper himself cites Sergey Brin of Google as saying, paraphrased, "that early on they were in no rush to have people try Google today. Tomorrow it would be better. Tomorrow would be fine." Google was in fact out early with something simple but well-considered in users' hands. They've evolved their product daily and weekly to what it is today. They are proof that releasing early does not necessarily mean prematurely.
Cooper's attempt to place interaction designers between programmers and their customers and users is also troubling. He claims that programmers don't want to talk to users which we know is simply wrong. In our experience, the further we are from our customer and end user in a project the greater the likelihood the project will go awry. I'd rather see an approach that figures out how to get programmers and interaction designers understanding the needs of their customers and users together and using that understanding to direct work in their specialized areas of expertise.
As a programmer, Cooper can be a bit hard to swallow. He definitely encourages an "us and them" perspective with his tongue-in-cheek comments about the inscrutability of programmers and their work. Of course he does the same for other relationships with a slew of disparaging claims about management and business executives.
Yet it's encouraging to hear him emphasize the importance of true collaboration between designers and programmers through the life of a project. I hope his understanding of programmers and their craft continues to evolve to support this goal.
Posted by Alon Salant at 10:33 AM | Comments (2)
