Saturday, April 26, 2008

Metawidget and JIDE

Update: the APIs shown in this blog entry have changed slightly in newer releases of Metawidget. Specifically you can now use WidgetBuilders to plug in third-party component libraries. Please download the latest documentation from http://metawidget.org

I was recently asked whether Metawidget can output JIDE components instead of standard Swing ones. The answer is yes! Metawidget makes it easy to support third-party component libraries. Here's how:

Step 1: Derive your own JideMetawidget

Subclass SwingMetawidget and override its buildActiveWidget method to select JIDE components. For example:

package com.myapp.metawidget.jide;

import static org.metawidget.inspector.InspectionResultConstants.*;
import java.util.*;
import javax.swing.*;
import org.metawidget.util.ClassUtils;
import com.jidesoft.spinner.DateSpinner;

public class JideMetawidget extends SwingMetawidget {

   public JComponent buildActiveWidget(Map<String, String> attributes)
      throws Exception {
      String type = attributes.get( TYPE );

      if ( type != null ) {
         Class<?> clazz = ClassUtils.niceForName( type );

         // Use JIDE DateSpinner
         if (Date.class.isAssignableFrom( clazz )) {
            DateSpinner dateSpinner = new DateSpinner();
            dateSpinner.setFormat( "dd/MM/yyyy" );
            return dateSpinner;
         }

      }

      // Not for JIDE
      return super.buildActiveWidget( attributes );
   }
}

That's it! You'll get all the other Metawidget benefits (like layouts, Beans Binding, etc.) for free.

Step 2: Use JideMetawidget in your Application

To try it, download the Metawidget source and update the Swing Address Book example's ContactDialog to say...

final SwingMetawidget metawidget = new JideMetawidget();

...(on about line 135). Run the example, double click on Homer Simpson and you'll see the JIDE DateSpinner component used for the 'Date of Birth' field (previously it was just a JTextField, because vanilla Swing doesn't have a Date picker):


Note: JIDE's DateSpinner doesn't support null values, but
in the Address Book example Date is a nullable field. This causes errors if you click on anything other than Homer Simpson. Note that SwingX's JXDatePicker doesn't have this problem.

@UiComesAfter versus setFields

Update: the information shown in this blog entry has changed slightly in newer releases of Metawidget. Specifically you can now use an InspectionResultProcessor to implement a .setFields approach if you so desire. Please download the latest documentation from http://metawidget.org

I was recently asked why Metawidget uses @UiComesAfter and @UiHidden annotations to define the ordering and visibility of fields, rather than something like:

metawidget.setToInspect(myLoginBean);
metawidget.setFields("login","password");

There are a few reasons:

1. Separation of Concerns

In Metawidget's architecture, Inspectors inspect objects (at runtime) and Metawidgets display them. The Metawidget itself has no compile-time knowledge of what it is displaying. This has all sorts of useful side effects.

For example, in the Swing Address Book example application, the code says:

metawidget.setToInspect(contact);

At compile time, contact is of type Contact. But Contact is actually an abstract class. At runtime, contact can be either a BusinessContact or a PersonalContact. It would not be possible to say...

metawidget.setToInspect(contact);
metawidget.setFields("firstname","lastname","dateOfBirth");

...without introducing compile-time dependencies on the type of object, which means we could no longer just have a single ContactDialog class - we'd need two separate BusinessContactDialog and PersonalContactDialog classes.

Think of Metawidget like JPA's EntityManager. If you were building a very generic CRUD app, you could use a single EntityManager instance and a single Metawidget instance to manage your whole app.

2. Duplication of Declaration

Metawidget is very big on avoiding duplicate declarations between the UI and the application. If you do...

metawidget.setFields("firstname","lastname","dateOfBirth");

...you are effectively restating that the business class has a firstname field. If ever that field name changes (or is deleted), it now has to be changed in at least two places - more if you have several screens using that Metawidget.

3. Not just Java

