The Bean-Backed Table Design Pattern

  • Jan 22, 2013

First off, I don't like the name of this that I came up with, but it'll have to do.

One of the design problems that comes up all the time in Notes/Domino development is the "arbitrary table" idea. In classic Notes, you could solve this with an embedded view, generated HTML (if you didn't want it to be good), or a fixed-size table with a bunch of hide-whens. With XPages, everything is much more flexible, but there's still the question of the actual implementation.

The route I've been taking lately involves xp:dataTables, Lists of Maps, MIMEBean, and controller classes. In a recent game-related database, I wanted to store a list of components that go into a recipe. There can be an arbitrary number of them (well, technically, 4 in the game, but "arbitrary" to the code) and each has just two fields: an item ID and a count. The simplified data table code looks like this:

<xp:dataTable value="#{pageController.componentInfo}" var="component" indexVar="componentIndex">
	<xp:column styleClass="addRemove">
		<xp:this.facets>
			<xp:div xp:key="footer">
				<xp:button value="+" id="addComponent">
					<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="componentInfo"
						action="#{pageController.addComponent}"/>
				</xp:button>
			</xp:div>
		</xp:this.facets>
						
		<xp:button value="-" id="removeComponent">
			<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="componentInfo"
				action="#{pageController.removeComponent}"/>
		</xp:button>
	</xp:column>
					
	<xp:column styleClass="itemName">
		<xp:this.facets><xp:text xp:key="header" value="Item"/></xp:this.facets>
						
		<xp:inputText value="#{component.ItemID}"/>
	</xp:column>
					
	<xp:column styleClass="count">
		<xp:this.facets><xp:text xp:key="header" value="Count"/></xp:this.facets>
		<xp:inputText id="inputText1" value="#{component.Count}" defaultValue="1">
			<xp:this.converter><xp:convertNumber type="number" integerOnly="true"/></xp:this.converter>
		</xp:inputText>
	</xp:column>
</xp:dataTable>

The two data columns are pretty normal - they could easily point to an array/List in a data context or a view/collection of documents. The specific implementation is that it's an ArrayList stored in the view scope - either created new or deserialized from the document as appropriate:

public void postNewDocument() {
	Map<String, Object> viewScope = ExtLibUtil.getViewScope();
	viewScope.put("ComponentInfo", new ArrayList<Map<String, String>>());
}

public void postOpenDocument() throws Exception {
	Map<String, Object> viewScope = ExtLibUtil.getViewScope();
	Document doc = this.getDoc().getDocument();
	viewScope.put("ComponentInfo", JSFUtil.restoreState(doc, "ComponentInfo"));
}

public List<Map<String, Serializable>> getComponentInfo() {
	return (List<Map<String, Serializable>>)ExtLibUtil.getViewScope().get("ComponentInfo");
}

Those two "addComponent" and "removeComponent" actions are very simple as well: they just fetch the list from the view scope and manipulate it:

public void addComponent() {
	this.getComponentInfo().add(new HashMap<String, Serializable>());
}
public void removeComponent() {
	int index = (Integer)ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(), "componentIndex");
	this.getComponentInfo().remove(index);
}

One key part to notice is that the "componentIndex" variable is indeed the value you'd want. In the context of the clicked button (say, in the second row of the table), the componentIndex variable is set to 1, and the resolver and FacesContext make that work.

On save, I just grab the modified Document object, serialize the object back into it, and save the data source (that's what super.save() from my superclass does):

public String save() throws Exception {
	List<Map<String, Serializable>> componentInfo = this.getComponentInfo();

	Document doc = this.getDoc().getDocument(true);
	JSFUtil.saveState((Serializable)componentInfo, doc, "ComponentInfo");

	return super.save();
}

The end result is that I have a user-expandable/collapsible table that can hold arbitrary fields (since it's a Map - it could just as easily be a list of any other serializable objects):

If I wanted to access the data from non-Java contexts, I could replace the MIMEBean bits with another method, like a text-list-based format or response documents, but the final visual result (and the XSP code) would be unchanged.

More On "Controller" Classes

  • Jan 7, 2013

Since my last post on the matter , I've been using this "controller" class organization method in a couple other projects (including a refresh of the back-end of this blog), and it's proven to be a pretty great way to go about XPages development.

As I mentioned before, the "controller" term comes from the rough equivalent in Rails. Rails is thoroughly MVC based, so a lot of your programming involves creating the UI of a page in HTML with a small sprinkling of Ruby (the "view"), backed by a class that is conceptually tied to it and stores all of the real business logic associated with that specific page (the "controller"). XPages don't have quite this same assumption built in, but the notion of pairing an XPage with a single backing Java class is a solid one. Alternatively, you can think of the "controller" class as being like a stylesheet or client JavaScript file: the HTML page handles the design, and only contains references to functionality defined elsewhere.

What this means in practice is that I've been pushing to eliminate all traces of non-EL bindings in my XSP markup in favor of writing the code in the associated Java class - this includes not only standard page events like beforeRenderResponse , but also anywhere else that Server JavaScript (or Ruby) would normally appear, like value and method bindings. Here's a simple example, from an XPage named Test and its backing class:

Test.xsp
<p><xp:button id="clickMe" value="Click Me">
	<xp:eventHandler event="onclick" submit="true" refreshMode="partial" refreshId="output"
		action="#{pageController.refreshOutput}"/>
</xp:button></p>
	
<p><xp:text id="output" value="#{pageController.output}"/></p>
controller/Test.java
package controller;

import frostillicus.controller.BasicXPageController;
import java.util.Date;

public class Test extends BasicXPageController {
	private static final long serialVersionUID = 1L;

	private String output = "default";
	public String getOutput() { return this.output; }

	public void refreshOutput() {
		this.output = new Date().toString();
	}
}

In a simple case like this, it doesn't buy you much, but imagine a more complicated case, say code to handle post-save document processing and notifications, or complicated code to generate the value for a container control. The separation between the two pays significant dividends when you stop having to worry scrolling through reams of code in the XSP markup when you're working on the page layout, while having a known location for the Java code associated with a page makes it easier to track down functionality. Plus, Server JavaScript is only mildly more expressive than Java, so even the business logic itself stays just about as clean (moving from Ruby to Java, on the other hand, imposes a grotesque LOC penalty).

It sounds strange, but the most important benefit I've derived from this hasn't been from performance or cleanliness (though those are great), but instead the discipline it provides. Now, there's no question where business logic associated with a page should go: in a class that implements XPageController with the same name as the page and put in the "controller" package. If I want pages to share functionality, that's handled either via a common method stored in another class or, as appropriate, via class inheritance. No inline code, no Server JavaScript script libraries. If something is going to be calculated, it's done in Java.

There is one area that's given me a bit of trouble: custom controls. For example, the linksbar on this blog requires computation to generate, but it's not associated with any specific page. For now, I put that in the top-level controller class, but that feels a bit wrong. The same solution also doesn't apply to the code that handles rendering each individual post in the list, which may be on the Home page, the Month page, or individually on the Post page, in which case it also has edit/save/delete actions associated with it. For now, I created a "helper" class that I instantiate (yes, with JavaScript, which is depressing) for each instance. I think the "right" way to do it with the architecture is to create my controls entirely in Java, with renderers and all. That would allow me to handle the properties passed in cleanly, but the code involved with creating those things is ugly as sin. Still, I'll give that a shot later to see if it's worth the tradeoff.