frostillic.us

How I Got XPiNC Run-On-Server With SSO Working

Among the new features in Domino 9 is this little guy, found on the Launch pane of a database's properties when you set it to open an XPage:

If you've ever used an XPiNC application before, you'll know this is a godsend, promising the vast performance benefits of running an app on a server combined with the "the users are stuck using the Notes client" benefits of XPiNC.

I turned this on for a new app on one of my client's servers (let's say the server name is ClientName-2/ClientName) and took it out for a spin. However, I immediately noticed something was amiss: the status bar declared that it was unable to find the server "www.clientname-2.com". With a bit of searching, I found I wasn't alone: Notes assumes that the Domino server is available as its common name in local DNS for some reason, and fails in various ways depending on your network's DNS failover behavior and whether or not www.clientname-2.com is a real web site.

So, okay, as the last comment on that linked question indicates, you can "fix" this behavior by manually adding entries to the "XPages Performance" preferences pane (I have two because it's on two clustered servers, you see):

(Though it's blurred here, I note that the "Server" column stores only the common name of the server, not its full hierarchy. Let's hope all your common names are distinct!)

Alright, now that that is sorted out, Notes should use the http://whatever.com URL you typed in rather than its own ludicrously-naïve guess. Open up the app again and bam:

Oh. Okay. Here's where things got fun. At this point, the Notes client is essentially using an embedded web browser to point to http://whatever.com/admin/vr.nsf/Page.xsp and carrying with it no authentication information. The documentation cheekily suggests that this means that your "configuration is not correctly set up" and basically leaves it at that.

I'll spare you the intervening days of frustration and cut to the chase: I needed to create an Account document in the client, set up SSO on the server (I had been using normal session auth), and disable loading Internet configurations from Server\Internet Sites documents (seriously).

Because I wasn't about to have my users use the Preferences -> Accounts pane if I could avoid it, I discovered that you can apply Accounts via policies. I went into the "Accounts" view of the "Configuration" tab of Administrator and created an account like so:

(That bit about "PreferredUsernameField" was because I stole the instructions from something about the social stuff in 9. I don't know if it's required, but I don't dare remove it now.)

Once I had that account document created, I went to the active Desktop policy document, then the "Accounts" tab, clicked "Update Links", and chose "All Supported" (because I only had the one anyway):

So at this point, I had it so that the Notes client was properly picking up the Account document, but it still wasn't signing in via SSO properly. That's when I had flashbacks to setting up a Sametime server, I felt a dread in the pit of my stomach, and I disabled Internet Sites for the server. Once I generated a new SSO configuration (with no "Organization" specified, so it was choosable in the server document) and restarted HTTP, it started to work. Hooray-ish!

Fortunately, in my case, I'm lucky: there's a spare server available that doesn't need to handle normal web requests (at least barring a catastrophe), so I can afford to disable Internet Sites configuration for it. However, long-term, I would be delighted to be wrong in my diagnosis - so if anybody knows a way to get this working while still supporting Internet Sites, please let me know. I would also love to know if there's a way to either avoid using those "run these apps on the server" entries in the client prefs or distribute those via policy as well.

Tags: xpinc, sso, miasma

0 comments

Public Service Announcement - NotesIn9 is down.

David Leedy has run into some trouble with NotesIn9.com being down and he asked if I could help him get the word out about it, which I'm more than happy to do:

 


 

Hi - Just wanted to drop a note out there about my NotesIn9.com website.  Currently it's redirecting to someplace else for some unknown reason.  I assume it's been php hacked but I don't know.

 
My Wordpress site is generously hosted by Chris Miller and I've sent him a note.  Though since he's in the middle of the whole IamLug thing, it might be down for a little while before he can look at it.
 
My other non wordpress sites appear fine - so you can use http://index.notesin9.com (XPages.TV) or http://cheatsheet.notesin9.com (XPagescheatSheet.com) for the time being.
 
If you use the index site - try NOT to use Internet Explorer.  There's a bug with that browser where every video link you click will play the SAME video.  I'm not sure what's up with that yet.
 
I am getting more active on redoing my websites - but with all my travel recently it's just very slow going.
 
I'm actually heading out to Orlando for the day job for a couple days so I'll have limited ability to try and correct this problem.  But I will get this fixed as soon as I can.
 
Sorry for any inconveniece. 
 
David Leedy
NotesIn9 ScreenCast
 

Tags: ghost-posting

1 comment

My Latest Programming-Technique Improvements

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.

Tags: java

2 comments

Java Traps and Misconceptions

I wrote a post over at the Social Biz UG site covering a number of traps and conceptual hurdles I frequently see people running into when it comes to Java:

https://www.socialbizug.org/blogs/gidgerby/entry/java_traps_and_misconceptions?lang=en_us

Tags: java

0 comments

Fun With Old XML Features

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.

Tags: java, xml

0 comments

Release M1 of org.openntf.domino

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!

Tags: org.openntf.domino

3 comments

We Have The Technology: The org.openntf.domino API

As Nathan and Tim posted earlier today, a number of us have been working recently on a pretty exciting new project: the org.openntf.domino API. This is a drop-in replacement/extension for the existing lotus.domino Java API that improves its stability and feature set in numerous ways. The way I see it, there are a couple main areas of improvement:

Plain Old Bug Fixes

doc.hasItem(null) crashes a Domino server. That's no good! We've fixed that, and we're going through and fixing other (usually less severe) bugs in the existing API.

Modern Java

Java has progressed a long way since Domino 4.x, but the Java API hasn't much. Our API is chock full of Iterators, proper documentation, generic type definitions, preference for interfaces over concrete classes, and other trappings of a clean, modern API. Additionally, Nathan put together some voodoo programming to take care of the recycle thing. You read that right: recycle = handled.

New Features

In addition to cleaning up the existing functionality, we're adding features that will be useful every day. My favorite of those (since I'm implementing it myself) is baked-in MIMEBean-and-more support in get/replaceItemValue:

Set<String> stringSet = new HashSet<String>();
stringSet.add("foo");
stringSet.add("bar");
doc.replaceItemValue("Set", stringSet);

doc.replaceItemValue("Docs", database.getAllDocuments());

NoteCollection notes = database.createNoteCollection(true);
notes.buildCollection();
doc.replaceItemValue("Notes", notes);

doc.replaceItemValue("ExternalizableObject", new MimeType("text", "plain"));

List<String> notAVector = new ArrayList<String>();
notAVector.add("hey");
doc.replaceItemValue("TextList", notAVector);

doc.replaceItemValue("LongArray", new long[] { 1L, 2L, Integer.MAX_VALUE + 1L });

That's all legal now! We have more on the docket for either the initial release or future versions, too: TinkerPop Blueprints support, easy conversion of applicable entities to XML and JSON, fetching remote documents from DXL or JSON files, proper Logging support, and more.

 

As we've been developing the new API, we've already found it painful to go back to the old one - it doesn't take long to get spoiled. And fortunately, the transition process will be smooth: you can start by just replacing one entry point for your code (say, changing "JavaAgent" to "org.openntf.domino.JavaAgent" in an agent), then advance to swapping out your "import lotus.domino.*" line for "import org.openntf.domino.*" line, and then start using the new methods. All the while, your existing code will continue to work as well or better than before.

We hope to have a solid release available around the beginning of next month, and in the mean time I highly encourage you to check out the code. Download it, give it a shot, let us know how it works for you and if you have anything you'd like us to add.

Tags: java, org.openntf.domino

5 comments

A Mini-Vacation With Ruby and the Domino Data Service

Since I've been neck-deep in LotusScript and Java for the past couple weeks, I decided to take a bit of a sanity break today and play around with Ruby. Specifically, I wrote a skeletal wrapper for the Domino Data Service in the ExtLib and the first steps of a Rails app using it a bit. I don't expect this to actually be useful down the line, or even necessarily to get any more work put into it, but it was a fun diversion.

The API takes the same general shape as the normal Domino API, except you start with a database, which can take connection and credential parameters (though the credentials don't actually work for some reason). So to access the testing database I created, I do this:

db = DHTTP::Database.new(
  :server => "api.frostillic.us",
  :path => "tests/http.nsf"
)

In the Database class, I implemented a way to get all views or to get a view by name, while views let you get entries, which in turn contain their values and a way to get documents, which provide the usual item access. For example:

db.views.each do |view|
    puts "Found view: #{view.title}"
end

view = db.get_view("TestView")
view.entries.each do |entry|
	puts "Column values: #{entry.column_values}"
	puts "Doc body: #{entry.document.Body}"
end

It's not particularly amazing, nor is it particularly efficient, but it does the job: it supports the service's paging for view entries and converts Date/Times to Ruby Times for document fields, so it could be used for light read-only use.

Then I set up an extremely bare-bones Rails app that uses the objects in the most direct and ugly way possible, but, again, it works:

class TesterController < ApplicationController
  def index
    db = DHTTP::Database.new(
      :server => "api.frostillic.us",
      :path => "tests/http.nsf"
    )
    @views = db.views
  end
  
  def view
    @title = params[:title]
    db = DHTTP::Database.new(:server => "api.frostillic.us", :path => "tests/http.nsf")
    @view = db.get_view(@title)
  end
end

The two pages just spit out lists of either the views or the view entries:

<ul>
<% @views.each do |view| %>
    <li><%= link_to(view.title, :action => 'view', :title => view.title) %></li>
<% end %>
</ul>

...and...

<ul>
<% @view.entries.each do |entry| %>
    <li><%= entry.column_values.inspect %></li>
<% end %>
</ul>

I think the Domino REST API could be really useful. When I looked into it a while ago, I ran into trouble wherein the view entry counts didn't reflect entries hidden via reader fields, which is probably still the case, but other than that I imagine it could be put to real use. It brings it more in line with the other modern NoSQL databases, which generally use HTTP/JSON-based APIs as well. Combine that with Apache for load balancing and failover and suddenly you have a really compelling modern database back-end for other platforms.

If you're interested in looking at the code, I tossed the API up on GitHub: https://github.com/jesse-gallagher/Domino-HTTP-API-for-Ruby

Tags: ruby

0 comments

I Know Some Guys

I've been a bit quiet lately, but that's mostly because I've been pretty busy lately. After my old company began closing down, I started going whole-hog in my consulting company, I Know Some Guys. Naturally, I can't go TOO much into it, but the general gist is "so far, so good." We have a couple clients so far and they've been keeping me busy indeed. A lot of that has involved classic Notes client and web development, but I guess that serves me right for snickering at people working on large "modernization" projects. Nonetheless, if you're looking for someone to build an app for you, consult, or so forth, drop me a line.

One nice side effect of having so many tasks flying around early is that it's really giving the project-tracking database I built a trial by fire. I built it to be pretty similar to the one I made at my old place, but boiled down to just the necessary components. Putting it under heavy use has done a fantastic job figuring out which parts I don't really use, which parts I sorely need to add, and which UI decisions I made turned out to be... questionable. Moreover, we're using Freshbooks for our time tracking and billing, and I'm really itching to integrate with their API. I hear tell that Domino has some REST- and OAuth-consuming capabilities and this will finally give me a chance to put them to use in a real situation.

Now, with any luck, I'll have some time shortly to knock out a few posts I've been developing notes on. In the mean time, if you somehow haven't already, go read Tim Tripcony's post about SSJS and the ensuing comment discussion. I promise it's worth your time.

Tags: inside-baseball

0 comments

The Bean-Backed Table Design Pattern

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.

Tags: java, techniques

0 comments

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.

Tags: xpages, mvc, java

6 comments

"Controller" Classes Have Been Helping Me Greatly

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).

Tags: xpages, mvc, java

6 comments

Moving From Ruby-in-XPages to 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.

Tags: ruby, polyglot

3 comments

Programming Tips: Implied Booleans and the Ternary Operator

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.

Tags: programming

0 comments

Putting Apache in Front of Domino

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.

Tags: apache, admin

1 comment

© 2013