It's a shame the Java bytecode specification doesn't retain the ordering of fields and methods, but not all technologies have that limitation. If you use HibernateInspector or XmlInspector, for example, their XML is inherently ordered and the inspectors use that information, instead of you having to use @UiComesAfter. SQL schemas also retain field ordering (though we don't have a SqlSchemaInspector yet).

And there are some other clever approaches we can try. Tapestry 5's ClassFactory, for example, uses Javassist to extract line numbering information and sort getter methods that way.

Conclusion

I think these are compelling reasons to avoid the setFields approach. However, feedback is very welcome!

Tuesday, April 22, 2008

Why A Runtime User Interface Generator

I was recently asked why Metawidget works at runtime, instead of pre-compiling and statically outputting generated code. There are two reasons.

Easier to Maintain

I think people like static code generators because they give the illusion of being productive: you click a few buttons and bam! you suddenly have this whole app with mountains of code written for you, all ready to go.

But I do think it's an illusion. Let's look at what, typically, you have to do with a piece of code:
  • write it
  • debug it
  • unit test it
  • update it
  • document it
  • hand it over

Whilst code generators might ease the first one (write) they're no help with the third (unit test) and they actually make the other four much worse. This is because the code they generate is usually quite poor (it being generated by generic algorithms) and because nobody, not even the developer who runs the tool, initially understands the code it produces.

Runtime generators, on the other hand, are great when it comes to updating, documenting and handing over, because a) they haven't produced a mountain of code for someone to trawl through and b) documentation on how the runtime generation process works is normally very good (eg. it's in the generator's user manual).

So I'd much prefer a runtime approach to a compile-time one. If you compare systems like JPA to those that pre-generate mountains of SELECT/INSERT/DELETE stored procedures I'm sure you'd agree.

More Powerful

There are lots of important properties of a system and the relationships between its parts that are only discernable at runtime, not statically at compile-time.

For example, the Address Book sample application adapts its screens dynamically at runtime depending on whether the incoming object is a PersonalContact or a BusinessContact. And there are @FacesNotHiddenInRole annotations that modify a UI based on the currently logged-in user.

Finally, if you're inspecting 'live' objects (as opposed to source files) you can automatically do a lot of the binding between UI and application layer, further reducing all that boilerplate code.

Monday, April 21, 2008

Useful Bounds of Automatic UI Generation

A recent post on the Metawidget forums asked why Metawidget doesn't 'do more'. Given we can automatically create forms from domain objects, and given there are solutions that can automatically persist domain objects (eg. JPA), why not 'fill in the gap' and automatically create the entire CRUD app?

And indeed, there are solutions that do this. The most notable being http://www.nakedobjects.org. The drawback with these solutions is they necessarily create very generic UIs, which bear little resemblance to how they would have appeared and functioned had they been designed by hand, with due consideration to their problem domain. For example, with Naked Objects you get this...


...which is simply not how many clients want their apps to look.

Good UI design is both art and science. When you try to 'fill in the gap' you realize there's a lot of 'art' in that gap, and trying to automate that art results in less effective UIs.

So Metawidget is not trying to be that solution. I believe that solution is less useful for many real world apps. Instead, Metawidget tries to identify the bounds between where UI generation can be useful and practical and where it becomes too generic and impractical, and stays within those bounds. I call these the 'Useful Bounds of Generation'.

Staying within the Useful Bounds of Generation lets you apply Metawidget to a large category of real world applications that fully automated CRUD solutions simply aren't interested in. You can even retrofit an existing app and remove lots of your boilerplate code.

Wednesday, April 16, 2008

Metawidget: now twice as useful!

The latest release of Metawidget includes a 'read-only mode' on all supported platforms. So now Metawidget can not only automatically generate the UI for your data entry screens...


...it can also automatically generate your data display screens too:


The mode can be toggled on and off through the setReadOnly method. It potentially doubles the number of places in your app where a chunk of boilerplate code can be replaced with a single Metawidget call.

