One of the things we love about IBM Web Experience Factory (WEF), as a development tool, is its friendliness to plugging in custom Java code. And by this we mean optionally plugging in code, only where it is truly needed, not being covered natively by the builder set. You can do this in…a lot of places, including places people wouldn’t necessarily even think to look. (Ironically, the full extent of this capability often eludes the highly experienced Java programmer, who may be mentally wired in to a different way of organizing code, and not intuitively “see” how WEF presents itself for such extension.)

And so we have the humble “Ell Jay Oh” as part of the toolkit—we teach its basic mechanics within the first five days of training—but we don’t talk a lot about the strategy of how to organize the LJOs that you do create. As you might imagine, there is value in putting a little thought into this, and the following ideas have evolved into a pretty useful strategy that I like more the more I use it.

LJO layers that work together

The big inspiration for my strategy came some years ago now, when the Domino builder set first became available and I worked on a number of projects that needed it. As a longtime Domino developer before I discovered the Factory, I understood that really getting value out of Domino meant going beyond (way beyond) the limited capacity of the builder set. And so lots of core Domino functions wound up as Java methods in LJOs. Enough so, that I had to do something to organize them!

At some point, it dawned on me to look at my LJOs in layers, much like you’d think of SOA layers. It turns out that there are two very clear separation points that you can lean on to do this: IXml, and WebAppAccess. This implies three layers of LJOs in your project architecture.

External APIs (no IXml)

The first layer is the one that is “most distant” from Factory models, and “closest” to purely standalone code—code that would be useful completely outside of WEF. It is distinguished by the absence of either IXml or WebAppAccess imports (or anything else specific to the Factory).

Using the example of my Domino integrations, this was code which had inputs and outputs that all belonged to the Domino API. It spoke Views, and DocumentCollections, and RichTextItems; internally it ran @Formula code and called Agents. In fact, much of it was code that I could have used in Domino Designer, to build things like Domino Java Agents.

The idea works for other external APIs, too. Just recently I started doing some work with XSLT transforms and PDF generation, and so built a definable layer to house that code, based upon this same idea. Housed neatly in its own layer from the beginning, my overall project progress was noticeably faster than what I’d seen before the Domino example evolved, despite my being new to both those APIs.

In short: this is the implementation layer. The code that does the meaningful API work. In SOA terms, think of this layer as the black box of your service operation’s implementation.

Middleware code (to/from IXml)

This layer might be called middleware. It sits between the API code layer and the layer that touches Factory models. It uses IXml, but not WebAppAccess. Its purpose is nearly exclusively to transform API objects to and from IXml (and other Factory-friendly formats). Many transforms can be done at this level (e.g., sorts, reorganizing XML, etc.), but not anything that requires WebAppAccess.

In my Domino integrations, this layer featured methods that took Domino objects as inputs and returned IXml, and other methods that took IXml inputs (and simple types, of course) and returned Domino objects. Method bodies would simply make calls to the “external API” layer for the Domino objects, push them into and out of IXml format, and be done. Transforms supported this core mission.

This is the layer that is at once the easiest to overlook, and the most valuable. Once I really committed to the practice, I noticed that I started to change the way I wrote my transforms. In a nutshell, I re-wrote a number of transforms specifically to avoid using WebAppAccess just to commit to the idea…you know, just because I’m a geek…and boy, if the transforms themselves didn’t become notably more useful and usable as portable code after the switch!

So, in short: this is your integration layer. The translator. In SOA terms, this is where you define your service input and output schemas, and do internal service transforms against the black box.

Model code (makes use of WebAppAccess)

This is the layer that is “closest” to Factory models, and “farthest away” from external systems. It makes use of both IXml and WebAppAccess. This means it knows about model variables, and model actions, and Data Service names, etc. And here’s the funny part: this is the only layer that you actually use as LJOs in your WEF models.

Because of how the request chains outward from here (first to middleware layer then to API), this code is usually highly readable: in general, outward-facing methods in this layer simply assemble inputs, invoke middleware layer calls, and make any result transforms that are uniquely specific to the consuming model. This makes them much friendlier to read, when they are sitting next to inward-facing methods that set model variable values, interpose a complex branching structure in front of a named service call, or other very WEF-specific things.

And, of course, when things are set up this way, the model-specific LJOs can share code from the middleware layer. More often than not, this proves useful as a project develops, in ways that weren’t obvious when the project started. Having the core architecture already in place usually makes for much happier developers at that time!

In short: this is the invocation layer. The kickoff point. In SOA terms, this is the Consumer. Transforms done here can be thought of as true pre- or post-processing transforms, not internal to the service operation but fully independent of it.

General practice notes

