<< May 2012 | Home | July 2012 >>

IBM Connections API tip - override Content-Type header

When working with the IBM Connections REST API all responses are returned with the application/atomsvc+xml Content-Type which makes it not show up natively in the browser (or at least in Firefox). A quick solution is the install the Force Content-Type extension and change the Content-Type to text/xml on the fly to make the result show up in the browser. Rules are quick and easy to define.

It's been quiet around here - what have I been up to?

Well it has been very quiet on my front both on the blogging side and on twitter. I've not been lazy but just have had a lot of stuff on my plate the last couple of months. It all started with a very productive trip to Japan and following that AusLUG to catch up with friends and speak at that user group. Businesswise Japan was especially great and we have hooked up with a new OnTime partner out there. Axcel Coporation is our new spearhead into the Japanese market and they are making great progress and already have an ontimesuite.com-like website available in Japanese (ontimesuite.jp) complete with trials, support and online buying all in Japanese. Impressive.

We've received a lot of very nice press coverage in Japan with the latest piece coming this weekend where Mr. Muneyuki Ohkawa of IBM Lotus Japan wrote a very nice summary of OnTime Group Calendar and how he sees it in the Japanese market (Japanese: ノーツのグループカレンダー - OnTime Group Calendar, Google Translated into English: OnTime Group Calendar - Group Calendar of Notes).

Besides our Japanese venture I also spent quite a lot of time working on opening up the API for OnTime Group Calendar (more information here). Part of opening up the API has been recording videos on the usage as well as writing samples as well as completing the API Explorer I started writing in a session at BLUG. Don't say you cannot get inspired everywhere! :) The API Explorer (demo.ontimesuite.com/apiexplorer) is interesting from a number of perspectives. First of all it affords easy access to the API and easily allows you to try out the API without installing it in your own environment. It's also written as a web application without any hardcoded piece of information. Every endpoint and every operation and argument for the operations are fully dynamic and read from a JSON file allowing up to easily extend and update the API Explorer when we need to. Very nice if I may say so myself.

As if that wasn't enough I've been very busy with the next release of the plugin based UI's for OnTime Group Calendar programming a sidebar app (Team-At-A-Glance, out in beta currently), adding private group support, adding meeting and allday appointment support to the Notes 2011 UI. I've also completed porting the UI's to use the newer JSON based OnTime API. Oh and of course finalizing the Japanese support in both clients by fully supporting the Imperial Calendar system. It's all been great fun.

For the next two weeks I'm doing an IBM Connections widget project which I'm looking very much forward to. It is going to be very cool to do some web and JavaScript development and it will give me an opportunity to use my iWidget Test Harness framework which allows me to easily develop and test iWidgets without running a full IBM Connections or Mashup server - oh yeah - wrote that too... :-)

Now back to work - happy coding!!

IBM still in the lead

I was pleasantly surprised to again see that IDC for the third year running is ranking IBM as the leader for enterprise social software. As a IBM business partner we too are seeing the increased demand for this transformational software although implementation and realization of the potential is oftentimes hard in smaller companies without the budget to drive adoption with community managers or evangelists. It will be interesting to follow this market and see when and if competitors will catch up. For now - good job IBM!!

Controlling the feature install location

When installing plugins (that is Eclipse features) into Lotus Notes and you're running a roaming environment you kind of have a problem. The problem is that the only part of the plugins that roam are the plugin settings (i.e. should the Sametime come of front on new IMs) but not the actual code for the plugin. That means that when a user roams to a new machine the plugin isn't installed and hence the user will experience that the plugin will start installing and prompt for a restart. This is because plugins are installed in what's termed the "user" location (ie. your workspace directory) and not in a shared location.

An alternative is to install the plugins in the "shared" location instead (on my laptop that would mean that features would go into C:\Notes8\framework\shared\eclipse\features instead of C:\Notes8\data\workspace\applications\eclipse\features). The "Controlling the feature install location" article from the Lotus Expeditor info center has a nice description on the 3 possible locations.

To do this you use what's called the colocation affinity of a feature and specify that in the feature.xml describing the feature (the feature.xml can be found inside the jar-file that makes up a feature). To do this you have two options - 1) edit the existing feature.xml and set the "colocation-affinity"-attribute of the top level "feature"-tag or 2) set it using the GUI editor when you build your feature. Option 2 is for those doing their own plugins and option 1 for those changing the colocation affinity for a 3rd party plugin.

