Wednesday, February 2, 2011

Metawidget: Defining Business Objects Using Maps

I was recently asked whether Metawidget could inspect, load and save business objects based not on a JavaBean (or GroovyBean, ScalaBean etc) but on a Map of names and attributes. The answer is yes!

I've sort of covered this before but last time I only blogged about inspecting the Map, not loading and saving the values.We already have a GWT version of this in the Metawidget distribution (see the GWT Client Side example) but now I've put together a Swing project you can download too. It's very similar to the previous blog but adds a MapWidgetProcessor that loads and saves values back into a Map.

An important point to grok is that the 'Map of properties' (used by MapInspector) is different to the 'Map of values' (used by MapWidgetProcessor). This is in the same way that a 'Class' is different to an 'Object': one defines the type, one defines the instance values. So in the code you will see:

metawidget.setToInspect( values );
metawidget.setPath( "person" );

We call both setToInspect (to tell Metawidget where the values are coming from) and setPath (to tell Metawidget what the type is). Normally when using a JavaBean (or GroovyBean, ScalaBean etc) you don't need to do this, because Metawidget will internally call...

metawidget.setPath( metawidget.getToInspect().getClass() )

...on your behalf. But if you want to start using Maps, these two concepts need to be explictly separate.

Take a look for yourself here. Or, for the impatient, here's the most important piece of code:

/**
* MapWidgetProcessor uses the Metawidget's <code>toInspect</code> to retrieve/store values.
*/

public class MapWidgetProcessor
   implements AdvancedWidgetProcessor<JComponent, SwingMetawidget> {

   //
   // Public methods
   //

   @Override
   public void onStartBuild( SwingMetawidget metawidget ) {

      getWrittenComponents( metawidget ).clear();
   }

   /**
    * Retrieve the values from the Map and put them in the Components.
    */


   @Override
   public JComponent processWidget( JComponent component, String elementName, Map<String, String> attributes, SwingMetawidget metawidget ) {

      String attributeName = attributes.get( NAME );
      getWrittenComponents( metawidget ).put( attributeName, component );

      // Fetch the value...

      Map<String, Object> toInspect = metawidget.getToInspect();
      Object value = toInspect.get( attributeName );

      if ( value == null ) {
         return component;
      }

      // ...and apply it to the component. For simplicity, we won't worry about converters

      String componentProperty = metawidget.getValueProperty( component );
      ClassUtils.setProperty( component, componentProperty, value );

      return component;
   }

   @Override
   public void onEndBuild( SwingMetawidget metawidget ) {

      // Do nothing
   }

   /**
    * Store the values from the Components back into the Map.
    */


   public void save( SwingMetawidget metawidget ) {

      Map<String, Object> toInspect = metawidget.getToInspect();

      for ( Map.Entry<String,JComponent> entry : getWrittenComponents( metawidget ).entrySet() ) {

         JComponent component = entry.getValue();
         String componentProperty = metawidget.getValueProperty( component );
         Object value = ClassUtils.getProperty( component, componentProperty );

         toInspect.put( entry.getKey(), value );
      }
   }

   //
   // Private methods
   //

   /**
    * During load-time we keep track of all the components. At save-time we write them all back
    * again.
    */


   private Map<String,JComponent> getWrittenComponents( SwingMetawidget metawidget ) {

      @SuppressWarnings( "unchecked" )
      Map<String,JComponent> writtenComponents = (Map<String,JComponent>) metawidget.getClientProperty( MapWidgetProcessor.class );

      if ( writtenComponents == null ) {
         writtenComponents = CollectionUtils.newHashMap();
         metawidget.putClientProperty( MapWidgetProcessor.class, writtenComponents );
      }

      return writtenComponents;
   }
}

Friday, January 28, 2011

Metawidget: PrimeFaces support

The guys over at Activiti recently expressed an interest in integrating Metawidget into their BPM product. They had a few concerns, most notably PrimeFaces support, so I thought I'd have a go at implementing it.

I'm delighted to say that, thanks to PrimeFaces' solid build quality and outstanding User Guide (over 400 detailed pages!) integration went very smoothly. There were a few minor glitches related to Mojarra which I will be working with that team to get resolved. But MyFaces works great.