This layout seems to make all the Java code (the code at each layer) much more readable, since it’s “siloed” more appropriately according to its function. There are more files to keep track of, of course, and sometimes the architecture can seem a little cumbersome, but it expands very elegantly, and with separate files it has been notably friendly to separating work between multiple project developers.

Note that this layer system does not necessarily impose anything on your practice of Java package naming. You can certainly set up a simple packaging practice based on those layers, but it isn’t necessary. In general, my own practice has been to make a separate LJO class for each model that needs an LJO, packaged under app.models, and named according to the model name. This makes it obvious that it’s tied to the model, and since it’s simply consuming the middleware layer for anything outward-facing, that tight coupling really doesn’t bother anything.

Having things separated like this, too, does make it notably easier to swap out one external API for another, if that need arises—and sometimes it does. Similarly, with a well-constructed external API and middleware layer, it is not difficult to bolt that on to a new set of WEF models that might need the same integration. Certainly in my Domino work I leaned heavily on the evolved two “outer” layers; tying a new project’s models into the existing core was extremely simple, and I did it a lot.

Global tools

A more recent practice I’ve started is to seriously consider an LJO inheritance hierarchy, for the project, and using the same general strategy. For any project that uses a lot of Java code, this can be very useful. Consider the following two classes in this project, AppBase and LJOBase:

AppBase is at the top of the hierarchy, and its purpose is to provide an inheritance chain back to things that are of interest to the entire project: constants, utility methods, etc. In general, my intention here is that there is no IXml in this class, but the jury may still be out on that. There’s a huge comment in this file, for the benefit of other developers (and of course reminding myself).

LJOBase extends AppBase, and adds things that are of interest to models in the project: IXml, WebAppAccess, model-specific constants and utility methods, etc. One thing I like to put in here are constants for working with schemas that are shared across multiple models.

With these in place, then, each time I go to create an LJO for a given model, I extend LJOBase and immediately get access to all the things that are “global” across the project for WEF models. When I’m creating the “external API” layer classes, extending AppBase gives me access to project-level items without the extra WEF model clutter. This is a simple idea that is commonly done outside WEF, but which we tend to overlook when we think of our Java code as “I just need a quick LJO for this model”.

Some specific thoughts about global artifacts:

Constants

I like listing global constants either in AppBase or LJOBase, depending on how coupled they are with models. Things like the application name (for the purpose of logging), logging messages, critical folder paths and URLs in the project, global date formats, file size thresholds, constants for generating code, etc. Again, in LJOBase I like listing schema element names for any schemas that are used by multiple models. And so on. I’ve been happy to be able to reach upstream in the hierarchy for these things, once I’ve located them in the right place.

Logging

One thing I’ve been really happy about is the use of a global logging class, offering a full range of static methods for logging messages throughout the application. My AppLogger class lives alongside AppBase and LJOBase in the app.util package, and provides a global framework for logging messages.

AppLogger, itself, is tied into AppBase in that AppBase specifies the logger output setting:

Internally, it defines a consistent vocabulary and textual signature for logged messages (e.g., supplies the application name, extracts message content from a supplied exception object, accepts a further unique message, etc.), and then wraps a generic Logger object’s methods with that:

With static methods available from this logging class available in the project, logging a message in LJO code is very compact and simple. And consistent. And enhanceable. Here is an example in the project’s PDF generator LJO:

And meanwhile, over in some service transforms:

And so on. In my experience, the return on investment for setting up that logger class has been huge, and I have had occasion to tweak or enhance the way messages get logged, here and there—and it’s nice to be able to do it in once place, and have it “cascade down” immediately.

Run with it!

Keep in mind that the above are ideas, and there’s no possible way they’re exhaustive. And, really, they’re nothing more than the outgrowth of a little attention to designing how you use custom Java in your WEF project. You may well find even more efficient ways to handle things yourself.

What many WEF developers forget is to look at their LJO strategy in the first place. By all means, design one. Agonize over the names, packaging, and breakout. It’s worth the effort!

————————————————————————————————————————————

About the Author: Kevin Wilmeth has been working with IBM Web Experience Factory (WEF) since 2001 as a field developer, trainer, coursewriter, mentor, and architect. He came to the Factory from a prior career with Lotus Notes/Domino, where he served the same roles in much the same depth, and for a while specialized in integrating the two technologies. Kevin has also spent some time in the education technology industry, bringing both the technology and the education to people and places that just didn’t have it before. He lives on Alaska’s Kenai Peninsula, where the fish laugh at him and musical instruments shudder at his very approach.

————————————————————————————————————————————
This article is from our monthly resource e-Newsletter. Did you miss it in your inbox? Visit our eNewsletter archives for past editions or if you want to receive our monthly newsletter automatically, simply write to Ruth O’Keefe and request to be added to our E-Newsletter list. You can also view the Master Series Archives.