Life in Other Worlds

Tue Mar 25 20:17:58 EDT 2014

I've found the reaction to BlueMix to be pretty interesting. Unsurprisingly, the main question in our community has been "how does this relate to Domino?" The answer is pretty clear: it doesn't.

That's not really a bad thing! About half of the problems a platform like BlueMix solves - app packaging, multi-server availability, data-store access - are either non-issues on Domino or are done much more easily with it than with other app servers. The other advantages, well... IBM could likely shoehorn Domino into a service like that if they cared to, but they clearly don't.

Instead, I think this is a good opportunity to look at BlueMix for what it is (or appears to be from my only-partially-informed perspective): a competitor to Heroku and (half of) Windows Microsoft Azure. This is a great thing, because those platforms are amazing. What they do is one conceptual level above what someone like Linode does: in addition to abstracting away the physical machine your app is hosted on, they also abstract away the OS and server stack, presenting you (the developer) with an idealized world free of sysadmins and DBAs.

The best way to get a feel for what this means is to embrace it on its own terms: pick an environment wholly unrelated to Domino, close Notes and Designer, and try out one of the supported frameworks. It should come as no surprise that I find Ruby on Rails to be a perfect way to do this. Specifically, Michael Hartl's Rails tutorial not only brings you up to speed with modern Rails programming, but it also provides an introduction to Heroku, showing you how to use their free tier to publish your apps using Git (seriously, Heroku uses Git to publish - it's amazing). Node.js is also absolutely worth your time: it's a programming environment unlike any other and gives you an excellent new perspective on client/server interaction on the web. It looks like BlueMix doesn't do Python or PHP, which is just as well. Please don't use PHP.*

Particularly if it's been a while since you did any non-Domino or non-Java development, it could be a very useful experience - different ways to structure apps, different tools (no need for Eclipse, for example), different conceptual models of programming. You may not stick with it (for all my bluster about Ruby, I'm still writing Domino apps), but it'll keep your brain healthy. Too much of any one type of programming makes you calcified and causes you to miss out on the rest of the world. I know I, for one, plan to do more of this type of exploratory exercise.

* One of the greatest features of these app platforms is that they solve one of the main problems keeping people on PHP: it's just so freaking easy to deploy an app written in PHP, whereas deploying a Ruby/Java/Python/etc. app has traditionally been much harder. By abstracting away the app server, the playing field is leveled and you can choose the better tool.

SNTT: Reconstructing DateRanges

Thu Feb 27 09:53:46 EST 2014

Tags: sntt

In the OpenNTF API chat, I was reminded yesterday of how terribly broken DateRange retrieval is in Java. Specifically, though you can create date ranges and use them in view lookups, retrieving them from documents or view entries is FUBAR. To wit:

Field Value 02/27/2014, 02/28/2014, 02/27/2014 - 02/28/2014, 01/01/2014 12:00 AM - 01/02/2014 12:00 PM
doc.getItemValue("SomeField") [02/27/2014, 02/28/2014, 02/27/2014, 02/28/2014, 01/01/2014 12:00:00 AM EST, 01/02/2014 12:00:00 PM EST]

