JavaAPI for Servertasks – Documented and Supported

It is very seldom that I feel so strongly about ideas in IdeaJam that I feel they are worth promoting further but this time I have to make an exception. I read a post on Bob Balfes blog about writing server addins and Domino server extensions as Eclipse plug-ins. How crazy and exciting would that be!! The possibility of developing truly cross platform extensions would be beyond words. And coming from a company that actually develop server tasks I can attest to the problems incurred by having to compile for Win32, Win64, AIX, Linux, Solaris, iSeries and zSeries. It’s inzane!

So head on over to IdeaJam and wrote for it – please!!

JavaAPI for Servertasks – Documented and Supported (login required)

Generating unique id’s for Notes widgets

If you write code to automatically generate widget descriptors (aka extension.xml) for users you have to ensure that the widget id is unique. A nice exxample of this can be found here. One caveat is that the widget id is used to distinguish the widgets hence has to be unique. To easiest way to generate a unique id in Java is to use the java.util.UUID class. Generating a unique, random, id with this class is easy.

String id = java.util.UUID.randomUUID().toString();

Don’t install Notes in the default location if doing Notes plug-in development

A little while back I was contacted by a fellow Yellow-head who had some issues with some plug-in development for Notes 8. Unfortunately I wasn’t able to help him at the time but when he some head scratching time later found the answer he was kind enough to share it. I thought I would share it here in case it can help someone.

From the offset the issue looked simple as it had to do with the good old nlsxbe which is normally caused by the binary Notes directory not being on the path. The issue was however a bit twisted as the error raised complained about the filename or extension being too long.

java.lang.UnsatisfiedLinkError: nlsxbe
   (The filename or extension is too long. )

What our good Yellow-head found out was that the issue was caused by his install location of the Notes client. Specifically he had installed his client in “C:Archivos de programaIBMLotusNotes” which is the default location for his OS. This however means that the path to the notes.jar for plug-ins becomes “C:Archivos de programaIBMLotusNotesframeworkrcpeclipsepluginscom.ibm.rcp.j2se.win32.x86_1.5.0.SR4-200707311521jrelibNotes.jar” which is too long a path (longer than 128 characters). Installing Notes in “C:program filesIBMLotusNotes” solved the issue.

So if you run into weird issues like this check your install path or choose an English copy of Windows! πŸ™‚

By popular demand – scaling images in Java for Lotus Connections

After blogging about how Lotus Connetions teaches you to scale images in Java the other day I was contacted by Lotus Support who really would like to see the code as customers were asking for such code. Mitch also forwarded me a response from Lotus Support where they referred to my blog post which I got a real kick out of… πŸ™‚

So here’s the code. Thanks to the customer for allowing me to blog the code. Use to your hearts content but don’t blame me if it doesn’t work for you. The disclaimer is there for a reason.

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOError;
import java.io.IOException;

import javax.imageio.ImageIO;

public class ScalePicture {

  public static void main(String[] args) {
    try {
      File f = new File("d:\images");
      File pictSrc = new File(f, "source_photo.jpg");
      File pictDest = new File(f, "destination_photo.jpg");
      if (pictDest.exists()) {
        System.out.println("deleting...");
        pictDest.delete();
      }

      // scale image
      BufferedImage img = scaleImage(pictSrc);

      // write to target image
      ImageIO.write(img, "jpg", pictDest);

    } catch (Throwable t) {
      t.printStackTrace();
    }
  }

  private static BufferedImage scaleImage(File source)
    throws IOException {
    // declarations
    final int width = 115;
    final int height = 115;

    // read source image
    BufferedImage imgSrc = ImageIO.read(source);

    // calculate scale factor
    double scaleFactor = (double)imgSrc.getWidth() /
      (double)width;

    // scale image
    BufferedImage imgScaled = new BufferedImage((int)
      (scaleFactor * 100), height,
      BufferedImage.TYPE_INT_RGB);
    AffineTransform transform = AffineTransform.
      getScaleInstance(scaleFactor, scaleFactor);
    Graphics2D g = imgScaled.createGraphics();
    g.drawRenderedImage(imgSrc, transform);
    g.dispose();

    // create new target image in correct size
    // with white background
    BufferedImage imgResized = new BufferedImage(width,
      height,
      BufferedImage.TYPE_INT_RGB);
    g = imgResized.createGraphics();
    g.setBackground(Color.WHITE);
    g.fillRect(0, 0, width, height);

    // calculate offset for scaled image on new image
    int xoffset = (int)((double)(width - imgScaled.getWidth()) /
      (double)2);

    // draw scaled image on new image
    g.drawImage(imgScaled, xoffset, 0, null);
    g.dispose();

    // return new image
    return imgResized;
  }

}

