SWT@Notes 8: Logging from SWT components

As you probably know using System.out.println for logging in Java is a no-no and this is also the case when developing plugins for Notes 8 and Lotus Expeditor. This post outlines how to use the logging framework supplied with Lotus Expeditor, how to manage the logging and where the logs go.

You have probably been using log4j or similar logging framework for years. Avoiding writing to System.out is even more important when writing SWT components and plug-ins for Notes 8 a.k.a. Eclipse since the output goes to a log tucked away under the covers. Luckily there’s an approach which is so much better and more flexible.

Logging basics

Luckily logging has been built into the language (since Java 1.4) and into the Lotus Expeditor platform. Actually there is 5 different frameworks built into the Lotus Expeditor platform. Based on advise from the Lotus and personal experience I really recommend you use the java.util.logging framework built into the core Java language. This is also known as JSR47 loggers. The below graphics shows the logging sub-system of Lotus Expeditor.



Page 8 from the
IBM Lotus Expeditor 6.1 Client for Desktop Serviceability – Logging and IBM Support Assistant (PDF).

As you can see from the above you can use the following logging frameworks in Lotus Expeditor:

  • Eclipse Logging
  • OSGi Logging
  • IBM Commons Logging
  • Apache Jakarta Commons Logging
  • java.util.logging a.k.a. JSR47

I haven’t got experience with all of the these frameworks and luckily I don’t have to. I just have to know about java.util.logging and there’s an abundance of info on that on Google. The Sun tutorial might be the best place to start if you’ve never used java.util.logging. All this being equal the frameworks work the same with the only difference being how you obtain loggers and what the different logging levels are called. Log levels are called something different in the different frameworks but slide 7 in the Lotus Expeditor documentation on logging shows the mapping between the different frameworks.

Using loggers in code

Using java.util.logging in code is really easy and consists of 3 steps:

  1. Obtain the logger to use (normally in a static final variable).
  2. Write info to the logger.

The below is an example of using loggers.

package com.example.logging;

import java.util.logging.Logger;

public class MyLoggingClass {
   // logger
   private final Logger log = Logger.getLogger(
      this.getClass().getPackage().getName());

   public MyLoggingClass() {
      log.finest("Finest...");
      log.finer("Finer...");
      log.fine("Fine...");
      log.info("Info...");
      log.warning("Warning...");
      log.severe("Severe...");
   }
}

The above code creates a logger in a variable called “log” as a private variable. The logger itself is called com.example.logging which is the name used to control the log level (see below). I find it best to name the logger after the package name that contains the class. Sometimes it is however beneficial to also have a logger with more specialized names to log info on specific events.

Configuring the logger log levels

The log level for a single logger or multiple loggers can be controlled using the LogManager using the <Notes data directory>/workspace/.config/rcpinstall.properties file. To change the log level for the com.example.logging logger to FINEST add the following line to the file and restart the client:

com.example.logging.level=FINEST

To change the level dynamically use the OSGi console commands as described below.

Interacting with loggers from the OSGi console

If you start the Notes 8 client with the OSGi console visible you can interact directly with the loggers using the console command called setlogrlev. To set the log level for the com.example.logging logger to FINEST use the following command:

setlogrlev com.example.logging FINEST

Please note that the logger levels are case sensitive so you have to write “FINEST” and not “finest”.

Where the logging goes

All log messages are written to log files in the <Notes data directory>/workspace/logs directory. Opening the directory will reveal a number of files. The two most important are error-log-X.xml and trace-log-X.xml. The file names are quite explanatory and are, as the extension implies, XML-files. When you open them they’ll be rendered using a XSLT stylesheet to be displayed nicely. You can either read them using a browser or using the “Help/Support/View Log” and “Help/Support/View Trace” menu item in the Notes 8 client.

While the log files are usable I personally find it much easier and better to use the OSGi console when developing as I can adjust the log levels on the fly.

Further reading

developerWorks: Plug-in development 101