Monday, April 14, 2008

Using Metawidget with Seam, Facelets, JPA and Hibernate Validator

This is a short tutorial on using Metawidget with JBoss Seam, Facelets, JPA and Hibernate Validator. It should take about 10 minutes.

Seam - the fusion of the best

Metawidget is a great fit for Seam, because both Metawidget and Seam are all about leveraging and integrating existing technologies. For this tutorial, we will use Seam 2.0.1.GA and JBoss 4.2.2.GA, so you'll need to download those first. Next, build the Seam Booking example using:

cd \jboss-seam-2.0.1.GA\examples\booking
ant

...and then...

cd \jboss-4.2.2.GA
copy \Applications\jboss-4.2.2.GA\server\default\deploy
   server\default\deploy
bin\run

...and finally open a Web browser to http://localhost:8080/seam-booking and check it's all running okay. We'll assume you're familiar with the standard Seam Booking app (if not, it's covered in detail in the Seam documentation).

Seam, meet Metawidget

Metawidget ships with an updated Seam Booking example. To build it...

cd \metawidget-0.43\examples\faces\seam-booking
ant

...then stop JBoss if it's still running and, as before...

cd \jboss-4.2.2.GA
copy \Applications\jboss-4.2.2.GA\server\default\deploy
   server\default\deploy
bin\run

Open a Web browser to http://localhost:8080/seam-booking and check it's running okay. The app should look remarkably similar, but the Metawidget version uses less code, is less error-prone, and is more 'proper'. What do we mean by that?

Less code

The Metawidget version of the Seam Booking app replaces book.xhtml, confirm.xhtml and hotelview.xhtml. In each case, most of the code has been replaced by a single tag. So instead of...

<s:decorate id="checkinDateDecorate" template="edit.xhtml">
  <ui:define name="label">Check In Date:</ui:define>
  <rich:calendar id="checkinDate"
   value="#{booking.checkinDate}"
   required="true"
   datePattern="MM/dd/yyyy"
   event="onblur"
   reRender="checkinDateDecorate"
   style="width: auto;"/>

   ...some 60 lines of code...

</s:decorate>

...we simply have...

<m:metawidget value="#{booking}" rendererType="div">
  <f:param name="divStyleClasses"
   value="entry,label,required,input,error errors"/>
</m:metawidget>

Less error-prone

Ironically, the Seam Booking example actually contains the exact sort of bug Metawidget is designed to avoid! In the original hotelview.xhtml, we see...

<ui:define name="label">Nightly rate:</ui:define>
<h:outputText value="#{hotel.name}">

...clearly this is wrong - nightly rate should display #{hotel.price}, not #{hotel.name}. It's a simple cut-and-paste, 'my UI has gotten out of sync with my business object' sort of bug.

Metawidget gets it right, because Metawidget outputs the code for you.

More 'proper'

Often, there's a bunch of business object metadata that really should be mapped to the UI, but it's too laborious to do. For example, Booking.java uses Hibernate Validator's @Length annotation. Ideally, we would put...

<input type="text" length="16">

...on every such occurance, but who has time to do that?

Metawidget gets it right, because Metawidget inspects the back-end, discovers the annotation, and outputs the laborious code for you.

Conclusion

That concludes this short tutorial. As we have seen, combining Metawidget with Seam saves you code and bugs, whilst making your UI more 'proper'. To learn more about Metawidget, the best place to start is the Reference Documentation.

Sunday, April 13, 2008

Automatic User Interface Generation: Metawidget v0.43

Version 0.43 of Metawidget, the tool for automatic user interface generation, is now available. This release includes:
  • Read-only mode for displaying (rather than editing) business objects
  • Facelets support
  • Mixin to ease development of custom Metawidgets
  • Yet more unit tests

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

http://metawidget.org/doc/reference/en/pdf/metawidget.pdf

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