Lotus Connections teaches you how to scale images in Java

Lotus Connections is a little bit screwy when it comes to profile pictures IMHO as they are being forced to be square in Profiles (115×115 pixels). In profiles search results however they are scaled to 55 pixel in width and height is automatic…

In my mind portrait pictures are rectangular and not square.

Yesterday this gave me some grief as I was at a customer where I had to write Java code to import pictures in the PHOTO table of the Lotus Connections PEOPLEDB database. The actual importing the pictures into the database is easy using JDBC but the pictures showed up wrongly in Lotus Connections as they were rectangular (200 x 133 pixels). They clearly had to be scaled but how – clearly not manually!

As with many other things you are gifted with Java as it already contains all the pieces you need to scale pictures. I quickly found some sample code on Google to use java.awt for the resizing. The solution was to

  1. Scale the source image from 200 x 133 pixels to 115 x 76 pixels to keep the aspect ratio
  2. Create a new blank white image sized 115 x 115 pixels
  3. Place the resized source image on top of the white image centered
  4. Upload the resulting image to the database as a byte array

Love Java!

Show ‘n Tell Thursday: Configuring Ubuntu for Notes 8 plugin development (2 April 2009)


This weeks SnTT post is about configuring Eclipse on Ubuntu 8.0.4 for Notes plug-in development. I use Notes 8.5 but it should work equally well for Notes 8.0.x clients. Wan’t to develop plug-ins? Well read on and do it on Ubuntu. Notes 8.x runs freakishly fast on Ubuntu. Read on…

Actually this is a cheat post as the steps are almost the same as for doing it on Windows. Most of the stuff has to do with platform differences. My post for doing it on Windows is here. I have updated this guide with steps for Linux. Enjoy!!

On plug-ins, features, update sites and extension.xml files…

I’m receiving quite a few e-mails asking questions about features, plug-ins, update sites and extension.xml files and how they relate so I thought I would try and clarify things.

Term Description
Plug-in The smallest unit of code you use to create functionality for an Eclipse based client. This is where the actual Java code is.
Feature Used to package and bundle plug-ins together. Features are thin wrappers for plug-ins and is basically a single file called feature.xml. You can bundle multiple plug-ins into a single feature. When installing code into Notes you actually install the features which in turn point to the plug-ins to copy to the client. You can only manage features through the Notes “code UI” (File/Application/Application Management) though you can install code into the platform by simply copying the plug-ins into the appropriate directories in the file system. This is not recommended… πŸ™‚
Update site Update sites are used to deploy features to clients. An update site is simply a directory containing a

  • “plugins”-directory containing a jar-file per plug-in
  • “features”-directory containing jar-file per feature
  • site.xml file describing which features and plug-ins (and in what versions) are available on that particular update site

When an Eclipse based client contacts an update site it reads and parses the site.xml file to discover what’s available there.

Update Sites may be remote or local. A local update site is a directory on a local hard drive or LAN drive with the above structure or a zip-file with the above structure. An update site may also be remote and may be read using HTTP (any server will do) or it may be read using NRPC if you’re using a Notes 8+ client. When using NRPC you use the Update Site Notes database template.

extension.xml These files are used when installing code using the MyWidgets sidebar plug-in and is a shorthand for manually installing code. There is no magic at work here. When you drop an extension.xml file onto the sidebar panel the following steps are performed:

  1. The extension.xml file is parsed and verified to be a valid XML file
  2. The features to be installed are located and a dependency graph is assembled so any required features are identified
  3. The update site address specified in the extension.xml file is contacted and each missing feature in the dependency grapg is attempted installed “bottom up”
  4. The client is restarted

The technique for building a better Notes Java API

This is a followup post to yesterdays post titled Want to join me in building a better Notes Java API? and here I’ll show just how easy it was to build the wrapper API.