I have checked the code into SVN ready for the Metawidget v1.10 release. I have also put together a sample project demonstrating PrimeFaces support which you can download here.

Thursday, January 27, 2011

HttpClient 4: Want To Make It 250x Faster?

I just spent a few hours debugging a performance problem with my app. It came down to a default setting inside Apache HttpClient 4.0.3.

Don't get me wrong, HttpClient is an awesome piece of work and a fantastic contribution to the community - I use it in lots of my projects and it's very useful. So my thanks to the HttpClient team for all their hard work.

However, by default HttpClient 4.0.3 adds a...

Expect: 100-continue

...header to every POST request. This appears to interact badly with Tomcat's (and JBoss', and possibly other containers) FormAuthenticator. Specifically somewhere around...

request.getParameter( Constants.FORM_USERNAME )

...FormAuthenticator disappears into a hole (during Request.parseParameters) for some 2 seconds before emerging with the parameters. It appears to be 'lazy loading' the request body during this time? According to the HTTP spec the header "allows a client that is sending a request message with a request body to determine if the origin server is willing to accept the request before the client sends the request body".

If you're using HttpClient to log in to your Java EE server (say, for black box testing) this can be a significant performance hit. Removing the Expect header logs you in in about 8ms, some 250 times faster.

You can remove the Expect header by doing:

client.removeRequestInterceptorByClass( RequestExpectContinue.class );

I've put together a sample WAR to reproduce the problem, along with deployment instructions, under this issue here.

Update: it appears this is actually a bug in Tomcat 6. Now tracking under this issue here.

Wednesday, January 19, 2011

Metawidget: Dynamic Layouts

I was recently asked whether Metawidget can change layouts dynamically, say from a tab-based layout to a panel-based layout?

The answer is yes! You can call...

metawidget.setLayout( myLayout );

...at any time to dynamically update the layout. The questioner was asking in relation to JSF, and the hardest bit in JSF is actually getting programmatic access to the Metawidget. This requires the binding attribute I've blogged about previously.

But once past that, you can put together something like this:

Note the buttons at the buttom that allow you to select which layout to use. I have put together the complete project you can download from here.

Monday, January 3, 2011

Grokking Seam Forge: Part 1

JBoss have just released Alpha 1 of Seam Forge, their framework for rapid-application development that includes Metawidget support.

You can read more about the release and how to use it here. After that, for those wanting to dive into the code (either out of curiosity or to start contributing) here's a step-by-step guide that may save some trial and error:

Install JBoss Tools

  • Download Eclipse 3.5 SR2 (just the 'Eclipse IDE for Java Developers' version)

  • Download JBoss Tools 3.1.GA

  • Unzip Eclipse, and run eclipse.exe

  • Select a workspace (I'd recommend starting a new one)

  • Use Help > Install New Software. Click Add then Archive (see screenshot):
  • Click OK

  • Check All JBoss Tools - 3.1.1. Click Next and Next again

  • Choose I accept the terms of the license agreements and click Finish
    (NB. there seems to be a bug here: the Finish button will not always be enabled? I found clicking back to the start of the wizard fixed it)

  • Wait for the install to complete. Accept any signing certificates. Restart the IDE
Install EGit

  • Use Help > Install New Software and click Add

  • Enter a Location of http://download.eclipse.org/egit/updates (see screenshot):
  • Check Eclipse Git Team Provider. Click Next

  • Click Finish

  • Wait for the install to complete. Accept any signing certificates. Restart the IDE
Clone the Seam Forge Repository

  • Use File > Import and choose Projects from Git (see screenshot):
  • Click Clone... and enter a URI of git://github.com/seam/forge.git (see screenshot):
  • Click Next, choose the Master branch, click Next (see screenshot):
  • Click Finish. Wait for the Git repository to clone (may take a while). See screenshot:
  • Click Cancel (not Next!)
Install Maven

  • Use Help > Install New Software

  • Choose to Work with the 'Maven Integration for Eclipse Update Site'

  • Check the 'Maven Integration for Eclipse' box (see screenshot):
  • Click Next, Next, accept the license terms and Finish

  • Wait for the install to complete. Accept any signing certificates. Close the IDE (don't restart it)

  • Edit eclipse/eclipse.ini and add the lines in bold:
    --launcher.XXMaxPermSize
    256m
    -vm
    <path to your JDK>\bin\javaw.exe

    -vmargs
    -Dosgi.requiredJavaVersion=1.5

    (Maven needs Eclipse running under a JDK, not a JRE)

  • Relaunch the IDE
Import Maven Projects

  • Use File > Import. Choose Maven > Existing Maven Projects (see screenshot):
  • Browse to the location of the Git clone (see screenshot):
  • Click Finish. Wait for import of Maven projects (may take a while)
All Seam Forge sub-projects should now be listed under the Package Explorer. There should be no compile errors under the Problems tab, though there may be warnings (as this is still an alpha release). See screenshot:

Tuesday, December 28, 2010

We're Hiring Again!

Here's the job ad we're posting. Following a successful first hire earlier this year, and increased investment from a leading Australian financial institution, we're hiring again. A chance to work from home, and work for me! What a dream job :)


Senior Java EE 5 (J2EE) Developer: $120,000 AUD + super
  • Small, focused team

  • Modern, well-crafted software

  • Work in city or from home
A rare opportunity to join our small consultancy team. We have recently received substantial investment from a leading Australian financial institution and we must grow to meet demand.

You will work side-by-side with one of Australia's premier Java EE developers, and be intimately involved in all design decisions.

You will be a pragmatic programmer. Software development will be in your blood: a natural instinct. You must be passionate about Open Source and Java EE. You will demonstrate advanced analytical skills; an eye for elegance and craftmanship; a passion for refactoring, unit testing, and delivering secure and robust code.

Your skills will include many of the following:
  • JBoss

  • JSF/RichFaces

  • XSL (T and FO) and XPath

  • EJB 3/JMS

  • JPA/SQL

  • Metawidget

  • HtmlUnit

  • HTML/CSS
In addition, you will have excellent communication skills and be comfortable interacting with stakeholders at all levels.

Work from home will be offered providing that you can demonstrate an appropriate working environment. You must be a citizen or permanent resident of Australia.

To help us sort applicants, include your CV and a 1 page overview highlighting your suitability for the role. Links to examples of your work (eg. web sites you have built, blogs you have written, Open Source contributions etc) will be highly regarded.

Please send your application to jobs@kennardconsulting.com.

Friday, December 17, 2010

Retrofitting a UI: GatewayWarDeployment example

Adam Bien recently asked me to create a Metawidget example for his Java EE Patterns and Best Practices Kenai Repository, based on his Real World Java EE Patterns book.

In order to demonstrate the changes typically required to incorporate Metawidget into a project (and the code savings it typically brings) I retrofitted one of Adam's original projects, his GatewayWarDeployment example. I've called mine GatewayWarDeploymentWithMetawidget.

To view the new example:
  1. Ensure you have Metawidget installed at its default location (/metawidget-1.05/metawidget.jar)

  2. Get sources for the Java EE Patterns Kenai project in NetBeans using Team > Team Server > Get Sources... (see this screencast for detailed instructions)
  3. Open the GatewayWarDeploymentWithMetawidget project
The example works just as before, but two files have been changed. The first is the managed bean, LoadView.java, which now includes Metawidget annotations and is therefore longer. The second is the UI page, createorder.xhtml, which now uses a <m:metawidget> tag and is therefore shorter:

LoadView.java
createorder.xhtml

In such a small example there is no overall saving in lines of code (larger examples have more dramatic savings). But there is still a significant reduction in duplicated declarations (field names, types, action names etc) and therefore reduced possibility of error when building (and maintaining) the UI. Note Metawidget is handling both the input boxes and the buttons, including enabling/disabling the buttons in line with the business logic. This is declared by the code:

@UiAction
@UiFacesAttribute( name = "read-only", expression = "#{load.emptyLoad}" )
public void dropLightest(){
   ...
}

Of course, this is not a great demonstration of all the features of Metawidget. More extensive examples can be found under the /examples folder of the Metawidget distribution. But hopefully it's enough to get people excited without confusing them with lots of code.

My thanks to Adam for his help and encouragement in developing this example!