Note: this is consistent with LotusScript
doc.getItemValueDateTimeArray("SomeField") [02/27/2014, 02/28/2014, null, null]
session.evaluate(" SomeField ", doc) [02/27/2014, 02/28/2014, 02/27/2014, 02/28/2014, 01/01/2014 12:00:00 AM EST, 01/02/2014 12:00:00 PM EST]
entry.getColumnValues().get(columnIndex) [02/27/2014, 02/28/2014, 02/27/2014, 02/28/2014, 12/29/**** 12:22:46 PM EST, 10/03/**** 04:53:38 PM EDT]

Note: the final values are inconsistent across multiple executions

So it appears that getItemValueDateTimeArray chokes and gives up when it encounters date ranges, instead just plopping a null value in the Vector. View entries appear to wander off into some invalid-data wilderness, perhaps not properly parsing the ITEM_VALUE_TABLE attached to the entry. Or maybe it's in the ITEM_TABLE. I don't remember; it's complicated. In any event, view entries are a mostly-lost cause.

Document item values, though, are salvagable. Since getItemValue returns the start/end values and getItemValueDateTimeArray returns nulls in the spots that represent DateRanges (which are always at the end, but that's incidental), the two can be matched up to reconstruct the real value. I wrote a method to do just this - it doesn't do any checking to make sure that the item value actually contains DateTimes (and would throw a ClassCastException if it contains something else), it should do the job when you know the item contents:

@SuppressWarnings("unchecked")
private List<Base> noSeriouslyGetItemValueDateTimes(final Session session, final Document doc, final String itemName) throws NotesException {
	List<Base> result = new Vector<Base>();

	List<DateTime> dateTimes = doc.getItemValue(itemName);
	List<DateTime> dateTimesArray = doc.getItemValueDateTimeArray(itemName);
	int foreignIndex = 0;
	for(DateTime dt : dateTimesArray) {
		if(dt != null) {
			result.add(dateTimes.get(foreignIndex++));
			dt.recycle();
		} else {
			DateTime dt1 = dateTimes.get(foreignIndex++);
			DateTime dt2 = dateTimes.get(foreignIndex++);
			DateRange range = session.createDateRange(dt1, dt2);
			result.add(range);
		}
	}

	return result;
}

I'll probably add a similar method to the OpenNTF API (or maybe fix getItemValueDateTimeArray), but this should do for now.

My Current Model Framework, Part 2: An Example

Fri Feb 21 15:27:19 EST 2014

Tags: xpages mvc
  1. My Current Model Framework, Part 1
  2. My Current Model Framework, Part 2: An Example

To go along with the updated release of my Scaffolding project yesterday, I've created an example database created with that template and containing a basic use of my model framework:

model-framework-example.zip

This demonstrates a few things about the framework:

  • Setting up the two classes that go with each object type - the object itself (e.g. model.Department) and the "manager" class that handles fetching objects and collections (e.g. model.DepartmentManager) and adding the collections to faces-config.xml.
  • Tying the manager class to the views in a database using views with a common prefix. In this case, they point to the current database, though I recommend storing the data in another DB.
  • Adding relations between objects. A Person object is tied to a given Department by way of the getDepartment method, while a Department lists its people via the getPeople method.
  • Using the collections on an XPage with xp:viewPanel controls, including showing "columns" that are not in the view - the objects automatically fetch from the document when the column isn't present (which is inefficient but useful sometimes).
  • Attaching a model object to an XPage using a xp:dataContext and using a controller class to save it.

I have a few ideas in mind to improve day-to-day use of the model framework (for example, I may set up proper data sources for individual objects so they can tie into the normal save events), so they may change over time, but this is how I develop most apps today.

Couchbase StateManager for XPages, Part 2

Fri Feb 21 10:17:33 EST 2014

  1. Couchbase StateManager for XPages, Part 1
  2. Couchbase StateManager for XPages, Part 2

I had the opportunity to dive a little back into the StateManager I wrote the other week to try out a theory. The first thing I noticed was that there was a minor problem: it didn't work at all. Specifically, the code I had didn't actually store the whole proper state of the view, so it ended up being regenerated anew each time; the fact that this didn't cause an error was what led me astry.

Fortunately, I was able to scrounge together enough code to get it to work properly, bringing the tree and viewScope along for the ride:

CouchbaseStateManager.java

The impetus of revisiting this was to test whether this setup would be enough to accomplish a fun goal: by having the view state serialized to a commonly-available location, you should be able to switch servers in between actions (e.g. partial refreshes) and still have it work. And indeed, it does! I set up a round-robin load balancer for two of my Domino servers, which more or less switches between Arcturus and Ceres on each hit. In my test page, you can see a refresh count (stored as an instance member of the page's controller class, which is in viewScope) incrementing when you click the button:

https://roundrobin.frostillic.us/tests/ccluster.nsf

You can also see a value stored in "clusterScope", which is an object pointing to the same Couchbase cluster, and so it is similarly available on both servers.

This is pretty promising! It's not the sort of thing you'd do with any old app (importantly, applicationScope and sessionScope are not shared between the servers), but by pairing this with SSO, you could make an app that is resilient and scalable, with an arbitrary number of app servers behind a rotating load balancer without the need of sticky sessions.

Domino the Identity Server

Tue Feb 11 09:10:16 EST 2014

Tags: domino

As seems to happen a lot lately, my fancy was struck earlier by a Twitter conversation, this time about the use of Domino as a personal mail server. Not only do I think there's potential there, but it should go further and be a drop-in replacement for personal mail, calendar, and contacts storage.

I think there's tremendous value in controlling your own domain and the services on it, without being permanently attached to someone else's name for an email address (your school, your company, your ISP, Google). This is good not only for personal freedom, since it lets you pack up and move at will, but also for security, since a large third-party mail service is a particularly juicy target.

Unlike Domino's inherited-but-abandoned place as the preeminent NoSQL server and replicating app-dev platform, Domino is just barely shy of being this server. You could already hit the three main services reasonably well by using the Notes client or iNotes, but actual humans shouldn't have to do that. It's already (more or less) there for mail with IMAP, while support for CardDAV for contacts and CalDAV+public iCalendar feeds would round it out for the other two pillars. Technically, the only things standing in between the existing open-source WebDAV plugin for Domino and this imagined future are the complexities of plugin development and the RFC.

The other main aspects that could make this great are further refinements to Domino's existing capabilities: a bundled spam filter (say, one of the open-source tools that can do the job already) and a strong configuration focus on creating an SSL-secured public-facing server. Non-SSL variants of IMAP, POP (if you must - that could be removed entirely), and HTTP should be off by default and the configuration should encourage you to acquire SSL certificates with your own private keys (the Server Certificate Admin would need a revamp for proper key size and ciphers), as well as S/MIME certificates to tie with each user for signed/encrypted mail outside the Notes client. Though Domino's history with the NSA is... checkered, it's still remarkably well-positioned to provide a secure foundation to deter snooping eyes. Certainly, running a Domino server own your own or a rented/virtual server is leagues better than a fully-managed service in this respect.

Having those features in place and smoothly integrated with a nice setup assistant would make for a very compelling product: an all-in-one, easy-to-install server that runs on several modern OSes and handles secure replication across physical locations at already-actually-affordable prices. Admittedly, I don't know how compelling that product would be for IBM's accountants, but it's certainly compelling for me, and the world could use more decentralization like this.

Couchbase StateManager for XPages, Part 1

Wed Jan 29 15:08:55 EST 2014

  1. Couchbase StateManager for XPages, Part 1
  2. Couchbase StateManager for XPages, Part 2

The other day, I attended Tony McGuckin's Connect session on XPages scalability (a companion to his earlier Masterclass on performance) and I was inspired to try out a thought I had: could it work to use Couchbase to further push the bounds of XPages scalability?

If you're not familiar with Couchbase, it's a product similar to CouchDB, which is in turn essentially Domino-the-DB-server re-done for modern needs. Last summer, I wrote some data sources to use Couchbase data in an XPages app in much the same way that you use Domino documents and views.

In addition to Couchbase's nature as a document database, it has another layer from its lineage as a continuation of Membase: its basis is a key-value store, suitable for caching or storing data in a very fast and scalable way. This capability is potentially a perfect match for the needs of XPages state holding: when an XPage is stored in between requests, it's really just taking a Java object and storing it by key either in memory or on disk. By taking Couchbase's key-value capability and using it for this, you end up with something that may (pending testing) really crank up the scalability.

So I wrote a class to do it, and so far, so good: it stores and retrieves the XPages properly and even automatically obeys session timeout to clean up unneeded entries. When I get home, I plan to convert it into a plugin, but for now I've uploaded the Java file:

CouchbaseStateManager.java

To use it, install the Couchbase Java Client (I dropped it in jvm/lib/ext, since adding them as Jars in the NSF was trouble), copy that class into your NSF in the "frostillicus" package, and modify your faces-config.xml and xsp.properties files similar to:

faces-config.xml

<faces-config>
  <application>
    <state-manager>frostillicus.CouchbaseStateManager</state-manager>
  </application>
  ...
</faces-config>

xsp.properties

frostillicus.couchbase.statemanager.uris=http://10.211.55.2:8091/pools
frostillicus.couchbase.statemanager.bucket=default
frostillicus.couchbase.statemanager.username=
frostillicus.couchbase.statemanager.password=

Once you have it up and running, you'll start seeing serialized views being stored in your Couchbase bucket (which is basically like an NSF) keyed by replica ID and the unique XSP view ID:

I'm looking forward to trying this out in a real setup, where I have the XPage running on one server and pointed at a cluster of multiple Couchbase servers elsewhere. This should really play to the strengths of both: it would use JSF's capabilities to take a little work off of the plate of the XPage server while letting Couchbase do its job of spreading the data across its cluster, caching in memory, and so forth. Barring unfixable performance pitfalls, this could push XPages performance and scalability even further.

"Build Automatically", "Refresh automatically", and "Team Development"

Thu Jan 23 18:06:26 EST 2014

TL;DR: "Build Automatically" and "Refresh automatically" are unrelated, but work together when both enabled when using SourceTree for source control.

I just had a conversation on Twitter where I went a little crazy talking about my hopefully-correct view of how the various sync processes in Designer work when you're dealing with source control.

The original impetus was a post by Mark Leusink about the fact that you have to build your NSF project in order to sync it with an on-disk-project (ODP) set up via Team Development. From there, that got into the topic of the "Refresh automatically" Eclipse setting that is helpful when using source control tools outside of Designer (e.g. SourceTree instead of eGit). The problem is that this stack of builders and settings is hard to get a hold of conceptually.

There are three "entities" to know about when it comes to how these things work together, along with a couple processes that can get your source code from one to the other. I tossed together this terrible image to visualize them:

I'll work from right to left, starting with the NSF. That part is nothing new, but the thing to know is how the NSF "knows" how to sync to an ODP. It's done via something called an Eclipse Builder. A Builder is a task you can attach to a project in Eclipse to do essentially arbitrary things when the build process happens for a project (or an individual part of a project). NSFs have a ton of them:

The one in question is the unnamed one at the top (don't ask me why it's nameless): that's the Builder that exports your changes from the NSF to the ODP. There's a companion Builder, also unnamed, in the ODP, that does the reverse when you make a change to the files there (normally you don't, but source control does). Because this sync is implemented with a Builder, it doesn't run automatically when you have "Build Automatically" disabled. Instead, it will run when you manually do a "Build All" or "Clean", or when you choose "Team Development" → "Sync with On-Disk Project" from the application list.

So the Builder handles all of the syncing between the ODP and the NSF. Where does "Refresh automatically" (which is very different from "Build Automatically") come in, and why has it only become a thing recently with the cultural shift away from eGit and towards SourceTree?

The culprit is the way Eclipse/DDE deals with the filesystem. Because OSes have traditionally been bad at notifying programs of when files change on disk, DDE doesn't actually know when a file is changed on disk. This is expected for something like an NSF (say, if someone else adds a view to an NSF you have open, it's no surprise that you have to refresh to see it in Designer (also, don't edit the same NSF from two places like that)), but counterintuitive with local files. The view of the ODP you see in Designer's Package Explorer is analogous to running an "ls" or "dir" command in the terminal and then adding a new file: the output from your command won't change until you run it again. What "Refresh automatically" does is attempt to keep its file listing up-to-date. Later versions of Eclipse modified this option to be more explicit and technical:

The upshot is that turning this on will (hopefully) keep the ODP in step with the filesystem. The chart I made is misleading here: the ODP and filesystem are technically the same storage area, but the ODP you see in DDE is really a listing of what Designer saw the last time it looked.

This is useful when using a tool like SourceTree to handle your source-control operations. Because DDE doesn't normally "know" about filesystem changes, you could update your local source repository from the server with tons of changes, but Designer wouldn't know until you kicked it. With "Refresh automatically", it should notice those changes on its own, update the ODP in Package Explorer, and, if "Build Automatically" is on, sync those changes into your NSF.

The reason this matters now and didn't/doesn't when using eGit is that, because eGit is an Eclipse plugin and does its filesystem access through Eclipse, Designer already knows when a source-control-related operation changes files on the filesystem: since it made the change, it doesn't need to refresh. So if you're using an Eclipse plugin for source control and not editing files on the filesystem manually, you can remain in blissful ignorance about "Refresh automatically."

Data separation with an asterisk

Thu Jan 09 09:50:59 EST 2014

Tags: xpages

A little while ago, I converted over to the practice of separating your XPage app from the NSF that contains the data, even when it's going to be a one-to-one mapping. This confers a number of advantages in the areas of easier testing, easier development, programming discipline, and security. One of my favorite aspects is the ability to do this on your data DB:

Don't allow URL open

This alone adds tons of security: no more worrying about ?ReadViewEntries, hiding your views from the web, errant forms allowing too much access, and so forth. Once you have that checked and you funnel your access through a separate app, the surface of vulnerability is much, much smaller.

But there's a hitch.

I discovered this the other day, and it's only a problem in mixed web/Notes environments: when you have that option enabled, you can't see native rich text content from a document in your XPage. I say "native" to refer to the traditional Composite Data format of rich text; MIME content is fine. The reason for this appears to go down to the C-API level, where setting that flag seems to entirely bar the HTML conversion functions from working with that database.

My guess is that that was deemed the most expedient way to implement the feature at the time, but it's problematic now. I can think of a couple ways to deal with it:

  • Don't use native rich text. If you're writing whole-cloth XPages apps, you don't have to worry about this at all, since MIME/HTML content is still fine. If you have a mixed app and you're fine with some ugly-looking text in the Notes client, you can turn on the field option to store the data as MIME even in the client.
  • Store the app on separate servers. To maintain the security benefits, you could store the data database on a different server, one not publicly accessible via HTTP.
  • Disable that option and use web rules instead. You should probably be able to achieve a lot of the security benefits by substituting all HTTP requests for the data database with blank pages, though that requires coordination of the server config with every DB like this, which is a drag. Also don't forget __$replicaid.nsf URLs.

Fortunately, the fact that it only affects conversion from native rich text to MIME means the impact is limited, but it's still a caveat to data separation.

A Quick, Dirty, and Inefficient @ManagedBean Implementation in XPages

Tue Dec 17 09:24:31 EST 2013

Tags: xpages java

By now, most of us are pretty familiar with the process for adding managed beans to an XPages app: go to faces-config.xml and add a <managed-bean>...</managed-bean> block for each bean. However, JSF 2 added another method for declaring managed beans: inline annotations in the Java class. This wasn't one of the features backported to the XPages runtime, but it turns out it's not bad to add something along these lines, and I decided to give it a shot while working on the OpenNTF API.

The first thing that's required is a version of the annotations that ship with JSF 2. Fortunately, annotations in Java, though ugly to define, are very simple, and each one involves only a few lines of code and can be added to your NSF directly:

javax.faces.bean.ManagedBean

package javax.faces.bean;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(value=RetentionPolicy.RUNTIME)
public @interface ManagedBean {
	String name();
}

javax.faces.bean.ApplicationScoped

package javax.faces.bean;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(value=RetentionPolicy.RUNTIME)
public @interface ApplicationScoped { }

javax.faces.bean.SessionScoped

package javax.faces.bean;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(value=RetentionPolicy.RUNTIME)
public @interface SessionScoped { }

javax.faces.bean.ViewScoped

package javax.faces.bean;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(value=RetentionPolicy.RUNTIME)
public @interface ViewScoped { }

javax.faces.bean.RequestScoped

package javax.faces.bean;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(value=RetentionPolicy.RUNTIME)
public @interface RequestScoped { }

javax.faces.bean.NoneScoped

package javax.faces.bean;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(value=RetentionPolicy.RUNTIME)
public @interface NoneScoped { }

The more-complicated code comes into play when it's time to actually make use of these annotations. There are a couple ways this could be handled, and the route I took was via a variable resolver. Note that this code requires my current branch of the OpenNTF API, though a mild variant would work with the latest M release:

resolver.AnnotatedBeanResolver

package resolver;

import java.io.Serializable;
import java.util.*;
import javax.faces.context.FacesContext;
import javax.faces.el.EvaluationException;
import javax.faces.el.VariableResolver;
import javax.faces.bean.*;

import org.openntf.domino.*;
import org.openntf.domino.design.*;

import com.ibm.commons.util.StringUtil;

public class AnnotatedBeanResolver extends VariableResolver {

	private final VariableResolver delegate_;

	public AnnotatedBeanResolver(final VariableResolver resolver) {
		delegate_ = resolver;
	}

	@SuppressWarnings("unchecked")
	@Override
	public Object resolveVariable(final FacesContext facesContext, final String name) throws EvaluationException {
		Object existing = delegate_.resolveVariable(facesContext, name);
		if(existing != null) {
			return existing;
		}

		try {
			// If the main resolver couldn't find it, check our annotated managed beans
			Map<String, Object> applicationScope = (Map<String, Object>)delegate_.resolveVariable(facesContext, "applicationScope");
			if(!applicationScope.containsKey("$$annotatedManagedBeanMap")) {
				Map<String, BeanInfo> beanMap = new HashMap<String, BeanInfo>();

				Database database = (Database)delegate_.resolveVariable(facesContext, "database");
				DatabaseDesign design = database.getDesign();
				for(String className : design.getJavaResourceClassNames()) {
					Class<?> loadedClass = Class.forName(className);
					ManagedBean beanAnnotation = loadedClass.getAnnotation(ManagedBean.class);
					if(beanAnnotation != null) {
						BeanInfo info = new BeanInfo();
						info.className = loadedClass.getCanonicalName();
						if(loadedClass.isAnnotationPresent(ApplicationScoped.class)) {
							info.scope = "application";
						} else if(loadedClass.isAnnotationPresent(SessionScoped.class)) {
							info.scope = "session";
						} else if(loadedClass.isAnnotationPresent(ViewScoped.class)) {
							info.scope = "view";
						} else if(loadedClass.isAnnotationPresent(RequestScoped.class)) {
							info.scope = "request";
						} else {
							info.scope = "none";
						}

						if(!StringUtil.isEmpty(beanAnnotation.name())) {
							beanMap.put(beanAnnotation.name(), info);
						} else {
							beanMap.put(loadedClass.getSimpleName(), info);
						}
					}
				}

				applicationScope.put("$$annotatedManagedBeanMap", beanMap);
			}

			// Now that we know we have a built map, look for the requested name
			Map<String, BeanInfo> beanMap = (Map<String, BeanInfo>)applicationScope.get("$$annotatedManagedBeanMap");
			if(beanMap.containsKey(name)) {
				BeanInfo info = beanMap.get(name);
				Class<?> loadedClass = Class.forName(info.className);
				// Check its scope
				if("none".equals(info.scope)) {
					return loadedClass.newInstance();
				} else {
					Map<String, Object> scope = (Map<String, Object>)delegate_.resolveVariable(facesContext, info.scope + "Scope");
					if(!scope.containsKey(name)) {
						scope.put(name, loadedClass.newInstance());
					}
					return scope.get(name);
				}
			}
		} catch(Exception e) {
			e.printStackTrace();
			throw new RuntimeException(e);
		}

		return null;
	}

	private static class BeanInfo implements Serializable {
		private static final long serialVersionUID = 1L;

		String className;
		String scope;
	}
}

faces-config.xml

<faces-config>
	...
	<application>
		<variable-resolver>resolver.AnnotatedBeanResolver</variable-resolver>
	</application>
</faces-config>

This is not efficient! What happens is that, whenever a variable is requested that the main resolver can't find (or is null), the code goes through each Java class defined in the NSF (not in plugins, the filesystem, or in attached Jars) and checks if it contains the @ManagedBean definition. Fortunately, the worst of this is only incurred the first time (according to the duration of applicationScope), but that one-time process is still something that would get linearly slower as the number of Java resources in your app increases.

Now, there are still other features of the actual JSF 2 implementation that this doesn't cover, particularly @ManagedProperty, but it can still be useful on its own. In practice, it lets you ditch the faces-config declaration and write your beans like this:

package bean;

import javax.faces.bean.*;
import java.io.Serializable;

@ManagedBean(name="someAnnotatedBean")
@ApplicationScoped
public class AnnotatedBean implements Serializable {
	private static final long serialVersionUID = 1L;

	// ...
}

I plan to give this a shot in the next thing I write to see how it works out. And regardless of future use, it's a nice example of the kind of thing that gets easier with the OpenNTF API.