Tuesday, June 24, 2008

Metawidget and Tooltips

Update: the APIs shown in this blog entry have changed slightly in newer releases of Metawidget. Specifically you should now use a WidgetProcessor to call .setToolTipText. Please download the latest documentation from http://metawidget.org

I was recently asked whether Metawidget could 'add an annotation for field/methods to describe a field in the UI with a tooltip'. Here's how to add that:

Step 1: Write the Annotation

You can basically copy @UiLabel:

package com.myapp;

import java.lang.annotation.*;

@Retention( RetentionPolicy.RUNTIME )
@Target( { ElementType.FIELD, ElementType.METHOD } )
public @interface UiDescription {
   String value();
}

Step 2: Write an Inspector for the Annotation

Most of the work is done for you by BasePropertyInspector:

package com.myapp;

import java.util.*;
import org.metawidget.inspector.impl.*;
import org.metawidget.util.*;

public class DescriptionInspector extends BasePropertyInspector {
   public DescriptionInspector() {
      this( new BasePropertyInspectorConfig() );
   }

   public DescriptionInspector( BasePropertyInspectorConfig config ) {
      super( config );
   }

   @Override
   protected Map<String, String> inspect( Property property, Object toInspect )
      throws Exception {
      Map<String, String> attributes = CollectionUtils.newHashMap();
      UiDescription description = property.getAnnotation( UiDescription.class );

      if ( description != null )
         attributes.put( "description", description.value() );


      return attributes;
   }
}

Step 3: Extend the Metawidget to understand the Inspector

For this example, I'll extend SwingMetawidget:

package com.myapp;

import java.util.Map;
import javax.swing.JComponent;
import org.metawidget.swing.SwingMetawidget;

public class DescriptiveSwingMetawidget extends SwingMetawidget {
   @Override
   protected JComponent buildActiveWidget( Map attributes )
      throws Exception {
      JComponent component = super.buildActiveWidget( attributes );

      if ( component == null )
         return null;

      component.setToolTipText( attributes.get( "description" ));
      return component;
   }
}

Step 4: Annotate the Business Object

We're done! To test it, first annotate the business object. We'll reuse the business object from the Metawidget tutorial:

package com.myapp;

public class Person {
   @UiDescription( "The name of the person" )
   public String name;

   @UiDescription( "The age of the person" )
   public int age;

   @UiDescription( "Whether the person is retired" )
   public boolean retired;
}

Step 5: Use the Business Object

Now use the business object. Again, we'll reuse the Main class from the Metawidget tutorial:

package com.myapp;

import javax.swing.JFrame;

import org.metawidget.inspector.composite.*;
import org.metawidget.inspector.propertytype.*;

public class Main {
   public static void main( String[] args ) {
      DescriptiveSwingMetawidget metawidget = new DescriptiveSwingMetawidget();
      CompositeInspectorConfig config = new CompositeInspectorConfig();
      config.setInspectors( new PropertyTypeInspector(), new DescriptionInspector() );
      metawidget.setInspector( new CompositeInspector( config ) );
      metawidget.setToInspect( new Person() );

      JFrame frame = new JFrame();
      frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      frame.getContentPane().add( metawidget );
      frame.setSize( 400, 210 );
      frame.setVisible( true );
   }
}

Run it! You'll see the usual Swing tutorial app, but this time with tooltips on all the fields.

Of course, this simple example has its problems. Most notably, you'd probably want some localization on the descriptions. Both @UiLabel and @UiSection do this, but that's for another blog!

Sunday, June 22, 2008

GWT and Metawidget Part 4: Over to you

A recent Metawidget bug report has highlighted I need better documentation around how to build your own GWT app. I will be putting this in the next release, but for now please note:
  • when building your app, you need both metawidget.jar and metawidget-gwt-client.jar in your compiler CLASSPATH
  • when deploying your app, you need metawidget.jar in your WEB-INF/lib and a reference to GwtRemoteInspectorImpl in your web.xml
  • you can see this in action in addressbook-gwt.war in the binary distribution
  • you may also find the example-gwt-addressbook Ant task in the source distribution's build.xml useful

