More On "Controller" Classes

Jan 7, 2013 7:39 PM

Tags: xpages mvc java
  1. "Controller" Classes Have Been Helping Me Greatly
  2. More On "Controller" Classes

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.

Commenter Photo

Stephan H. Wissel - Jan 7, 2013 8:56 PM

Nice one,

now you can push that one step further...

When you design your controller to have no dependency on the XPages runtime, just good ol' Notes classes and dependencies get injected if needed...

... then you can add a main() to the controller and run/debug them from the command line. And besides debugging you can run them from jUnit to get full test coverage.

 

Commenter Photo

Ils Niz - Jan 7, 2013 9:53 PM

@Stephen
But you would more than likely want to lean on the XSP runtime for your domino session etc (via FacesContext binding) rather than the traditional POJO `NotesThread.sinitThread()` etc.

Wouldn't this be more efficient as it is using objects already existing in memory, or is domino clever enough to share this sort of stuff between XPages and non-xpagey stuff?

Commenter Photo

Karsten Lehmann - Jan 8, 2013 2:52 AM

My XPages apps look quite similar with a "page handler" class behind every XPage and no SSJS in the XPage, just EL. How do you handle richtext editing in your approach (HTML with embedded images)?

Commenter Photo

Jesse Gallagher - Jan 8, 2013 4:17 AM

@Karsten For these projects, I've been using the standard data sources, so rich text controls are tied directly to xp:dominoDocument objects and work normally. This works well in these cases, since the minor amount of business logic related to opening/saving documents fits fine in the "controllers" without getting too messy, but it does mean that it's not a clean break between the UI and models. It also ties it pretty closely to the XSP environment.

Eventually, I'll give another shot to splitting the "model" portion of the app away from the XPage as well, but for now the tradeoff is worth it to take advantage of all the black magic written into the Domino data sources.

@Stephan For testing, I'd love to have the framework provide a nice, clean way to do that, and then to really encourage you to do so. After source-control, proper automated testing is the biggest area where I (and many programmers) have slacked off. It's a tricky notion to figure out how best to test without also splitting out the code like you suggest, but if you do, you run into the tradeoff of losing a lot of handy functionality in the XSP environment that's not in the core Domino Java API.

Commenter Photo

Gernot Hummer - Mar 6, 2013 1:45 AM

I just stumbled upon this post, great article. I am moving towards the same direction and fully agree on the last part you mention. I haven't found any good way yet to have controllers for custom components in the "right" place. Are there any best practices for this out there?

New Comment