Designer Experiment and Feature Request: JSF Tools in Designer

Wed Sep 17 18:31:59 EDT 2014

Tags: jsf fixit

TL;DR: You can install JSF tools in Designer to help out quite a bit with faces-config.xml editing, but there are bugs that may require changes in Designer's code to fix.

I was having a discussion about Andrew Magerman's recent on-point jeremiad about SSJS and the topic got to the difficulty of using Java in XPages if you don't already know the ropes - creating classes, managed beans, etc.. I looked around a bit for examples of how other tools do it, and I found this page on using the Web Tools Platform (WTP) plugins in Eclipse for doing basic JSF development. Looking through the tutorial, you can see parts that don't apply to XPages (the stuff about locating the tags and creating JSP elements), but some parts clearly would, such as the faces-config.xml editor. Mid-lamentation about how this isn't available to us, I noticed the date: June 18, 2007. "2007?" I said to myself. "Why, that's even older than Designer!"

So I set out trying to cram this stuff into Designer. The first step was to find a version of WTP that would work with the base version of Eclipse used in Designer - Ganymede, or Eclipse 3.4. I found an archived build of WTP version 3.0.5, which fits our needs. Unlike most Eclipse plugins, the download lacks a normal site.xml file, so I dropped the features and plugins into their respective folders in <Notes Data>\domino\workspace\applications\eclipse.

The next step was to install the prerequisites. To do that, I added the standard Ganymede Update Site to Designer in the File → Application → Install screen with the name "Ganymede" and URL "http://download.eclipse.org/releases/ganymede/". I found everything I could relating to the core EMF, EMF XSD, GEF, DTP, and their SDKs. Once I had them installed and I restarted, I went to File → Application → Application Management to find the category containing the WTP stuff, the "Java EE Developer Tools":

For me, it was disabled by default, so I had to click the "show disabled" icon (the third in the toolbar) and then select and enable it. If you're missing any dependencies, it'll tell you, though it'll give you the plugin ID instead of a friendly name. Fortunately, it's usually easy enough to match the friendly name to what you need from the Update Site. Everything is in there, in any event.

Once that stuff was enabled (and I restarted Designer), I still had the task of actually enabling the tools for an NSF project. Normally, you'd create a new Web Project in Eclipse and it would come pre-configured, but that's not how it works with NSFs. There's supposed to be a way to enable the features after the fact to an existing project ("Project Facets"), but I found that that didn't even show up until I took a couple steps first.

To find what I needed, I created a new Web project (New → Web → Dynamic Web Project) with the "JavaServer Faces v1.1 Project" configuration:

Then, I went to copy some of the project settings from that project into the NSF. To do that, I enabled displaying dotfiles in the Package Explorer (the "sandwich" icon → Filters... → uncheck ".* resources") and then opened ".project" inside the newly-created project. From there, I copied some lines from the natures node of the XML and pasted them into the same place in the ".project" file for the NSF:

		<nature>org.eclipse.wst.common.modulecore.ModuleCoreNature</nature>
		<nature>org.eclipse.wst.common.project.facet.core.nature</nature>

I also copied two files from the ".settings" folder of the new project to the one in the NSF:

org.eclipse.wst.common.component
org.eclipse.wst.common.project.facet.core.xml

Once I did that, I was able to right-click on the NSF project, go to Properties, and see "Project Facets". In there, I selected the "JavaServer Faces v1.2 Project" and then clicked the "Further configuration required..." link that sprouts at the bottom. I tweaked the settings slightly to match the NSF layout, namely the source folder:

Then I hit Next and... nothing happened. Or, more accurately, an NPE was thrown out to the OSGi console. That appears to happen sometimes and I'm not sure what triggers it, but some combination of re-opening Designer and re-copying those files seems to help. Who knows?

Once the Next button DID work, the next page was fine, so I hit okay. When I did that, Eclipse got to work JSF-ifying the project, creating stuff like web.xml and MANIFEST.MF files we don't need. Those aren't important (I wish web.xml was important), but they're not everything it enables: the cool thing that you get to use is the faces-config.xml editor. Since the DB I created used an older, pre-Framework-and-@ManagedBean version of my XPages Scaffolding project, it came chock full of values already filled in:

And it's not just viewing what's there. The editor comes with tools for letting you create each of these elements. In some cases, it's just a Java class picker (which on its own is valuable due to not having to remember the XML element name), but in others it's much more complex. Managed beans are a perfect example - the editor lets you create beans based on either an existing class or an inline new class (make sure you pick the right source folder), it recommends a name for you (for if you're lazy), and even lets you specify the different types of managed properties, the names of which it picks up from the getters and setters in the class (!):

This includes the esoteric list and map values:

So this is pretty cool, huh? Should everyone just drop it into Designer and lead better, more-productive XPage-developing lives? Well... not quite. Aside from the fact that we can't use all the other goodies from the tool set (like the JSP editor) and the parts that the tools don't know about (like view-scoped managed beans), there's a problem wherein part of the configuration needed to support the editor is reset whenever you close and re-open the NSF in Designer. I've been able to track down changes it makes to the .settings/.jsdtscope file, but just fixing that isn't enough to make it work again (or, if it is, it takes a project re-open to refresh, which defeats the point). The upshot is that you need to go through that project-facet setup every time you open the project. The editor also doesn't open up when you open faces-config.xml from the "Applications" view, only the "Package Explorer" view (well, presumably any non-"Applications" view would do).

This is where the feature request comes in: I think this sort of thing should be in Designer (better: the XPages/VFS bits of Designer should be in stock Eclipse, but that's a larger project). There's a lot standing in between us and using all of the available web tools, but even just the faces-config.xml editor would go miles toward making Java palatable to legacy-Notes developers, and would even be a nice quality-of-life improvement to those of us who breathe Java daily. The first step to improving XPages app development is to make it easier to do the right thing, and this would be a big step in that direction.

Quick Tip: A View-Filtering Search Box

Tue Sep 16 21:31:16 EDT 2014

Tags: xpages

One of the problems that crops up in some situations in XPages is the one described here: executing Ajax queries in too rapid a succession can cause the browser to cap them out until a full refresh. Depending on how you're encountering the problem, there may be a built-in solution: XSP controls that execute Ajax requests often have a throttling or latency parameter, and the same applies for "manual" JS widgets like Select2 (called "quietMillis" there).

Another such situation is the topic of this code snippet: a "filter view" control that allows the user to type and executes partial refreshes for each keypress or clearing of the field. To solve this, I found a block of code I wrote years ago, but should do the trick nicely. It uses setTimeout and clearTimeout to do this sort of delayed search and throttling. As I recall, it worked pretty well, though you'd want to increase the 500ms latency if the request usually takes longer, I suppose (or improve your page speed).

The code is from a Custom Control with styleClass, style, and refreshId properties and stores its value in viewScope.searchQuery.

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core">
	<xp:div styleClass="lotusSearch #{compositeData.styleClass}" style="#{compositeData.style}" id="searchBox">
		<xp:inputText id="searchQuery" styleClass="lotusText" value="#{viewScope.searchQuery}" type="search">
			<xp:this.attrs><xp:attr name="placeholder" value="Search"/></xp:this.attrs>
			<xp:this.onkeypress><![CDATA[
				if(event.keyCode == 13) {
					if(window.__searchTimeout) { clearTimeout(window.__searchTimeout) }
					XSP.partialRefreshPost("#{javascript:getComponent(compositeData.refreshId).getClientId(facesContext)}", {})
					return false
				}
			]]></xp:this.onkeypress>
			<xp:eventHandler event="search" submit="false">
				<xp:this.script><![CDATA[
					// search is fired when the "X" in WebKit is clicked to clear the box
					if(window.__searchTimeout) { clearTimeout(window.__searchTimeout) }
					window.__searchTimeout = setTimeout(function() {
						XSP.partialRefreshPost("#{javascript:getComponent(compositeData.refreshId).getClientId(facesContext)}", {
							execMode: "partial",
							execId: "#{id:searchBox}"
						})
					}, 500)
				]]></xp:this.script>
			</xp:eventHandler>
			<xp:this.onkeyup><![CDATA[
				// Keypress doesn't fire for deletion
				if(event.keyCode != 13) {
					// Let's try some trickery to auto-search a bit after input
					if(window.__searchTimeout) { clearTimeout(window.__searchTimeout) }
					window.__searchTimeout = setTimeout(function() {
						XSP.partialRefreshPost("#{javascript:getComponent(compositeData.refreshId).getClientId(facesContext)}", {
							execMode: "partial",
							execId: "#{id:searchBox}"
						})
					}, 500)
				}
			]]></xp:this.onkeyup>
		</xp:inputText>
	</xp:div>
</xp:view>

The Basic Xots Tasklet in the Blog

Sat Sep 06 09:28:25 EDT 2014

Tags: blog xots

Continuing in my two-day spat of blog posts shamelessly containing "blog" in the title, I figured I'd mention how I'm using Xots for new-comment notifications.

If you're not familiar with it, Xots is a recent addition to the OpenNTF Domino API (added in the recently-released M5 RC1 build), intended to replace both agents and DOTS. There's still more work to be done on the scheduling portion, but Xots is perfectly capable of running manually-created tasks in a similar manner to Threads and Jobs as well as, to a slightly-lesser extent, responding to custom-named events.

The latter is the way I'm using it. I created a Tasklet class and told it to be triggered when something sends an event named "newBlogComment". The code therein is pretty simple: there's a handleEvent method that is fired when an event with that name is fired (by any app on the server, but it's just the one currently), and that code is pretty bog-standard Domino emailing code. The trigger happens in the Comment model class, and it's just a basic one-line affair.

Now, admittedly, in order to get the Xots task working, I had to write an agent to specifically name the class in the $Xots field of the icon note, but that is something that will be handled by a Designer plugin eventually - it's just the price of being an early adopter for now.

So is this a big, world-changing paradigm shift? Not in this instance, but it demonstrates that it's pretty straightforward to start writing multi-threaded and decoupled code using Xots, including custom events. Over time, it will expand to cover scheduled tasks and API-triggered database events ("document saved", etc.). It's pretty cool stuff.

How I'm Handling URLs for the Blog

Fri Sep 05 20:27:31 EDT 2014

Tags: blog

As I mentioned in the introductory post for the blog, I'm putting my investigation into RequestCustomizerFactory classes to work in the blog.

At its core, the point of what I'm doing is to allow me to write code like this:

<xp:link text="whatever" value="/post.xsp?id=somepostid">

...and have the generated link be something like:

<a href="/blog/posts/somepostid">whatever</a>

The core of this is the ability of a RequestCustomizerFactory to specify a UrlProcessor that is used by basically every URL-generation routine in XPages to map the XSP-side URLs to their final HTML version. You can find the code in the config.ConfigRequestCustomizerFactory class. What I do is make use of a List of Maps in my config document to represent a configurable list of known redirections (which correspond to Substitution rules in the Directory). The UI in the configuration page looks like this (kindly ignore the unsightly buttons):

Alias Configuration

The first two columns are regular expressions to match the server name (to ensure that the DB still works if copied to another server or accessed by a different set of web rules) and XSP-side URLs, while the last is a replaceAll replacement pattern, where $1 represents "group #1" from the regular expression - the text enclosed by the first set of parentheses.

Using this, I'm able to keep my XSP code agnostic as to what cleaner routing is available on the server - I don't have to hard-code assumptions about "/blog/posts/somepostid" anywhere in XSP or Java. Instead, that's handled entirely via the user-editable configuration document.

Now, ideally, you wouldn't even need the configuration document. Ideally, the code would look to the Directory to figure out which web site is active and if it has any Substitution or (maybe) Redirection rules that apply to the current database. That's on the docket for future improvement, but for now the current method strikes a reasonable balance of agnostic code with user-level configurability.

New Blog Structure

Fri Sep 05 17:37:26 EDT 2014

Tags: blog

So I finally got around to re-doing my blog app after letting the previous one wither on the vine for years. The main things this new template has over the previous one are:

  • A properly responsive design care of WrapBootstrap. Conveniently, it's the same design I use for our internal task-tracking app, so I had most of the renderers ready.
  • Along those lines, the XSP structure is heavily based on standard/ExtLib components when at all possible, rather than putting the Bootstrap structure into the page code.
  • I've also switched it to being based on the frostillic.us Framework, which I'd darn well better, since it's the name of the blog.
  • I've finally separated the app and data NSFs, which I should have done a long time ago.
  • I'm trying out a RequestCustomizerFactory combined with some web rules to generate somewhat-better URLs while still writing the normal ".xsp" page names and query strings in the XSP code itself, so it remains portable. I'll have to go into how I'm doing that eventually... and I'll also have to expand how it works to cover RT data as well.
  • I put an actual license statement at the bottom of the page (again: finally).
  • Translation support for the app UI if I bother to add that.

One thing it doesn't have is any amount of professionalism in the development and deployment: it's the work of part of the last couple days and accordingly lacks a lot of even basic features (tags, threads, a proper search UI, etc.) and is probably buggy as sin. Still, I wanted to get something shipped instead of letting it linger forever. I've got a reasonably-lengthy TODO list in mind. As expected, it's been a good exercise in finding out what I still need to do both in the Framework and in my renderer.

For those curious, the code is up on GitHub:

https://github.com/jesse-gallagher/frostillic.us-Blog

(Not) My Slide Decks From MWLUG

Tue Sep 02 20:42:23 EDT 2014

Tags: mwlug

At this year's MWLUG, I presented two sessions: one on using nginx as a reverse proxy and load balancer, and one on structured XPages development.

Normally, the next step would be to post the slides, but my decks aren't particularly useful on their own - they were small 8-slide affairs that mostly served as a memory assistance to me, one sight gag, and then a "Demo" slide where I switched to the normal screen for the actual code.

So my plan instead is to blog with the details. The latter session's blog posts actually mostly exist already; I just need to finish the series and provide a capping-off. The nginx stuff, though, doesn't exist yet, and I hope to post my configs and an explanation of the basic process soon enough.

A Centralized Bean for Translation

Mon Sep 01 16:54:59 EDT 2014

Tags: java

The normal method for doing translation in XPages is by using the built-in Designer tooling, which creates properties files for each language for each XPage in your app. This is okay, though it requires using Designer to update the properties (since apparently the translation happens as part of the build process). For the refresh of my blog I'm working on, I'm taking a different tack, inspired by the way a client does it and similar to how I do it in the Framework.

Specifically, I have a centralized bean named "translation" that accepts strings and returns a best-match translation for the current session's locale. This "best match" is the routine that the XSP framework uses for getting resource bundles. So, taking my browser as an example, requesting the bundle named "translation" will cause it to search the classpath for these files until it finds one:

  1. translation_en_US.properties
  2. translation_en.properties
  3. translation.properties
  4. translation_fr.properties

(I'm not sure why it uses translation.properties before translation_fr.properties - maybe it assumes that it's the English strings when there's no locale code on the file).

So I take advantage of this by creating a DataObject bean to do my translation:

package config;

import java.io.IOException;
import java.io.Serializable;
import java.util.HashMap;
import java.util.Map;
import java.util.MissingResourceException;
import java.util.ResourceBundle;

import javax.faces.context.FacesContext;

import com.ibm.xsp.application.ApplicationEx;
import com.ibm.xsp.designer.context.XSPContext;
import com.ibm.xsp.model.DataObject;

import frostillicus.xsp.bean.SessionScoped;
import frostillicus.xsp.bean.ManagedBean;
import frostillicus.xsp.util.FrameworkUtils;

@ManagedBean(name="translation")
@SessionScoped
public class Translation implements Serializable, DataObject {
	private static final long serialVersionUID = 1L;

	public static Translation get() {
		Translation existing = (Translation)FrameworkUtils.resolveVariable(Translation.class.getAnnotation(ManagedBean.class).name());
		return existing == null ? new Translation() : existing;
	}

	private transient ResourceBundle bundle_;
	private Map<Object, String> cache_ = new HashMap<Object, String>();

	public Class<String> getType(final Object key) {
		return String.class;
	}

	public String getValue(final Object key) {
		if(!cache_.containsKey(key)) {
			try {
				ResourceBundle bundle = getTranslationBundle();
				cache_.put(key, bundle.getString(String.valueOf(key)));
			} catch(IOException ioe) {
				throw new RuntimeException(ioe);
			} catch(MissingResourceException mre) {
				cache_.put(key, "[Untranslated " + key + "]");
			}
		}
		return cache_.get(key);
	}

	public boolean isReadOnly(final Object key) {
		return true;
	}

	public void setValue(final Object key, final Object value) {
		throw new UnsupportedOperationException();
	}


	private ResourceBundle getTranslationBundle() throws IOException {
		if(bundle_ == null) {
			FacesContext facesContext = FacesContext.getCurrentInstance();
			ApplicationEx app = (ApplicationEx)facesContext.getApplication();
			bundle_ = app.getResourceBundle("translation", XSPContext.getXSPContext(facesContext).getLocale());
		}
		return bundle_;
	}
}

This uses the Framework's annotation-based managed-bean declaration, but it'd work just fine in faces-config. This allows you to use EL to request a translation, such as #{translation.home}, #{translation['home']}, or #{translation[someVar.prop]}, or by using translation.getValue(...) in Java or JavaScript.

I've found this approach to be much easier to work with. There's only one central file to manage (you could split it up into multiple beans), you don't have to re-generate files for new languages, and the keys can be natural and human-friendly, instead of XPaths.

In addition, you could easily change this bean to get its translation information elsewhere without modifying the XPages that use it - it could look up, for example, against Domino documents to allow non-developers to change the translation values without any special rights (though you'd likely want to tweak the cache in that case).

Quick Tip: Use Dojo Content Panes for Speedier Initial Page Loads

Mon Aug 18 19:12:57 EDT 2014

Tags: quick-tip

The XPages Extension Library is full of hidden gems and one I particularly like keeping in my back pocket is the <xe:djContentPane/> control. It's a fairly unassuming control; like the rest of the components in the "Dojo Layout" category, it takes its name and basic concept from the underlying Dijit. However, you don't have to use it in a full Dojo layout - and, in fact, all of the actual layout types have more-appropriate XSP components. Presumably, its initial use is to load content from a specified URL (via the href attribute), but I find the partialRefresh attribute MUCH more interesting. By setting that attribute to true, you actually carve out a little sub-tree of the page that loads in a separate request from the rest of the page.

That's "loads" in the JSF sense: any of the properties - including ${...}-bound ones - of any component inside of the content pane aren't evaluated until that second request. So what's the upshot of this? If the values inside are particularly expensive, such as heavy database access or a remote API, the rest of the page will load first and then the content pane's area will be a loading message until it finishes.

This tool comes in handy even if you don't touch a single other piece of Dojo UI. The content pane by default is just a <div/>, and you're free to replace the loading message. For example, to Bootstrap-ify it:

<xe:djContentPane id="timeEntriesPane" partialRefresh="true">
	<xp:this.loadingMessage><![CDATA[
		<div style="padding: 1em; text-align: center"><i class="icon-spinner icon-spin green bigger-200"></i></div>
	]]></xp:this.loadingMessage>
	
	...
</xe:djContentPane>

(That snippet is from a pane that loads open client time entries from FreshBooks, hence the id)

The nicest part is that using this technique is often just as simple as wrapping your content in the control and switching the attribute. There are only a few situations I've run into in regular use that require some consideration:

  • If you have some on-load client-side JavaScript that should operate on the contents, such as dojo.behavior. In that case, you can use the onDownloadEnd event (if I recall correctly) to trigger the behavior. You could likely set this via a theme (it's available as both a normal "Event" as well as an attribute on the tag).
  • If the code inside is dependent on query string parameters. Parameters are not included in the partial-refresh URL. I don't recommend the specific suggestion of dumping all query parameters into viewScope (that's asking for an injection vulnerability); instead, make sure that anything you get from the query string is either "baked into" the loading of the rest of the page (say, documentId="${param.documentId}" on a document data source instead of documentId="#{param.documentId}") or added via a data context (e.g. <xp:dataContext var="someParamHolder" value="${param.someParam}"/>).
  • If the content of the pane is error-prone. The partial refresh masks the error message (this may actually be preferable from a UI perspective): you just get an "error loading contents" message on the page. To see the normal stack trace, you have to either disable partial refresh or check the Network pane of your browser's debug tools.

With those in mind: give it a try! Used in the right situation, you can get a nice little boost of initial page load with only a little cost in overall load time - it can make for a surprisingly-better experience.

My Sessions at MWLUG This Year

Mon Aug 18 15:13:02 EDT 2014

Tags: mwlug

As I mentioned at the end of this morning's post about SSL and reverse proxies, I'm going to be giving a session on using a reverse proxy in front of Domino at this year's MWLUG next week. Specifically, it will be one of two sessions, both on Friday:

OS101: Load Balancing, Failover, and More With nginx

I'll be discussing the general reasons why you would use a reverse proxy - not just the aforementioned SSL benefit, but also load balancing, failover, multi-app integration, new features like GeoIP, config/upgrade management, and content rewriting. I'll also discuss a specific setup based on my use of using nginx + HAProxy to use a proxying server to stand in front of two Domino servers on nearby machines. Though the practical example will be based on nginx on a Linode instance, the overall concepts are useful with other front-end servers or devices.

AD105: Building a Structured App With XPages Scaffolding

I'll be discussing the process and reasons for building an app using my XPages Scaffolding project and its further-advanced cousin the frostillic.us Framework. As with the ongoing series, I'll be demonstrating the structure of a basic app using my techniques, where they differ from common practice, where they are the same, and why it's worthwhile to adopt a similar structure. And much like my other session, though the specific example will be with my framework, the concepts and priorities can be applied without it.

Domino SSL and Reverse Proxies

Mon Aug 18 10:11:19 EDT 2014

Tags: ssl

Domino's SSL stack has been long-in-the-tooth and awkward to deal with for a while. Until recently, this has mostly just resulted in the sort of stilted way you have to set up SSL keychains, using the Server Certificate Admin database initially and then "IKeyMan" more and more (specifically, an old version you need 32-bit Windows XP for, like a barbarian), but the job eventually got done.

However, as a post from Steve Pitcher points out, this is becoming rapidly impractical. While I generally second his point that Domino's SSL stack needs a revamp, I believe that the primary importance of that will be for the "secondary" protocols - SMTP, IMAP, LDAP - that require SSL for responsible use. For HTTP, however, I'm on board with the spirit of IBM's proposed workaround - IBM HTTP Server - though not the unsuitable reality of a Windows-only implementation.

The reason for this is my general infatuation with reverse proxying over the last few years. It started, actually, with the amazing article PHP: a fractal of bad design, which is required reading on its own, but the pertinent part is in the section on deployment. In it, the author dings PHP for its traditional deployment method of "just jam it directly into the web server" and instead proposes the "reverse proxy in front of an app server" scenario as generally preferable and easy to do. Having seen just that sort of setup with recommended Ruby on Rails deployments - where the Rails app has a small, local-only HTTP server that a "real" server proxies to - that percolated in my mind for a while. Domino is, after all, primarily an app server, and so this criticism of PHP applies just as much to it. A little while later, I had a reason to try that setup in order to host two distinct app types on the same server (ironically - or irresponsibly - deploying that very type of bad PHP setup in the process).

Over time, I improved my setup by using the WebSphere connector headers to cause Domino to view proxied requests as if they were coming directly, using SNI to allow multiple distinct SSL certificates on a single host, adding GeoIP headers at the nginx level, and setting up a sticky-session load balancer to share access between multiple servers and silently fail over when one goes down.

I'm now at the point where I pretty much consider it irresponsible to not use a reverse proxy in front of a production Domino deployment.

Is setting this up as easy as just giving ports 80 and 443 to Domino? Nope, not at all. Is it difficult, though? Not really. I managed to set it up readily, and I'm no admin. Other than a few rough edges IBM should shave off of Domino (such as using the faux-SSL header switching to the crummy single-Internet-Site behavior), I've had the stated benefits and more of an IHS deployment for a long time and without having to use Windows in production. I strongly recommend that everyone view Domino as not a web server - instead, it's a back-end HTTP app server that can serve as a makeshift web server for development, but is best deployed behind a proper front end.

And totally coincidentally, I'll be giving a session on this very topic at MWLUG later this month (in the Open Source track). I'll specifically be discussing nginx, but the same principles would apply with other viable choices like Apache, IHS, or IIS.