Thanks to the anonymous bug reporter for highlighting this lack of documentation!

Friday, June 20, 2008

Metawidget and Resource Bundles

I was recently asked if I could 'give an example of how to get translated labels (from a resource bundle) in the JSF version' of Metawidget.

You can see a working example of this in the addressbook-faces.war included in the download (with tutorial here), but in this blog entry I'll focus specifically on translated labels.

Metawidget generates labels for widgets based on...
  • the uncamel-cased version of the property name (eg. 'Date of birth' for 'dateOfBirth')

  • the exact text specified in the @UiLabel annotation (or label XML attribute)

...if a resource bundle is specified, it also tries...
  • a bundle key based on the camel-cased version of the text specified in @UiLabel or label (eg. 'dateOfBirth' for 'Date of birth')

  • a bundle key based on the property name

That's a lot of combinations, I know! But the idea is you can develop your app first without a bundle, and Metawidget will use sensible defaults. Then you can add a bundle later and Metawidget will automatically adapt to using it.

To add a bundle, you can either add one globally in faces-config.xml...

<application>
   <message-bundle>com.myapp.Resources</message-bundle>
</application>

...or on the page...

<f:loadBundle basename="com.myapp.Resources" var="bundle"/>
   ...
<m:metawidget value="#{myBusinessObject}" bundle="#{bundle}"/>

...and that's it! The other supported Metawidgets (for Android, GWT, JSP, Spring, Struts, Swing, etc.) all do localization in a similar fashion, following the conventions of their particular platform.

Thursday, June 19, 2008

Metawidget and Name/Value Pairs

I was recently asked whether Metawidget 'covered the scenario where my object contains a list of properties (name/value pairs) stored in a collection'?

The answer is yes! Metawidget can read properties from almost any source. You don't need a physical Class. Metawidget can even combine properties from multiple sources (using CompositeInspector). With respect to name/value pairs in a collection:

Step 1: Write an Inspector

Write an Inspector suited to your particular architecture. Here's an example:

package com.myapp;

import static org.metawidget.inspector.InspectionResultConstants.*;
import java.util.Map;
import org.metawidget.inspector.iface.*;
import org.metawidget.util.*;
import org.w3c.dom.*;

public class MapInspector implements Inspector {
   private Map<String, Map<String, Class<?>>> mObjectProperties = CollectionUtils.newHashMap();

   public MapInspector() {
      // Some dummy data
      Map<String, Class<?>> personProperties = CollectionUtils.newLinkedHashMap();
      personProperties.put( "name", String.class );
      personProperties.put( "age", int.class );
      personProperties.put( "retired", boolean.class );

      mObjectProperties.put( "person", personProperties );
   }

   public String inspect( Object toInspect, String type, String... names )
      throws InspectorException {
      Map<String, Class<?>> properties = mObjectProperties.get( type );

      if ( properties == null )
         return null;

      try {
         Document document = XmlUtils.newDocument();
         Element elementRoot = document.createElementNS( NAMESPACE, ROOT );
         document.appendChild( elementRoot );

         Element elementEntity = document.createElementNS( NAMESPACE, ENTITY );
         elementEntity.setAttribute( TYPE, type );
         elementRoot.appendChild( elementEntity );

         for ( Map.Entry<String, Class<?>> entry : properties.entrySet() ) {
            Element element = document.createElementNS( NAMESPACE, PROPERTY );
            element.setAttribute( NAME, entry.getKey() );
            element.setAttribute( TYPE, entry.getValue().getName() );
            elementEntity.appendChild( element );
         }

         return XmlUtils.documentToString( document, false );
      }
      catch ( Exception e ) {
         throw InspectorException.newException( e );
      }
   }
}