Below is the code from the implementation of the getView(String) method of the lotus.domino.Database interface. As you can see the central element is to get the current thread the method is called on and the thread used for Notes data access. If we’re not on the Notes thread execute a blocking request and return the result. If we’re on the Notes thread simply go ahead and do the operation. Simple right?

The most difficult part of all this was that I had to write my own Notes thread handler (equivalent of NotesPlatform) as there isn’t a method for doing a blocking request on the Notes thread in NotesPlatform class supplied by Lotus.

public View getView(final String name) throws NotesException {
  // get threads
  Thread curThread = Thread.currentThread();
  Thread notesThread = NotesPlatform.getInstance().getThread();

  // decide if we're in the Notes thread or not
  if (curThread != notesThread) {
    // we're not on the Notes thread so wrap
    final Holder h = new Holder();

    NotesPlatform.getInstance().syncExec(new Runnable() {
      public void run() {
        try {
          h.value = DatabaseWrapper.this.db.getView(name);

        } catch (NotesException e) {
          h.throwable = e;
        }
      }
    });

    if (null != h.throwable) {
      throw (NotesException)h.throwable;
    } else {
      return new ViewWrapper((View)h.value);
    }

  } else {
    // we're on the thread so do request
    return new ViewWrapper(DatabaseWrapper.this.db.getView(name));
  }
}

Want to join me in building a better Notes Java API?

One of the things that are the hardest for Notes Java developers transitioning to developing plug-ins for Notes 8 is handling threading. All access to the Notes backend classes must occur on the Notes thread using NotesJob or a thread statically initialized using NotesThread.sinitThread. This is cumbersome and prone to runtime exceptions making the development process slow and agonizing. Also most interaction is started from the UI thread making the need for handling the Notes thread all too common.

A far better approach, IMHO, would be to not require the programmer to handle the threading all together as there are solutions for this readily available. The solution isn’t hard to do.

In an effort to prove this point I sat down for an hour or so at Lotusphere (I know I’m behind in blogging this) and cooked up a wrapper API that wraps the Notes API and provides access to the Notes backend classes without the need to worry about threading. The Notes API is made up of interfaces which makes it even easier.

The whole concept with the wrapper is that the user can write code like this in an event handler

public void widgetSelected(SelectionEvent event) {
  try {
    // get value
    Session session = NotesPlatform.getInstance().getSession();
    Database db = session.getDatabase(null, "names.nsf");
    View v = db.getView("($People)");
    ViewEntryCollection vec = v.getAllEntries();

    // set value in label
    lblCount.setText("Found " + vec.getCount() + " contacts...");

    // return from event
    return;

  } catch (Exception e) {
    StringWriter sw = new StringWriter();
    e.printStackTrace(new PrintWriter(sw));
    txtException.setText(sw.toString());
  }
}

instead of doing stuff like this which includes two extra jobs and having to make sure that no Notes API access is done from the UI thread:

public void widgetSelected(SelectionEvent event) {
  // kick of Notes job to get data
  new NotesJob("Some job") {
    public IStatus runInNotesThread(ProgressMonitor m)
                               throws NotesException {
      // get value
      Session session = NotesPlatform.getInstance().getSession();
      Database db = session.getDatabase(null, "names.nsf");
      View v = db.getView("($People)");
      ViewEntryCollection vec = v.getAllEntries();

      // get count into final variable
      final int count = vec.getCount();

      // kick of new UI job to update UI
      new UIJob("Update UI") {
        public IStatus runInUIThread(IProgressMonitor monitor) {
          // set value in label
          lblCount.setText("Found " + count + " contacts...");
          return Status.OK_STATUS;
         }
      }.schedule();

      // return
      return Status.OK_STATUS;
    }
  }.schedule();

  // return
  return;
}

Besides being shorter code-wise the former code is also much easier to read and understand. The performance of the former is somewhat slower than the latter (“native” API) due to context switching but the fact that I could do this is 60 minutes proves that something can and should be done to make plug-in development easier. Imagine what could be done in more time. Tweaking for performance should be possible and easy enough. Also using this wrapper API could be a choice the developer makes – ease of use over performance… It could be a call I as a developer was willing to make.

This could actually be a good idea for a project on OpenNTF. If anyone is interested to join up on the project let me know – I’ll be interested in joining forces with someone.

Tomorrow I’ll blog about how this is done and just how easy it is.