Tuesday, July 29, 2008

Death by Annotations

Every so often I read how 'we're all going to be drowning in annotations' or how someone's 'annotations are exploding' and consequently how 'OMG annotations suck'. I'd agree it's a problem. But, seeing as how Metawidget is probably going to make it worse, not better, I thought I should offer a balanced view.

Here's the basic problem. Here's a piece of Real Code, from one of my production systems, that uses JPA, JAXB, Hibernate extensions and Metawidget:

@OneToMany( cascade = CascadeType.ALL )
@JoinColumn( name = "survey_id" )
@IndexColumn( name = "number", base = 1 )
@Cascade( org.hibernate.annotations.CascadeType.DELETE_ORPHAN )
@UiComesAfter( "dateExtension" )
@XmlElementWrapper
@XmlElement( name = "question" )
public List getQuestions()
{
   return mQuestions;
}

A lot of people are going to look at that, think it looks really messy, and turn off. And I agree - it does look really messy. But it's important to consider what we've gained here, and what the alternative is:
  • it's less code, and more typesafe, than 4 separate XML configuration files

  • the metadata is closer to the thing the metadata is referring to

  • whilst it may seem like a lot of annotations for little actual code (the bit in bold), that's backward thinking: annotations are code, albeit declarative code. And it's a lot less code to write something declaratively than procedurally
So whilst I don't necessarily think annotations are as good as life can get, I do think they're better than anything we ever had before. The challenge now is how do we make them better?

Sunday, July 20, 2008

JEXL: An Expression Language (EL) for Swing?

In response to this forum posting, the next release of Metawidget (v0.6) adds support for Commons JEXL.

This lets environments that don't have an Expression Language (EL) of their own (environments like Swing, GWT etc) wire components together more easily. So, for example you can do:

class Person {
   public boolean retired;

   @JexlAttribute( name="hidden", value="${!this.retired}" )
   public Date retirementDate;
}

And the 'retirement date' field will only appear if the 'retired' checkbox is checked.

To enable JEXL support, you just need to add a JexlInspector into your CompositeInspector.

Thursday, July 10, 2008

Metawidget and Rebinding

One of the cool features of Metawidget is automatic binding between the business model and the UI, using whatever technologies the underlying platform supports (such as BeansBinding).

Up until now, however, there has been a bit of a 'gotcha' with this.

For Metawidgets that don't use automatic binding, the general approach is to call setToInspect and then setValue to populate the generated UI from business model values. This technique has an implicit side effect: the values can also be repopulated as many times as you like with new source objects, without re-calling setToInspect. This allows the Metawidget to be generated once and reused many times, mitigating the performance cost of generation.

For Metawidgets that do use automatic binding, however, setValue is never used. Setting new values requires re-calling setToInspect (and re-running generation) for every new source object.

To avoid this the new release of Metawidget supports a second, lightweight version of setToInspect called rebind. This function is only relevant when using automatic binding. Using rebind, a Metawidget can update the values in its generated widgets without re-running generation. This allows the Metawidget to be generated once and reused many times.

The downside of rebind is that the rebound source object must have exactly the same set of field names as the original object, else the call will fail. It becomes the responsibility of the caller to ensure this consistency.

For an example of rebinding, see the GWT Address Book sample application.

Metawidget: Javassist versus @UiComesAfter

Update: the APIs shown in this blog entry have changed slightly in newer releases of Metawidget. Specifically PropertyStyles are now set as object instances, not as classes. Please download the latest documentation from http://metawidget.org

There's been some feedback that having to annotate your business model with @UiComesAfter like this...

public class Person {
   public String name;

   @UiComesAfter( "name" )
   public int age;

   @UiComesAfter( "age" )
   public boolean retired;
}

...to order the UI fields is a bit annoying. I agree. The problem is the Java classfile specification doesn't maintain the original declaration order of methods and fields, so we can't extract this information from reflection.

Of course, Metawidget can extract it from other places instead, such as hibernate-config.xml or metawidget-metadata.xml files, where available - but failing that you have to provide the ordering information somehow.