Step 2: Use the Inspector

You can try MapInspector by updating the code from the Metawidget tutorial:

package com.myapp;

import javax.swing.*;
import org.metawidget.swing.SwingMetawidget;

public class Main {

   public static void main( String[] args ) {
      SwingMetawidget metawidget = new SwingMetawidget();
      metawidget.setInspector( new MapInspector() );
      metawidget.setPath( "person" );


      JFrame frame = new JFrame( "Metawidget Tutorial" );
      frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
      frame.getContentPane().add( metawidget );
      frame.setSize( 400, 210 );
      frame.setVisible( true );
   }
}

Running this will give you a UI driven by name/value pairs in a collection. And because Metawidget decouples its Inspectors from its Metawidgets, it can be equally applied to Spring, Struts, JSF, JSP, GWT, Android etc. for free!

Step 3: Change your Inspector

Of course, your particular take on name/value pairs may be different.

For example, you may not be storing the class in the 'value', you might be storing an Object instead. In that case you'll need to either use Object.getClass() as the type, or just return no type (in which case Metawidget will choose a textbox widget). Equally, you may be storing more than just the value - you may be storing whether the field is required, its maximum length etc. You'll need to add these as extra attributes to the XML elements you create.

Finally, you may want to think about how you're going from a Map to a piece of XML, and whether you can optimize that process. For example, where is the Map coming from? Wherever it is, can you have the Inspector get it from there directly?

Wednesday, June 18, 2008

Metawidget and SwingX

Update: the APIs shown in this blog entry have changed slightly in newer releases of Metawidget. Specifically you should now use a WidgetBuilder to add support for third-party components. Please download the latest documentation from http://metawidget.org

There's been a bit of interest in whether Metawidget can use SwingX 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 SwingXMetawidget

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

package com.myapp.metawidget.swingx;

import static org.metawidget.inspector.InspectionResultConstants.*;
import java.awt.*;
import javax.swing.JComponent;
import org.jdesktop.swingx.JXDatePicker;
import org.metawidget.util.ClassUtils;

public class SwingXMetawidget 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 SwingX JXDatePicker
            if ( Date.class.isAssignableFrom( clazz ) )
                  return new JXDatePicker();

      }

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

   protected String getValueProperty( Component component ) {
      if ( component instanceof JXDatePicker )
         return "date";


      return super.getValueProperty( component );
   }
}

That's it! You'll get all the other Metawidget benefits (like layouts, internationalization, etc.) for free. Note that overriding getValueProperty is optional, but necessary if you want automatic two-way data binding between the JXDatePicker and your business objects.

Step 2: Use SwingXMetawidget 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 SwingXMetawidget();

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

Tuesday, June 17, 2008

GWT and Metawidget Part 3: Binding

Update: the APIs shown in this blog entry have changed slightly in newer releases of Metawidget. Specifically bindings are now set using .addWidgetProcessor. Please download the latest documentation from http://metawidget.org

The latest release of Metawidget adds supports for Google Web Toolkit (GWT) 1.5. This series of blogs explores the challenges that had to be overcome to acheive this.

Two Way Binding

Almost all Web frameworks support some form of automatic two-way binding between Web pages and domain objects. Some supply sophisticated expression languages (Java Server Faces), others straight property name mapping (Struts), but most supply something.

Except GWT.

To be fair, Google are planning to add this feature in an upcoming release - but it's not there in 1.5 just yet. One of the main stumbling blocks is that most binding implementations use reflection, and JavaScript doesn't support reflection.

As an interim solution, Metawidget supplies SimpleBinding. Metawidget's binding implementations are pluggable, so we can swap in 'real' GWT binding one day, but for now SimpleBinding gets you most of the way: a simple two-way binding, including converters.

But! How do we solve the reflection problem?

