"Controller" Classes Have Been Helping Me Greatly

Wed Dec 26 16:54:16 EST 2012

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

I mentioned a while ago that I've been using "controller"-type classes paired with specific XPages to make my code cleaner. They're not really controllers since they don't actually handle any server direction or page loading, but they do still hook into page events in a way somewhat similar to Rails controller classes. The basic idea is that each XPage gets an object to "back" it - I tie page events like beforePageLoad and afterRenderResponse to methods on the class that implements a standard interface:

package frostillicus.controller;

import java.io.Serializable;
import javax.faces.event.PhaseEvent;

public interface XPageController extends Serializable {
	public void beforePageLoad() throws Exception;
	public void afterPageLoad() throws Exception;

	public void afterRestoreView(PhaseEvent event) throws Exception;

	public void beforeRenderResponse(PhaseEvent event) throws Exception;
	public void afterRenderResponse(PhaseEvent event) throws Exception;
}

I have a basic stub class to implement that as well as an abstract class for "document-based" pages:

package frostillicus.controller;

import iksg.JSFUtil;

import javax.faces.context.FacesContext;

import com.ibm.xsp.extlib.util.ExtLibUtil;
import com.ibm.xsp.model.domino.wrapped.DominoDocument;

public class BasicDocumentController extends BasicXPageController implements DocumentController {
	private static final long serialVersionUID = 1L;

	public void queryNewDocument() throws Exception { }
	public void postNewDocument() throws Exception { }
	public void queryOpenDocument() throws Exception { }
	public void postOpenDocument() throws Exception { }
	public void querySaveDocument() throws Exception { }
	public void postSaveDocument() throws Exception { }

	public String save() throws Exception {
		DominoDocument doc = this.getDoc();
		boolean isNewNote = doc.isNewNote();
		if(doc.save()) {
			JSFUtil.addMessage("confirmation", doc.getValue("Form") + " " + (isNewNote ? "created" : "updated") + " successfully.");
			return "xsp-success";
		} else {
			JSFUtil.addMessage("error", "Save failed");
			return "xsp-failure";
		}
	}
	public String cancel() throws Exception {
		return "xsp-cancel";
	}
	public String delete() throws Exception {
		DominoDocument doc = this.getDoc();
		String formName = (String)doc.getValue("Form");
		doc.getDocument(true).remove(true);
		JSFUtil.addMessage("confirmation", formName + " deleted.");
		return "xsp-success";
	}

	public String getDocumentId() {
		try {
			return this.getDoc().getDocument().getUniversalID();
		} catch(Exception e) { return ""; }
	}

	public boolean isEditable() { return this.getDoc().isEditable(); }

	protected DominoDocument getDoc() {
		return (DominoDocument)ExtLibUtil.resolveVariable(FacesContext.getCurrentInstance(), "doc");
	}
}

I took it all one step further in the direction "convention over configuration" as well: I created a ViewHandler that looks for a class in the "controller" package with the same name as the current page's Java class (e.g. "/Some_Page.xsp" → "controller.Some_Page") - if it finds one, it instantiates it; otherwise, it uses the basic stub implementation. Once it has the class created, it plunks it into the viewScope and creates some MethodBindings to tie beforeRenderResponse, afterRenderResponse, and afterRestoreView to the object without having to have that code in the XPage (it proved necessary to still include code in the XPage for the before/after page-load and document-related events).

So on its own, the setup I have above doesn't necessarily buy you much. You save a bit of repetitive code for standard CRUD pages when using the document-controller class, but that's about it. The real value for me so far has been a clarification of what goes where. Previously, if I wanted to run some code on page load, or attached to a button, or to set a viewScope value, or so forth, it could go anywhere: in a page event, in a button action, in a dataContext, in a this.value property for a xp:repeat, or any number of other places. Now, if I want to evaluate something, it's going to happen in one place: the controller class. So if I have a bit of information that needs recalculating (say, the total cost of a shopping cart), I make a getTotalCost() method on the controller and set the value in the XPage to pageController.totalCost. Similarly, if I need to set some special values on a document on load or save, I have a clear, standard way to do it:

package controller;

import com.ibm.xsp.model.domino.wrapped.DominoDocument;
import frostillicus.controller.BasicDocumentController;

public class Projects_Contact extends BasicDocumentController {
	private static final long serialVersionUID = 1L;

	@Override
	public String save() throws Exception {
		DominoDocument doc = this.getDoc();
		doc.setValue("FullName", ("CN=" + doc.getValue("FirstName") + " " + doc.getValue("LastName")).trim() + "/O=IKSGClients");

		return super.save();
	}

	@Override
	public void postNewDocument() throws Exception {
		super.postNewDocument();

		DominoDocument doc = this.getDoc();
		doc.setValue("Type", "Person");
		doc.setValue("MailSystem", "5");
	}
}

It sounds like a small thing - after all, who cares if you have some SSJS in the postNewDocument event on an XPage? And, besides, isn't that where it's supposed to go? Well, sure, when you only have a small amount of code, doing it inline works fine. However, as I've been running into for a while, the flexibility of XPages makes them particularly vulnerable to becoming tangled blobs of repeated and messy code. By creating a clean, strict system from the start, I've made it so that the separation is never muddied: the XPage is about how things appear and the controller class is about how those things get to the XPage in the first place.

