My Latest Programming-Technique Improvements

Tue Apr 23 11:34:58 EDT 2013

Tags: java

Over the past couple weeks, I've been trying out some new things to make my code a bit cleaner and I've been having a good time of it, so I figured I'd write up a quick summary.

Composed Method

First and foremost, I've been trying out the Composed Method of programming. This is not a new idea - indeed, it's a pretty direct application of general refactoring - but the slight perspective change from "factor out reused code" to starting out with clean, small blocks has been a pleasant switch. The gist of the idea is that, rather than writing a giant, LotusScript-agent-style block of code first and then breaking out individual components after the fact, you think in terms of small, discrete operations that you can name. The result is that your primary "control" methods end up very descriptive and easy to follow, consisting primarily of ordered phrases specifically saying what's going on. I encourage you to search around and familiarize yourself with the technique.

import static

Java's import static statement is a method-level analog to the normal import statement: it lets you make static methods of classes available for easy calling without having to specify the surrounding class name. Most of the examples you'll see (like those on the linked docs) focus on the Math class, which makes sense, but it applies extremely well to XPages programming. Pretty much every app should have a JSFUtil class floating around, and most should also use the ExtLibUtil class for its convenience methods like getCurrentSession:

import static com.ibm.xsp.extlib.util.ExtLibUtil.getSessionScope;
import static com.ibm.xsp.extlib.util.ExtLibUtil.getCurrentSession;

// ...

getSessionScope().put("currentUser", getCurrentSession().getEffectiveUserName());

Night and day? Not really, but it's a little more convenient and easier to read.

Now, as the linked docs above indicate, import static comes with a warning to use it cautiously. Part of this is Java's cultural aversion to concision, but it's also a good idea to not go too crazy. When the methods you're importing are clear in intent and unlikely to overlap with local ones (like those in ExtLibUtil), it makes sense, particularly because Designer/Eclipse allows you to hover over the method or hit F3 and see where it's defined.

createDocument

Since creating a document in the current database with a known set of fields is such a common action, I've taken a page from one of our extended methods in org.openntf.domino and have started using a createDocument method that takes arbitrary pairs of keys and value and runs them through replaceItemValue, along with a helper method to allow for storing additional data types (since I'm still using the legacy API for these projects):

protected static Document createDocument(final Object... params) throws Exception {
	Document doc = ((Database)resolveVariable("database")).createDocument();

	for(int i = 0; i < params.length; i += 2) {
		if(params[i] != null) {
			String key = params[i].toString();
			if(!key.isEmpty()) {
				doc.replaceItemValue(key, toDominoFriendly(params[i+1]));
			}
		}
	}

	return doc;
}
protected static Object toDominoFriendly(final Object value) throws Exception {
	if(value == null) {
		return "";
	} else if(value instanceof List) {
		Vector<Object> result = new Vector<Object>(((List<?>)value).size());
		for(Object listObj : (List<?>)value) {
			result.add(toDominoFriendly(listObj));
		}
		return result;
	} else if(value instanceof Date) {
		return ((Session)resolveVariable("session")).createDateTime((Date)value);
	} else if(value instanceof Calendar) {
		return ((Session)resolveVariable("session")).createDateTime((Calendar)value);
	}
	return value;
}

In use, it looks like this:

Document request = createDocument(
		"Form", "Request",
		"Principal", getCurrentSession().getEffectiveUserName(),
		"StartDate", getNewRequestStartDate(),
		"EndDate", getNewRequestEndDate(),
		"Type", getNewRequestType()
);

 

The result of these changes is that I'm writing less code and the intent of every line is much more clear. It also happens to be more fun, which never hurts.

Fun With Old XML Features

Wed Apr 03 01:35:55 EDT 2013

Tags: java xml

One of the side effects of working on the OpenNTF Domino API is that I saw every method in the interfaces, including ones that were either new to me or that I had forgotten about a long time ago. One of these is the "parseXML" method found on Items, RichTextItems, and EmbeddedObjects. This was added back in 5.0.3, I assume for some reason related to the mail template, like everything else added back then. Basically, it takes either the contents of a text item, the text of a rich text item, or the contents of an attached XML document and converts it to an org.w3c.dom.Document object (the usual Java standard for dealing with XML docs).

That's actually kind of cool on its own in some edge cases (along with the accompanying transformXML method), but you can also combine it with ANOTHER little-utilized feature: XPath support in XPages. So say you have an XML document like this attached to a Notes doc (or stored in an Item):

<stuff>
	<thing>
		<foo>bar</foo>
	</thing>
	<thing>
		<foo>baz</foo>
	</thing>
</stuff>

Once you have that, you can write code like this*:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
	<xp:this.data>
		<xp:dominoDocument var="doc" formName="Doc" action="openDocument">
			<xp:this.postOpenDocument><![CDATA[#{javascript:
				requestScope.put("xmlDoc", doc.getDocument().getFirstItem("Attachment").getEmbeddedObjects()[0].parseXML(false))
			}]]></xp:this.postOpenDocument>
		</xp:dominoDocument>
	</xp:this.data>
	
	<xp:repeat value="#{xpath:xmlDoc:stuff/thing}" var="thing">
		<p><xp:text value="#{xpath:thing:foo}"/></p>
	</xp:repeat>

</xp:view>

I don't really have any immediate use to do this kind of thing, but sometimes it's just fun to explore what the platform lets you do. It's one more thing I'll keep in the back of my mind as a potential technique if the right situation arises.

* Never write code like this.

Release M1 of org.openntf.domino

Tue Apr 02 23:33:03 EDT 2013

Yesterday, we released milestone 1 of our improved Domino API. This is our first tagged release meant for proper testing - all of the classes are implemented, many of the banner features are in there, and we've been using it in various real-world situations. I switched a couple of my side projects over - my portfolio site, the code for this blog (though I haven't deployed the template yet), and a couple personal game-related apps. That kind of testing is going to be crucial in getting us to a real ready-for-production release, and it's pretty exciting that we're already this far.

I encourage you to go check out the release on OpenNTF, browse and follow the code on GitHub, and don't hesitate to pitch in. We welcome contributors of all types - outright code contributions, real-world testing, or just feature requests. One of the overarching goals of the API is to solve as many of the little annoyances that we've all dealt with for years, so definitely let us know, either individually or via the Issues section of the GitHub project.

We're hard at work on some of the next steps: more documentation, streamlining the process of upgrading from the legacy to the new API in various scenarios (agents, XPage apps, DOTS, etc.), and all of the issues slated for M2. Watch that space!