Square Peg vs. Round Requirements: Pushing the boundaries of a tool

One of the things that most web application frameworks do is try to make common activities easy — to ease the annoyance factor of putting together web applications. And, boy howdy, that’s great. But the issue, for me, always occurs when the practices that make the common stuff easy end up making the uncommon stuff really hard. I think I’d even go so far as to suggest that we’re seeing web application development demand more in the way of richer behaviours than have been traditionally supported.

Here’s an example. I was recently working on a feature of an e-Health application. While most HL7 stuff uses a central broker architecture, my system needs to support a point-to-point configuration: Hospital A sends messages directly to Hospital B. Here’s what I needed to set up: different health care organizations are supported on different servers. Hospital A and Hospital B have their data on a server in an Eastern region and Hospital C has data on a server in the Northern region.

When a user in Hospital A wants to send some health data to a user in Hospital C, the system needs to know which server receives the data.

Now, in a perfect world, my application would know nothing about servers. My application would probably write a message to some enterprise service bus, and that bus would be configured to know how to route the message. But my application actually needs to properly construct the message in HL7 format and, unfortunately, part of the message content involves a block that identifies the server (or “device”, in HL7-speak). The message part might look like this:

<receiver typeCode="RCV">
    <telecom value="http://northern.example.com" />
    <device classCode="DEV" determinerCode="INSTANCE">
      <id extension="server1" root="2.16.124.113635.1.3.3" />
      <name>Northern region server</name>
      <agent classCode="AGNT">
        <agentOrganization classCode="ORG" determinerCode="INSTANCE">
          <id extension="HospitalC" root="2.16.124.113635.1.3.2" use="BUS"/>
        </agentOrganization>
      </agent>
    </device>
  </receiver>

Not all of this data is mandatory, but some of it is. So, if I want my application to emit correctly-formatted HL7 messages, then I need to know what data values to put into these fields.

Off I went, then, to build meaningful management pages in my application so that the data can be provided. The key features appear as an admin user is configuring a health care organization. For each organization, I need to specify the server details. The data might start out looking like this:

To provide functionality for assigning a server/device, so I implement an “edit” button, which changes the UI to look like this:

I’m pretty sure that the typical shape of the data involves multiple organizations sharing the same server, so I decide that I want to let the user pick which device to associate with the organization in a drop-down menu. That seems like a simple design to me. And this is the element that I’m most interested in talking about: how to implement the drop-down mechanism.

I’m using JSF, but most web frameworks have similar concepts in their implementation, so I’m not suggesting that JSF has done something that other frameworks can’t. I’m not even suggesting that JSF has the best implementation here; I prefer JSF to Tapestry, but this is actually one scenario where I found that Tapestry’s constructs were a bit nicer.

Drop-down lists of Strings or enum values are trivially easy, so they’re barely worth talking about. For this scenario, my list of devices exists in the database, and I use Hibernate to manage the persistence. So I’m fundamentally interested in rendering a list of Hibernate entities in a drop-down list, like this:

In JSF, there’s a basic tag for rendering a drop-down list. The syntax looks like this:

<h:selectOneMenu id="device" value="#{OrganizationController.device}">
  <f:selectItems value="#{OrganizationController.availableDevices}"/>
</h:selectOneMenu>

In this example, I have a corresponding Java class — a “controller” or “backing bean” — that JSF works with to implement the functionality. It happens that there are three methods on the controller that are interesting: the getter and setter for the “device” property, and the getAvailableDevices()method to get a list of options to select.

public Device getDevice() {
  return this.device;
}
public void setDevice(Device device) {
  this.device = device;
}

public List<SelectItem> getAvailableDevices() {
  ...
}
...

Now JSF really wants things that are intended to be put into drop-down lists to be SelectItems — a special JSF type. Either I can make my Hibernate entity extend the SelectItem type, or I can wrap my Hibernate entity.

I tend to do the latter, to keep some separation between my database classes and my presentation-tier artifacts. I’m fussy that way. So a simple version of the method might look like this:

public List<SelectItem> getAvailableDevices() {
  List<SelectItem> selectList = new ArrayList<SelectItem>();
  selectList.add(new SelectItem(null, ""));
  for (Device device : getOrganizationService().findAllDevices()) {
    selectList.add(new SelectItem(device, device.getName()));
  }
  return selectList;
}