We'll see how it holds up as I use it more, but so far this "controller"-class method strikes a good balance between code cleanliness without getting too crazy on the backing framework (as opposed so some of the "model" systems I've tried making).

Moving From Ruby-in-XPages to Polyglot

Wed Dec 12 14:47:28 EST 2012

Tags: ruby polyglot

I've tweeted about this a couple times, but in some of my spare time lately I've put together a new, cleaner implementation of the generic-scripting-language support I first created back when I did my original work with Ruby. I named the new project "Polyglot" and it has two facets:

  1. The aforementioned generic-scripting-language support, done more cleanly and with better capabilities.
  2. A method for storing standalone page-generating scripts as documents in the database that are executed in the context of an XPage.
Generic Scripting Languages

With the usual when-I-get-around-to-it caveat, I plan to use Polyglot to entirely supplant Ruby-in-XPages, since its JSR-223-based features cover JRuby as well. The main thing I need to check on is proper memory usage - the embedding mechanism I originally used provides clear control over the lifetime of the scripting runtime, while JSR 223 does not. That may be fine, since it may properly manage itself, but it makes me a bit nervous.

I'll also have to work out proper context variable access (e.g. "database", "currentDocument", etc.). Some languages (Ruby, namely) seem to have gained a magic power since last I checked to access those variables without having to add special support, but I need to figure out if others work the same way or if I need to try an idea for an adapter I had.

In addition to the JSR 223 languages, I also just today added support for formula language via #{formula: ... } bindings, since I realized that that was exceedingly easy to do. For that one, I didn't bother binding it to the contextual variables, but instead just have it look for "currentDocument" - if it's present, it passes the lotus.domino.Document from that to the session.evaluate(...) call; if it's not, it forgoes the second parameter. I don't expect I'll use that binding much, but it was fun to add.

Standalone Scripts