Well, thanks to an idea by those guys from the Tapestry team, there is now another option: JavassistPropertyStyle.

If you have Javassist available in your environment, the new JavassistPropertyStyle can use Javassist to extract debug information from the Java classfile and order the fields according to source code line number! No more @UiComesAfter! You use it like this:

<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"
   config="org.metawidget.inspector.impl.BasePropertyInspectorConfig">
   <propertyStyle>
      org.metawidget.inspector.impl.propertystyle.javassist.JavassistPropertyStyle
   </propertyStyle>

</propertyTypeInspector>

There are downsides. First, if you accidentally recompile your business model without debug information (like, say, when you go from development to production) all your fields will quietly unorder themselves. To protect from this, JavassistPropertyStyle fails early and fails hard if there is no debug information.

Second, JavassistPropertyStyle uses the following sorting algorithm:
  • superclass public fields come first, sorted by name

  • superclass methods come next, sorted by getter line number (or, if no getter, setter line number)

  • public fields come next, sorted by name

  • methods come last, sorted by getter line number (or, if no getter, setter line number)
This algorithm is less flexible than @UiComesAfter, which can interleave superclass and subclass properties. However, it is possible to use both @UiComesAfter and JavassistPropertyStyle together to get the best of both worlds!

Runtime UI Generator: Metawidget v0.55

Version 0.55 of Metawidget, the runtime UI generator, is now available. This release includes:

Many thanks to Tom Bee for all his help testing this release!

As always, the best place to start is the Reference Documentation:


Your continued feedback is invaluable to us. Please download it and let us know what you think.

Wednesday, July 2, 2008

Metawidget and Filtering Properties

Update: the APIs shown in this blog entry have changed slightly in newer releases of Metawidget. Specifically PropertyStyles are now set as object instances, not classes. Please download the latest documentation from http://metawidget.org

I was recently asked whether Metawidget can filter out properties during its inspection process. You may want to do this if, for example, your business classes all extend a common base class that has framework-specific properties that are not 'real' business model properties.

The answer is yes! Metawidget is very flexible in what it does (and does not) inspect.

Whilst you could roll your own ProperyTypeInspector, the convenience implementation BasePropertyTypeInspector - which all annotation-based inspectors extend - already supports pluggable PropertyStyles. The PropertyStyle interface allows fine-grained control over what is considered a 'property'. We use this for both Groovy support, and also StrutsActionFormPropertyStyle:

public class StrutsActionFormPropertyStyle extends JavaBeanPropertyStyle {

   protected String[] getExcludeNames() {
      return new String[] { "servlet", "servletWrapper", "multipartRequestHandler" };
   }
}

Here, we can see StrutsActionFormPropertyStyle excludes, for example, the property 'servletWrapper' which is a method defined by the Struts ActionForm base class (ActionForm.getServletWrapper).

We can also exclude based on subtype. From GroovyProperty:

   protected Class[] getExcludeTypes() {
      return new Class[] { Class.class, MetaClass.class };
   }

To filter your own base classes:

Step 1: Define a PropertyStyle

In most cases, it's easiest to extend JavaBeanPropertyStyle (see StrutsActionFormPropertyStyle above)

Step 2: Tell the Inspectors to use the PropertyStyle

If you're using inspector-config.xml:

<propertyTypeInspector xmlns="java:org.metawidget.inspector.propertytype"
   config="org.metawidget.inspector.impl.BasePropertyInspectorConfig">
   <propertyStyle>
      org.metawidget.inspector.impl.propertystyle.struts.StrutsActionFormPropertyStyle
   </propertyStyle>

</propertyTypeInspector>

If you're setting your Inspector programmatically:

BasePropertyInspectorConfig config = new BasePropertyInspectorConfig();
config.setPropertyStyle( StrutsActionFormPropertyStyle.class );
metawidget.setInspector( new PropertyTypeInspector( config ) );

And that's it!