Are you ready to get going on development of plug-ins for the Notes 8 client (Eclipse)? If you are then look no further than this series of articles on developerWorks: Plug-in development 101. It’s a bit sad that it isn’t Notes 8 specific but hey it’s in the Open Source zone but even though you get the basics of the necessary steps for creating plug-ins.

TwitNotes – deploying in Sametime

Deploying the TwitNotes shelfView (aka sidebar) component in Sametime 7.5.1/8.0 yields a nasty stacktrace from the underlying platform. I thought everything should work across both platforms with everything based on XPD. I haven taken care not to rely on Notes 8 API’s such as the topology service but I guess that would yield a bundle dependency issue and not a stacktrace like this.

Anyone seen similar?

java.lang.NullPointerException: The active part must not be null
        at org.eclipse.ui.internal.expressions.ActivePartExpression.(Unknown Source)
        at org.eclipse.ui.internal.PartSite.initializeDefaultServices(Unknown Source)
        at org.eclipse.ui.internal.PartSite.(Unknown Source)
        at org.eclipse.ui.internal.ViewSite.(Unknown Source)
        at com.ibm.rcp.ui.internal.shelf.ShelfViewReference.createPartHelper(Unknown Source)
        at com.ibm.rcp.ui.internal.shelf.ShelfViewReference.createPart(Unknown Source)
        at org.eclipse.ui.internal.WorkbenchPartReference.getPart(Unknown Source)
        at com.ibm.rcp.ui.shelf.ShelfPage.activatePart(Unknown Source)
        at com.ibm.rcp.ui.shelf.ShelfPage.access$10(Unknown Source)
        at com.ibm.rcp.ui.shelf.ShelfPage$7.handleEvent(Unknown Source)
        at org.eclipse.swt.widgets.EventTable.sendEvent(Unknown Source)
        at org.eclipse.swt.widgets.Widget.sendEvent(Unknown Source)
        at org.eclipse.swt.widgets.Widget.sendEvent(Unknown Source)
        at org.eclipse.swt.widgets.Widget.sendEvent(Unknown Source)
        at org.eclipse.swt.widgets.Widget.notifyListeners(Unknown Source)
        at com.ibm.rcp.swt.swidgets.SViewForm.internalSetMaximized(Unknown Source)
        at com.ibm.rcp.swt.swidgets.SViewForm.setMaximized(Unknown Source)
        at com.ibm.rcp.swt.swidgets.SViewForm.onMouseUpTitle(Unknown Source)
        at com.ibm.rcp.swt.swidgets.SViewForm.onMouseUp(Unknown Source)
        at com.ibm.rcp.swt.swidgets.SViewForm$4.handleEvent(Unknown Source)
        at org.eclipse.swt.widgets.EventTable.sendEvent(Unknown Source)
        at org.eclipse.swt.widgets.Widget.sendEvent(Unknown Source)
        at org.eclipse.swt.widgets.Display.runDeferredEvents(Unknown Source)
        at org.eclipse.swt.widgets.Display.readAndDispatch(Unknown Source)
        at org.eclipse.ui.internal.Workbench.runEventLoop(Unknown Source)
        at org.eclipse.ui.internal.Workbench.runUI(Unknown Source)
        at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Unknown Source)
        at org.eclipse.ui.PlatformUI.createAndRunWorkbench(Unknown Source)
        at com.ibm.rcp.personality.framework.internal.RCPApplication.run(Unknown Source)
        at org.eclipse.core.internal.runtime.PlatformActivator$1.run(Unknown Source)
        at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:92)
        at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:68)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:400)
        at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:177)
        at java.lang.reflect.AccessibleObject.invokeL(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at org.eclipse.core.launcher.Main.invokeFramework(Main.java:336)
        at org.eclipse.core.launcher.Main.basicRun(Main.java:280)
        at org.eclipse.core.launcher.Main.run(Main.java:977)
        at com.ibm.rcp.core.internal.launcher.Main.startLaunch(Main.java:895)
        at com.ibm.rcp.core.internal.launcher.Main.main(Main.java:1313)
        at java.lang.reflect.AccessibleObject.invokeV(Unknown Source)
        at java.lang.reflect.Method.invoke(Unknown Source)
        at com.ibm.oti.vm.JarRunner.main(Unknown Source)

Setting table row height in SWT

So it appears you can actually set the row height of a SWT table despite me previously saying you couldn’t. The caveat is that it cannot be set on a per row basis. Row height is set using a Listener for SWT.MeasureItem and then setting the height property of the supplied event object as shown below.

// resize the row height using a MeasureItem listener
table.addListener(SWT.MeasureItem, new Listener() {
   public void handleEvent(Event event) {
      // height cannot be per row so simply set
      event.height = 67;
   }
});

If need be you could compute the actual height using the GC.stringExtent(String).y and simply set to the biggest value you encounter.

Information from Custom Drawing Table and Tree Items at Eclipse.org.

Watch wikis

Bob Balfe just blogged about how you can access the underlying data model for Composite Applications and how he wrote up a page on the Composite Application wiki. This is great but did you know that you can subscribe to the wikis and be notified when new content is added? Also without Bob blogging about it. One option is RSS and another is by e-mail. While I prefer RSS the wikis doesn’t allow that since the wiki (Atlassian Confluence) throws an exception due to a missing class.

caused by: java.lang.NoClassDefFoundError:
com/sun/syndication/io/WireFeedOutput
 at com.atlassian.xwork.results.RssResult.doExecute(RssResult.java:41)

My guess is that the wiki is run on an IBM JVM and hence doesn’t have the referenced com.sun class. Any way it’s really bad form relying on classes in the com.sun namespace as they are not part of the official API so shame on Atlassian in this case.

Any ways for now e-mail notification is the way to go.

Contributing to a developerWorks wiki

Today I entered the IBM wiki world by contributing to the Composite Application wiki over at developerWorks. It all started with me having to figure out how to add menu items to the menu of a sideshelf component. I found the answer in the wiki (for those interested it’s quite easy using the org.eclipse.ui.viewActions extension point). What I didn’t find was an example of how to programmatically add actions which I also needed so once I figured it out I added the information to the wiki. Programmatically adding actions is useful if you don’t know which actions to add at compile and/or deployment time.

So if you go to the page on creating sideshelf components you’ll find a section on programmatically adding actions.

I really like the idea of the wikis and I really feel good after having contributed. Get some, give some.

P.S.: I know I’m still way behind blogging on my holiday compared to Ed Brill… 🙂

Learning the ropes (pun intended)

I discovered this article through my developerWorks subscription and it was an interesting read. I picked up on the article since I do a lot of String manipulation in LotusScript.doc but hardly in the the 50MB range as described in the article. Still I think you should read the article if you

  1. like to learn new API’s
  2. like to think outside the box
  3. do stuff in Java

Article abstract:

“Systems that manipulate large quantities of string data are poorly served by the Java language’s default String and StringBuilder classes. A rope data structure can be a better alternative. This article introduces Ropes for Java, a rope implementation for the Java platform; explores performance issues; and provides pointers for effective use of the library.”

developerWorks: Ropes: Theory and practice – Why and when to use Ropes for Java for string manipulations

Caveat when converting an Eclipse Java project to a plug-in project

Based on some advise I got in the “Meet the Developers” lab at Lotusphere I converted a Java project to a plug-in project in Eclipse today. This will make it easier to manage my dependencies instead of manually having to export the model classes as a jar and including that. I tried it before but it didn’t work. Today I found out why.

The conversion via right-clicking and choosing “Convert Projects to Plug-in Projects” (in the “PDE Tools” section) went fine but afterwards the classes from the plug-in wasn’t found by my sidebar plug-in in Notes 8. Compilation in Eclipse went fine.

The issues turned out to be caused by Eclipse not automatically adding the compiled classes to the plug-in. To solve this I had to open the manifest (MANIFEST.MF), switch to the “Build” tab and in the “Runtime Information” section add a library named “.” (yes a period) on the top left and the source code folder I wanted to include on the top right.

Once I did that everything went smoothly.