The standalone script support is a little different - the idea is that, rather than working another language into an XPage, you write a script entirely in the other language, much like you might do with, say, PHP (don't use PHP). You can write a script using a basic editor, it's stored in a document in the database, and then you can execute it in the context of a ScriptRunner XPage, meaning you have the same Java environment as usual (with the same caveat as above that I need to sort out variable access).

Besides the usual suite of JSR 223 languages, I've also started working on integrating the XSP parser from the XPages Bazaar, both as a way to familiarize myself with the Bazaar and to see if there's any use in a setup where your XSP markup is stored in documents and evaluated at runtime rather than design elements compiled in Designer. Maybe, maybe not, but it's a fun test.

 

For now, it's still in an "experimental" state, but eventually I hope to cobble it into a proper releasable state and have it supersede Ruby-in-XPages entirely.

Programming Tips: Implied Booleans and the Ternary Operator

Tue Dec 11 08:40:52 EST 2012

Tags: programming

This isn't Domino- or XPages-specific per se, but I figured I'd make a post about some of my favorite stylistic bits in many programming languages: implied boolean values and the ternary operator, which are distinct but often used together.

What I mean by "implied boolean values" is when a language lets you use things that aren't strictly comparisons or boolean values in if/then tests. The best example for this is probably C (and no doubt the languages that came before it): for a long time, C didn't actually have a boolean data type. Instead, the convention was to use 1 for "true" and 0 for "false" - and many libraries would map constants to those values. That's well enough on its own, but the fun part was that the real rule behind it is that C treats ANY non-zero numeric value as true. Most of the time, that doesn't matter too much, but there are occasions where you can take advantage of that property to save a bit of code without sacrificing readability.

Many languages, such as JavaScript, continue this tradition, and also allow for implied null checking this way (I can't be bothered to look into C's deal with nulls). To demonstrate:

var foo = null
if(foo) { alert("foo") } else { alert("not foo") }
if(!foo) { alert("not foo") } else { alert("foo") }

That code will display "not foo" twice. JavaScript acts the same way for "undefined" values, such as uninitialized object properties:

var foo = { bar: 1 }
if(foo.bar) { alert("yep") } else { alert("nope") }
if(foo.baz) { alert("yep") } else { alert("nope") }

That will display "yep" followed by "nope". This can be handy if you want to test for, say, the presence of an attribute value in an HTML entity (it also treats "" as false).

Though JavaScript and, by extension, SSJS support thing kind of thing, Java is stricter about this kind of thing, forcing you to specifically test for null.

That brings me to the ternary operator, which is one of my favorite things in the world. If you're not familiar with it, the ternary operator is a shorthand way to do an if/then test that results in a value. As a demonstration:

var result = a > b ? option1 : option2

While you can get the same job done with a normal if/then/else block, that operator saves you some typing and, more importantly, often makes it easier to understand your intent. They can even be chained together, though you should be careful not to create too much of a mess:

var result = a == 1 ? option1 :
             a == 2 ? option2 :
             a == 3 ? option3 :
                      option4

In this case, Java DOES support the operator in pretty much the same way as every other non-PHP language (including Formula - @If(...) is basically this in different garb). EL in XPages supports this too, but only in ${}-bound values (due to Designer complaining about "invalid" syntax in #{}-bound values... presumably, you could write your own value bindings programmatically).

I use this kind of thing all the time. One pretty common use is to provide default values, either by checking the current state of a variable and re-assigning it as necessary or looking in, say, an options for overrides:

title = title ? title : "default title"
someVal = options.someVal ? options.someVal : "default someVal"

If you want to be EXTRA slick, you can take advantage of an extra property of JavaScript's "or" operator:

title = title || "default title"

That basically means "use the value of title unless it's null/false/0, in which case use 'default title'".

Hopefully, you can put some of these tricks to good use. Used properly, this kind of thing can drastically simplify your code while making it much easier to read and understand.

Putting Apache in Front of Domino

Sat Dec 08 13:59:11 EST 2012

Tags: apache admin
  1. Putting Apache in Front of Domino
  2. Better Living Through Reverse Proxies
  3. Domino's Server-Side User Security
  4. A Partially-Successful Venture Into Improving Reverse Proxies With Domino
  5. PSA: Reverse-Proxy Regression in Domino 12.0.1

The other day, for my side-project company, I wanted to set up hosting for a WordPress site, ideally without setting up another whole server. The first two ideas I had were pretty terrible:

  1. Hosting it on Domino directly with PHP via CGI. Even if this worked, I assume the performance would be pretty poor and I'd have no confidence in its general longevity.
  2. Hosting it on Apache on another port and using Domino to proxy through. While Domino does have some minor proxy capabilities, they didn't strike me as particularly thorough or suitable for the task.

Since the second option involved running two web servers anyway, I decided to flip it around and do the more-common thing: making Apache the main server and switching Domino to another port. Fortunately, even though it's been years since I ran an Apache server and I'd consider myself novice at best, the process has been exceptionally smooth and has brought with it a couple benefits:

  1. Apache's virtual-host support works perfectly, allowing me to host just the one named site and pass through all other requests to Domino.
  2. My crummy SSL setup works better with Apache, allowing for a poor-man's multi-host-name SSL with my one basic StartSSL certificate. Not only does Apache support SNI for down the line, but in the mean time I can use the same certificate for multiple names (with the usual "mis-matched name" warning) - since Apache handles all requests and funnels them over with the host name to Domino via HTTP, I don't run into the usual Domino problem of only the one SSL-enabled Web Site document being active.
  3. I'm now ready to add load-balancing servers at any time with just a one-line adjustment in my Apache config.

The actual configuration of Apache on my Linux machine was pretty straightforward, with the layout of the configuration directory making it fairly self-explanatory. I linked the modules I wanted from the /etc/apache2/mods-available directory to /etc/apache2/mods-enabled (namely, proxy, proxy_balancer, proxy_http, php5, rewrite, and ssl). Then, I set up a couple NameVirtualHost lines in ports.conf:

NameVirtualHost *:80
NameVirtualHost *:443

Then, I set up a new default site in /etc/apache2/sites-available and linked it to /etc/apache2/sites-enabled/000-domino:

<VirtualHost *:80>
        <Proxy balancer://frostillicus-cluster>
                BalancerMember http://ceres.frostillic.us:8088
                ProxySet stickysession=SessionID
        </Proxy>
        ProxyPass / balancer://frostillicus-cluster/ nocanon
        ProxyPassReverse / balancer://frostillicus-cluster/
        ProxyPreserveHost On
        AllowEncodedSlashes On
</VirtualHost>

That last directive is an important note, and I missed it at first. The "optimize CSS and JS" option in 8.5.3 creates URLs with encoded slashes and, by default, Apache's proxy breaks them, leading to 404 errors in apps that use it. If you turn on AllowEncodedSlashes, though, all is well. Note also the ProxySet line: if that's working correctly (I haven't tested it yet since I don't have a second host set up), that should make sure that browser sessions stick to the same server.

For SSL, I'm not sure what the absolute best way to do it is, but I set it up as another proxy just pointing to the HTTP version locally, so I don't have to set up multiple SSL sites for each virtual host (put into a new site document, 002-ssl):

<VirtualHost *:443>
        ProxyPass / http://127.0.0.1/
        ProxyPassReverse / http://127.0.0.1/
        ProxyPreserveHost On

        SSLEngine On
        SSLProtocol all -SSLv2
        SSLCipherSuite ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM

        SSLCertificateFile /path/to/ssl.crt
        SSLCertificateKeyFile /path/to/ssl-decrypt.key
        SSLCertificateChainFile /path/to/sub.class1.server.ca.pem
        SSLCACertificateFile /path/to/ca.pem
</VirtualHost>

With that config, SSL seems to work exactly like I want: all my sites have an SSL counterpart that acts just like the normal one, much like with Domino normally.

It's only been running like this a couple days, so we'll see if I run into any more trouble, but so far this seems to be a solid win for my hosting, other than the increase in memory usage. I'm looking forward to having another clustermate in the same location so I can try out the load balancing down the line.