Below is a screenshot of setting the colocation affinity to "com.ibm.rcp.site.anchor.shared.feature" driving the feature to the shared location using the GUI editor in Eclipse.

Please note that installing into the shared location require read/write access in the file system for that location.

Tags :

The easy way to loop an XPage managed bean

I got harassed by Nathan (hmmm his blog appears to be down just now) yesterday for not contributing to the XPages dicussions out there so I thought I'd better remedy that. A quick tip is to make sure your Managed Bean implements Iterable if it contains a list of "things" and the obvious behaviour would be to loop over these things. If you implement Iterable you simple loop on the object using the "new" for-loop (when in Java) instead of first returning a List or array and then looping that with a for- or while loop.

Below is an example of a Managed Bean (just a Java-element from 8.5.3) that implements Iterable and hence it implements the iterator() method. That method should return an Iterator for the data you want to expose. Here I just return an iterator to the underlying list.

package com.example;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.Session;
import lotus.domino.View;

import com.ibm.domino.xsp.module.nsf.NotesContext;

public class PeopleLister implements Iterable {
  // declarations
  private List people = new LinkedList();
  
  public PeopleLister() {
    NotesContext ctx = NotesContext.getCurrent();
    Session session = ctx.getCurrentSession();
    try {
      Database db = session.getDatabase(null, "names.nsf");
      View view = db.getView("People");
      Document doc = view.getFirstDocument();
      while (null != doc) {
        this.people.add(doc.getItemValue("FullName")
          .elementAt(0).toString());
        Document docTemp = view.getNextDocument(doc);
        doc.recycle();
        doc = docTemp;
      }
    } catch (Throwable t) {
      
    }
  }
  
  public Iterator iterator() {
    return this.people.iterator();
  }

  public String toString() {
    StringBuilder b = 
      new StringBuilder(this.people.size() * 25);
    for (String p : this) {
      if (b.length() > 0) b.append(',').append(' ');
      b.append(p);
    }
    return b.toString();
  }
  
  public String[] getList() {
    return (String[]) people
      .toArray(new String[people.size()]);
  }
}

With that in mind I can just use the bean as follows from SSJS code (e.g. a Label):

var people = new com.example.PeopleLister();
var result = "";
for (p in people) {
  if (result.length() > 0) result += ", ";
  result += p;
}
return result;
Happy coding!

Using the IBM Connections API from other languages using custom certificates

I was reading up on some stuff in the IBM Connections REST API during the weekend and came across a post titled Using IBM Connections API in different programming languages on how to use the REST API from other languages than JavaScript from within IBM Connections. The approach there is very nice and quite valid but it fails to mention what to do if the SSL certificate of the API endpoint either isn't trusted or isn't certified using a "known" root certificate. In this case "known" means to the Java runtime you're using or the runtime of any other language for that matter. Here I'm only dealing with Java though.

By default the java.net classes will not allow a SSL connections to a server using a unknown/untrusted certificate but there are ways around that. Of course the best is always to make sure that the certificate of the server may be validated by the Java keystore (including intermediate certificates) but for testing - or if you know what you're doing - simply ignoring the certificate test can be beneficial. Below is some code showing how to configure the SSL runtime to ignore the certificate and hostname checks. The code is a static configuration method and I deem it pretty readable. The code allows *all* certificates but could pretty easily be locked down to be more restrictive if need be.

private void enableSelfSignedCerts() throws Throwable {
  TrustManager[] trustAllCerts = new TrustManager[] { 
    new X509TrustManager() {
      public java.security.cert.X509Certificate[] 
        getAcceptedIssuers() {
        return null;
      }
      public void checkClientTrusted(X509Certificate[] certs,
          String authType) {
      }
      public void checkServerTrusted(X509Certificate[] certs,
        String authType) {
      }
    }
 };

 SSLContext sc = SSLContext.getInstance("SSL");
 sc.init(null, 
         trustAllCerts, 
         new java.security.SecureRandom());
  HttpsURLConnection.
    setDefaultSSLSocketFactory(sc.getSocketFactory());

  // Create all-trusting host name verifier
  HostnameVerifier allHostsValid = new HostnameVerifier() {
    public boolean verify(String hostname, 
      SSLSession session) {
      return true;
    }
  };
  
  // Install the all-trusting host verifier
  HttpsURLConnection
    .setDefaultHostnameVerifier(allHostsValid);
}