As with the last blog entry, Generators are our friend. SimpleBinding uses SimpleBindingAdapters, and comes with a SimpleBindingAdapterGenerator that can generate SimpleBindingAdapters automatically for your business classes.

A SimpleBindingAdapter is capable of 'reflecting' every property (and even sub-property) of a business class by pre-generating code that automatically traverses getters and calls setters. In effect, it statically generates codes that looks like this:

if ( "address".equals( property[0] )) {
   Address address = contact.getAddress();
   if ( property.length == 1 ) return address;

   // Address properties

   if ( "street".equals( property[1] )) {
      if ( property.length > 2 )
         throw new RuntimeException( "Cannot reflect into property 'street." + property[2] + "'" );
      return address.getStreet();
   }

To stop the tree of possible properties getting too large, it restricts itself to only classes within the same package or sub-package.

To use SimpleBinding in your code, add the following to your application-name.gwt.xml file:

<generate-with class="org.metawidget.gwt.generator.binding.simple.SimpleBindingAdapterGenerator">
   <when-type-assignable class="org.metawidget.example.shared.addressbook.model.Contact"/>
</generate-with>

...and in your application code add...

metawidget.setBindingClass( SimpleBinding.class );
SimpleBindingAdapter<Contact> adapter = (SimpleBindingAdapter<Contact>) GWT.create( Contact.class );
SimpleBinding.registerAdapter( Contact.class, adapter );

How does this work in practice? See for yourself! The Metawidget download includes a pre-built sample application, addressbook-gwt.war, that showcases a GwtMetawidget using two-way bindings in the UI. It's covered in detail in the reference guide.

GWT and Metawidget Part 2: Pluggability

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

The latest release of Metawidget adds supports for Google Web Toolkit (GWT) 1.5. This series of blogs explores the challenges that had to be overcome to acheive this.

Pluggability

Pluggability is everywhere in Metawidget: pluggable inspectors, pluggable layout managers, pluggable binding implementations, etc. etc. The usual way this is acheived is through code like...

metawidget.setLayoutClass( TableLayout.class );

...which, somewhere behind the scenes, ends up calling...

layoutClass.newInstance();

GWT, however, is a very different kind of Web framework: it compiles Java to JavaScript, and JavaScript doesn't support newInstance.

But! Using GwtMetawidget, you can plug in different implementations by calling setLayout (and also setBinding, setInspector, etc.)! How is this possible?

GWT supplies a powerful concept called Generators. With a bit of work, you can use Generators to do all sorts of things, including scanning a project's available classes and generating fragments of code that be inserted at runtime.

For example, you can scan all the classes that implement Layout, and then generate a function that says:

if ( givenClass.equals( TableLayout.class )) return new TableLayout();

What's really great is you can push all this into the framework (in our case, Metawidget) so that it becomes 'magic' to the application code.

How does this work in practice? See for yourself! The Metawidget download includes a pre-built sample application, addressbook-gwt.war, that showcases a GwtMetawidget using pluggable bindings at runtime to generate the UI. It's covered in detail in the reference guide.

GWT and Metawidget Part 1: Reflection and Inspecting Annotations

The latest release of Metawidget adds supports for Google Web Toolkit (GWT) 1.5. This series of blogs explores the challenges that had to be overcome to acheive this.

Reflection and Inspecting Annotations

Whilst Metawidget does not require reflection and annotations (eg. you can just use XmlInspector), it's certainly a lot more powerful with them. By using reflection and inspecting the existing annotations on your business objects (you know, the JPA ones, the Hibernate Validator ones, etc.), Metawidget can automatically generate accurate UIs.

However, GWT is a very different kind of Web framework because it pre-compiles Java code to JavaScript. Java and JavaScript have similarities, but two areas they differ greatly is that JavaScript has no support for reflection or annotations.

But! Using GwtMetawidget, you can take your business object, reflect and inspect it, and generate your UI! How is this possible?

Well, GwtMetawidget showcases the power of Metawidget's separate Inspector/renderer architecture. Metawidget comes with a range of different inspectors targeting different aspects of back-end architectures (JPA annotations, Hibernate Validator annotations, JavaBean getters/setters etc). Each Inspector returns a piece of XML, and you can use CompositeInspector to compile them all together into one.

Because of this approach, inspection can be performed anywhere and the result passed somewhere else for rendering:

The process becomes:

  • instantiate the business object on the client-side as normal (ie. as JavaScript)
  • give your business object to GwtMetawidget (a client-side, JavaScript GWT Widget)
  • GwtMetawidget uses AJAX to pass the business object to the server
  • the server, using Java, runs all the Inspectors (including reflection and annotations)
  • the server returns the inspection results as an XML document
  • GwtMetawidget uses JavaScript to render the HTML widgets

How does this work in practice? See for yourself! The Metawidget download includes a pre-built sample application, addressbook-gwt.war, that showcases a GwtMetawidget using reflection and inspecting annotations at runtime to generate the UI. It's covered in detail in the reference guide.

Monday, June 16, 2008

Metawidget gets Groovy

Update: the APIs shown in this blog entry have changed slightly in newer releases of Metawidget. Specifically <propertystyle> now takes a PropertyStyle instance, not a class name. Please download the latest documentation from http://metawidget.org

The latest release of Metawidget adds supports for Groovy. This means Metawidget's Inspectors can now inspect GroovyBean properties for types and annotations.

GroovyBean properties are similar to JavaBean's getters/setters. Indeed, at compile time, GroovyBean properties try to 'look' like JavaBean properties by automatically generating a private member variable and a public getter/setter.

So, if your Groovy code says this...

class Booking {
   @NotNull
   String creditCard;
}

...at compile-time Groovy generates this:

class Booking {
   @NotNull
   private String mCreditCard;

   public String getCreditCard() {
      return mCreditCard;
   }

   public void setCreditCard(String creditCard) {
      mCreditCard = creditCard;
   }
}

Notice, however, that the annotation stays on the private member variable - it does not get applied to the getter. There are good reasons for this, but it means Metawidget's native JavaBeanPropertyStyle cannot be used to find those annotations.

So, instead, Metawidget now has a pluggable GroovyPropertyStyle that understands Groovy properties natively. To use it, you just need to update your inspector-config.xml...

<propertytypeinspector xmlns="java:org.metawidget.inspector.propertytype"
   config="org.metawidget.inspector.impl.BasePropertyInspectorConfig">
   <propertystyle>org.metawidget.inspector.impl.propertystyle.groovy.GroovyPropertyStyle</propertystyle>
</propertytypeinspector>

How does this work in practice? See for yourself! The Metawidget download includes a Seam Groovy Booking sample application, that showcases using Metawidget with Groovy. It's covered in detail in the reference guide.

Declarative User Interface Generator: Metawidget v0.5

Version 0.5 of Metawidget, the declarative user interface generator, is now available. This release includes:

To accomodate this, there have been some minor API changes:

  • JavaBeanInspector is now called PropertyTypeInspector
  • All classes that were called AbstractXXX are now named BaseXXX

All of the examples have been updated to the new API. 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.

Sunday, June 15, 2008

Bulletproof Backups with the ReadyNAS Duo

Update: I wrote up my findings, four years on, in this new blog post!

I just invested in a ReadyNAS Duo, which is a mean-looking little device:


It's small (smaller than a shoebox), quiet (about as loud as a fridge), got terabytes of storage and can run autonomously doing all kinds of things (FTPing files, hosting web sites, downloading BitTorrents, etc.).

It's so flexible infact, there seems to be a lot of confusion on the forums around which strategy to adopt to make best use of it. So I thought I'd write this quick blog on how I use it as a really good backup device.

Step 1: Don't Use It

Well, don't use it as your main hard disk drive (HDD) anyway. It's tempting to try and move all your important files onto it, so that if your PC dies you can just hook up a different PC and off you go. But networks are much, much slower than local HDDs and it just isn't practical. Even e-mail programs run sluggishly if you're serving their EXE over the network.

Instead, get a good mirroring program like MirrorFolder, and have it mirror your HDD to the ReadyNAS frequently (even in real-time, if you like).

Step 2: Rely on it

Expect your PC to fail. Create a separate partition on your local HDD and call it something distant from C: (like N:). Then move all your important programs to it and configure them to be running off N: drive. This can be a tedious process, but once it's done your entire environment is relative to N:, which means as (and when) your PC fails, you can hook up another PC, map drive N: to the ReadyNAS and be up and running again nearly instantly.

Of course, it'll be slow (see Step 1), so ultimately you'll want to copy everything back to the 'mirrored partition' model on that new PC, but if your old PC dies and you need to continue working immediately for, say, the rest of the day it should suffice.

Step 3: Trick the software

Oddly, the ReadyNas Duo's weak spot is its backup software. You can't do weekly backups, or monthly backups, you can't filter out file types etc. etc.

Most suggestions on the forums are to work around this using some 'real' backup software instead, but that's silly because then it ties up your PC again while doing the backup. Instead:
  1. configure MirrorFolder (or equivalent) to only mirror the files you want to backup
  2. configure the ReadyNAS' built-in backup software to copy everything from the mirror into the backup share (this bit doesn't involve your PC, so you can run it overnight)
  3. set up different backup jobs, one for each day of the week, that backs up the mirror to different folders each day. You now have a rolling seven day backup
  4. create another backup job that, on Saturday, takes Sunday's backup and copies it to yet another folder (called, say 'Last Sunday')
  5. create yet another backup job that, on Friday, takes 'Last Sunday's backup and copies it to yet another folder (called, say 'Sunday Before Last'). Keep doing this so that you get monthly backups

