« Back to home

Cookies with XPages

My first XPages app is coming along nicely. Today I needed to add some cookie support, and discovered that the otherwise comprehensive Mastering XPages had no information on the topic. There’s a post on the Lotus Developer Domain, but it’s a bit vague and makes the whole thing look harder than it really needs to be.

I started by writing myself a quick Server Side JavaScript library to encapsulate the details. As always, I’m using the Revealing Module Pattern. Here’s the code:

var Cookies = (function () {
 /**
  * Get a cookie value as a Cookie object.
  */
 var get:javax.servlet.http.Cookie = function (cookiename:string) {
   return cookie.get(cookiename);
 },

 /**
  * Convenience method to get a cookie value as a string.
  */
 getString:string = function (cookiename:string) {
   return cookie.get(cookiename).getValue();
 },

 /**
  * Set a cookie value. Optional max age is in seconds.
  * Must be called during at least a partial update.
  */
 set = function (cookiename:string, value, maxagesecs:double) {
   var xcon = facesContext.getExternalContext();
   resp = xcon.getResponse(), cmap = xcon.getRequestCookieMap(), ck;
   if (cmap.containsKey(cookiename)) {
     ck = cmap.get(cookiename);
     ck.setValue(value);
   } else {
     ck = new javax.servlet.http.Cookie(cookiename, value);
   }
   if (typeof maxagesecs !== 'undefined') {
     ck.setMaxAge(maxagesecs);
   }
   resp.addCookie(ck);
 };

 return {'get': get, 'set': set, 'getString': getString};
}());

Note that for fetching cookie values in my get() method, I use the predefined cookie variable we’re handed by the XPages runtime, rather than fetching them raw via getHeader(). JSF has already parsed all the cookies out into individual objects, so it would be crazy not to take advantage of that.

The set() method is a bit more complicated, because if you already have a cookie set, you only want the method to change its value (and optionally its maximum age), rather than creating a whole new cookie and hence losing the other properties.

Finally, I added a getString() convenience method to cover the 99% of the time when you just want to fetch the cookie’s value and don’t care about any of the other properties of the Cookie object. As far as I can tell from the Chrome debugger, XPages sets the domain and path to sensible values by default. Oh, and as far as I can tell the above code works, but it’s a Friday and it has been pretty lightly tested, so no guarantees…

So, with this SSJS library loaded, you can give a control on a web page a default value from a cookie:

<xp:comboBox id="locationComboBox">
  <xp:this.defaultValue><![CDATA[#{javascript:
    var locid:string = Cookies.getString("Location");
    locid.length === 11 ? locid : '';} ]]>
  </xp:this.defaultValue>
  …

The length check I’m doing there is to make sure the value looks like a Notes/Domino short unique ID, as produced by @Unique. To set a value in an onChange event:

<xp:eventHandler event="onchange" submit="true" refreshMode="partial">
  <xp:this.action>
    <xp:executeScript>
      <xp:this.script><![CDATA[#{javascript:
        Cookies.set("Location", 
        getComponent('locationComboBox').getValue(), 86400);}]]>
     </xp:this.script>
    </xp:executeScript>
  </xp:this.action>
</xp:eventHandler>

The 86400 being a day’s worth of seconds. Note that since the library is server-side JavaScript, you need to call it as part of an event that causes an HTTP connection, such as a partial refresh. If you want to set cookies without any HTTP round trip, you’ll need to use a completely different JavaScript API, the one provided by the browser.