lunes 29 de octubre de 2007

Advanced data binding within the Spring Framework

Binding sits at the core of the Spring framework. In fact it is used in two different scenarios, as part of the inversion of control (IoC) container and in the MVC module. Beware that both use cases work in a completely independent way (although they share the same principles), that is the work for the former won't show up in the other unless it's replicated.

The IoC container needs binding capabilities to wire objects when injecting dependencies. This is one of the most basics features of Spring. For example, the following XML snippet declares a dependency that has to be wired on startup:

<bean id="oneBean" class="internna.OneClass">
   <property name="runtimeClass" value="java.util.Date" />
</bean>

How would Spring wire it at boot time? Well it really depends on the type, but assuming the field is declared as Class<?> runtimeClass, Spring would load the class and assign it to the variable using the correct setter (probably something like public void setRuntimeClass(Class<?> runtimeClass)).

The MVC framework twists a little the concept to be able to wire form fields to corresponding backing bean properties. In the end, everything is the same: there's a target object with a target property (of any type) that has to be filled based on the content of some string.

There's subtle magic happening there. First a String is converted to an actual object and second the binding happens. Let's try to explain it in detail.

The conversion mechanism is based on property editors. Everybody is used to work with them even if unconscious. Look at the following image of a property editor in action:



Of course, it's the typical use case. A GUI that needs to set the value of a property of something using just text (even though the actual variable may be anything..a Color or a Swing component in the example). Very common in IDEs indeed! The Spring team decided to mimic this approach even though there's no GUI really here. And such, they create PropertyEditors for the most common cases (classes, dates, files, urls...). The best of all is the extensibility of the solution (as with the rest of the framework). Custom editors can be easily developed and plugged. Let's see an example with something used in day by day development, JPA bindings.

public class JPAPropertyEditor extends PropertyEditorSupport {

   public JpaEditor(Class<?> entityClass, PersistenceFacade persistenceManager) {
      this.persistenceManager = persistenceManager;
      this.entityClass = entityClass;
   }

   public void setAsText(String text) throws IllegalArgumentException {
      Object entity = persistenceManager.find(entityClass, text);
      setValue(entity);
   }

}

As a matter of fact, the code above is surprisingly simple. It receives some text as the input (the primary key) and is capable of transforming it to a model object. The constructor may have as many parameters (of any type) as needed. This editor is then declared as any other bean in the context. As of now, Spring won't recognize the bean automatically and apply it to the bindings. It has to be registered before. This is done using a custom PropertyEditorRegistrar. This is not more than an interface with one method that has to be implemented

public class GenericRegistrar implements PropertyEditorRegistrar {

   private Map<Class<?>, PropertyEditor> propertyEditors;

   public void setPropertyEditors(Map<Class<?>, PropertyEditor> propertyEditors) {
      this.propertyEditors = propertyEditors;
   }

   public void registerCustomEditors(PropertyEditorRegistry registry) {
      if (getPropertyEditors() != null) {
         for (Class<?> editorClass : getPropertyEditors().keySet()) {
            PropertyEditor editor = getPropertyEditors().get(editorClass);
            registry.registerCustomEditor(editorClass, editor);
         }
      }
   }

}

Finally, after declaring the registrar in the XML as well, just one step is left, informing Spring:

<util:list id="registrars">
   <ref bean="genericRegistrar" />
</util:list>

<bean id="editorConfigurer" class="org.springframework...CustomEditorConfigurer">
   <property name="propertyEditorRegistrars" ref="registrars" />
</bean>

Everything is done by now and JPA entities can be binded by the framework. Take into account, that the MVC data binders need to receive this registration independently.

The second trick Spring performs during data binding is setting the values. This is done by the BeanWrapperImpl, probably one of the most important classes in the framework. And the one you should be basing a data binder implementation upon. Things you should know about this class are:
  • It wraps an instance of the bean to bind (so it cannot be a singleton)
  • It can bind any type of property (depending on the configured editors)
  • By default, all the Spring's editors are pre-configured
  • Itcan be used to obtain property descriptors and values as well.
  • MVC data binders use it internally
With this knowledge is quite easy to implement a custom data binder (for DWR, Spring Webflow, a web service or as a replacement in a form controller, for example). Here's the skeleton for one:

public class MyDataBinder extends GenericRegistrar {

   protected BeanWrapper getWrapper(Object entity) {
      BeanWrapper wrapper = new BeanWrapperImpl(entity);
      registerCustomEditors(wrapper);
      return wrapper;
   }

   public void bind(BeanWrapper beanWrapper, Map fields, Errors errors) {
      for (String fieldName : fields.keySet()) {
         try {
            PropertyValue pv = new PropertyValue(fieldName, fieldValue);
            beanWrapper.setPropertyValue(pv);
         } catch (Exception ex) {
            if (bindingErrors != null)
               bindingErrors.rejectValue(fieldName, "error.binding");
         }
      }
   }

}

Finally, just remember to check Chapter 5 of the Spring reference documentation when in doubt.

lunes 22 de octubre de 2007

Lessons to Learn : Why has McLaren failed?

Yesterday Kimi Raikkonen won the F1 World Championship. This came as a shock to many as Kimi was third entering the last race at Brasil, behind the two McLaren-Mercedes drivers (Alonso & Hamilton). As a matter of fact, Kimi has been behind them the whole season and wouldn't be champion as of today if it wasn't for the others faults. So I would like to go through some of the most flagrant errors they've make and try to map them to a common technology project.

Here's the little list:
  • Inexperience: The focused on Hamilton who is an outstanding driver, no doubt, but he lacks the experience to go through such tension. With the World Championship in his hands with just two races left he broke under the pressure.
  • Lack of backup plan: Betting on Hamilton was a risky move but ignoring the possibility of failure was catastrophic. No contingency plan was in order.
  • Poor management: When a manager starts making decisions based on heart and impulse and not on reasons the results start to go downhill.
  • Impossible teamwork: A competitive environment is expected when such high stakes are placed but McLaren has been the only team in the competition where the air was unbreathable. Hamilton has taken advantage of many of Alonso's contributions (specially to the car) but it never worked the other way around. McLaren talked about equality while always favoring one of the members of the team.
  • Lack of communication: Between the drivers, the manager and the team in general. No problem was resolved with an agreement and they never took actions against any member of the team. Of course, problems didn't solve by themselves with time..if anything, the got worse.
  • Increasing pressure: Being British and black make Hamilton a great candidate to win the championship. This added pressure to an English team already filled with it. It had to explode sooner or later.
  • Political encumbrances: As if racing was not stressing enough, both McLaren drivers had to go through a series of interviews with the regulator and where questioned and threatened about their licenses. All because a spying case they should not be involved at all.

I'm quite sure that with these conditions if it were a software project it would have failed miserably as well. The most useful lesson I get from all this is that management is in great percent responsible of the outcome of the project. If management fails to deliver even the best tools and the best people won't stand a chance.