Step 4: Trust nothing

Of course, even though your ReadyNAS is a secure backup of your PC, if you lose both at the same time you're still screwed. I'd recommend keeping the ReadyNAS in a separate part of the house to your PC, so that it's not lost in the event of a localized fire. But if the entire house burns to the ground, that's no help. Some of the forum backup strategies say 'buy another NAS' but there's a much nicer way:

The ReadyNAS Duo has 'swappable RAID', whereby you can swap one of the HDDs and the ReadyNAS will resync it. This feature is meant for when one of the HDDs is about to fail.

But you can also use it for off-site backups!

Invest in a third HDD and NAS drive tray (yes, you buy them separately if you hunt about a bit - the ones for the ReadyNAS NV+ are compatible). Then, on a weekly/monthly basis, swap one of the drives. Because you buy a third tray, you can leave the HDD screwed into the tray, so swapping takes very little time.Take the HDD you took out and keep it off-site. Now, even if your house burns down, you're covered!

Update - I've been advised there are problems with this style of swapping:

  1. Apparently both the SATA connectors on the drives and the ones inside the ReadyNAS are quite fragile, and likely to break with repeated swapping.
  2. If the drive is being written to as you swap it, those files might not be written correctly. Best to power the ReadyNAS down before doing the swap.

Instead, you can buy a USB enclosure for your extra HDD and backup over USB. Two things to note with this approach:

  1. If your USB enclosure is anything like mine, you have to unscrew it to put the HDD in. When you do this, you get to see just how flimsy the SATA connectors are!
  2. This is not a good solution if you have a serious amount of data. Re-syncing a 500GB HDD with RAID-X takes about an hour. With USB it'd take days.

Saturday, June 14, 2008

Awesome Sequence Diagram Generator

Just a quick blog about this great tool...

...it makes generating sequence diagrams a breeze, and is completely free! You just type your sequence in as text (no fiddly dragging and dropping and aligning) and it works out what the equivalent sequence diagram should look like. Impressive!
I'll be using this for the next release of Metawidget.