There are a few different ways that I could write that method, but this seems simple enough.

Here, I’m providing a “blank” entry, so that when the user is first presented with the drop-down list, no specific item is pre-selected. That’s just a style I prefer. I’m using a specific constructor for SelectItem that takes two parameters — the actual “thing” I’m choosing, and the text label that should appear in the drop-down list. If my class implemented a useful toString()method, I wouldn’t need to explicitly provide a label.

Now, I suppose what I really want to be sent to the browser is something that looks like this:

<select name="...">
  <option value=""></option>
  <option value="1">Eastern region server</option>
  <option value="2">Northern region server</option>
  ...
</select>

I mostly don’t care what the values are, but I do care that they’re distinct and that they translate back-and-forth to Devices easily. Like I said, if I were dealing with simple data types, JSF figures out how to translate things easily. But since I’m dealing with a custom data type, I need to create something called a Converter and register that with the JSP application. Any time JSF needs to know how to convert to and from Devices, it calls my registered converter.

Here’s a simple Converter implementation:

public class DeviceConverter implements Converter {

  public Object getAsObject(FacesContext context, UIComponent component, String value) throws ConverterException {
    ...
  }

  public String getAsString(FacesContext context, UIComponent component, Object value) throws ConverterException {
    return value == null ? null : String.valueOf(((Device) value).getId());
  }
}

Here I’m using the Hibernate-generated database id to give me a unique id. This is all pretty vanilla-flavoured JSF. Arguably, I didn’t need to fuss with Converters — I could have made my controller expose ids, and handle all the conversion to and from ids internal to my controller. And, hey, that’s how my great grand-pappy used to build web applications after the Great Depression. But there are advantages — like in testing — if I can separate the “fuss with ids” logic from the “implement UI behaviour” logic.

Now, all of that is no big whoop. The part that interests me is what I did next.

I was interested in asking the question: how do new devices get created? What if I needed to set up a new organization on a brand new device that didn’t exist before?

A simple answer is that there’s another part of the application that lists all of the devices, and gives people the option to add devices, there. If they want to configure an organization on a new device, they need to go to another part of the application, first. For my part, I don’t think that’s a great UI design. Convincing a user to go somewhere else is never an easy thing. And, besides, I think that the user will probably want to

What I really want is something like this:

If the user chooses “New server…”, then I want to refresh part of the page to show some extra fields.

Personally, I think that this is a much more graceful UI. Using the magic of Ajax, you can re-render UI portions relatively easily. And using the JSF component, RichFaces, this is really easy. Essentially, my tag changes like this:

<h:selectOneMenu id="device" value="#{OrganizationController.device}">
  <f:selectItems value="#{OrganizationController.availableDevices}"/>
  <a4j:support event="onchange" reRender="mainPanel" />
</h:selectOneMenu>

That one simple tag addition automatically invokes some Ajax-fu to re-render the page with the new fields suddenly visible. I never fuss about with JavaScript or anything like that.

But here’s the thing. Now my drop-down list seems to contain two different “things”: it contains a number of options that represent existing devices, and an option that represents and instruction to create a new thing.

So how does that change the code constructs behind the scenes? I suppose I could have three states: my getDevice()method could produce:

  1. null, indicating that the user hasn’t selected anything;
  2. an existing Device; or
  3. an unpersisted Device, indicating that the user selected “New server…”.

And I have a problem with the Converter — what do I choose as the valuekeys? I can come up with some convention to represent the three states, I suppose.

That works, but I don’t feel too clean about it.

Eventually, I stopped selecting Device instances, and put in a “DeviceSelection” class that more strictly modelled the different choices the user could make. That made my code a bit more expressive, but JSF fought me a bit of the way. I started getting this “Validation Error: Value is not valid” error that sent me looking things up in Google. And I’m pretty good with JSF, so I got past that problem relatively quickly. But I could easily see that kind of situation ending in tears.

Fundamentally, I’m making this assertion: the essence of most web frameworks is that they should make the easy things easy. My concern is, I don’t think many web apps want to do easy things any more. I think good, modern apps are much more expressive than the ones my great grand-pappy used to make.

So my question is: how do you decide to compromise on your UI because the ideal UI demands too much fighting with the web framework?

It's only fair to share...
Share on FacebookGoogle+Tweet about this on TwitterShare on LinkedIn

Leave a Reply