wissel.net

Usability - Productivity - Business - The web - Singapore & Twins

By Date: July 2013

Protecting your XPages Application


One of the hallmarks and success factors of IBM Notes was the openness of the design of all Notes applications. Since lots of people put blood, sweat and tears into their applications, Lotus added the possibility to hide the design of an application to protect it from spying eyes. While I don't like hidden designs I can understand the rationale.
Others keep their design open, but compile the LotusScript libraries with references to lss files outside, so the core business logic is protected.
Hiding the design doesn't work for XPages, you can poke around and get back to the source. So what are your options?
  • Keep your business logic in Java classes (Pojo, Beans, Managed Beans) and develop them outside of the NSF and transfer them as jar file only
  • Deliver your custom controls as Extension Library (which can be deployed using an NSF) - which obviously requires advanced Skills
  • Keep your XPages lean and handle the delicate parts in custom controls that you hide (see below)
Of course compiled Java can be transformed back to Java source code, so you might want to add another layer using Java obfuscation, but then you might have crossed the line between precaution and paranoia (to my best knowledge IBM doesn't use obfuscation in Java products). An additional level of protection is offered using Refuctoring (no, that's not a typo).
So how would a typical workflow look like? There seem to be quite some moving parts involved:
Protecting XPages Workflow
  1. Develop and test your application as per normal. Keep items you want to protect in custom controls and Java classes
  2. Make sure you have your project build before doing the next steps
  3. Link your database to an On Disk Project for version control (but you do that anyway don't you?). Let's call that project "OnDisk Open"
  4. Have your target database (the one that you will deliver to your customer) setup in a similar way. Let's call that project "OnDisk Closed"
  5. Copy all elements you want to keep open (Forms, Views, XPages, CSS etc.) from OnDisk Open to OnDisk Closed
  6. Copy your Java source files and the generated Java source files for your custom controls (found in the Local directory, actually outside the NSF) in OnDisk Open to a src structure. Custom controls live in the xsp package. Your code lives in your packages
  7. Compile your Java
  8. Put all the compiled classes into a jar
  9. Copy that jar into the /WebContent/WEB-INF/lib folder of the OnDisk Closed project. At the first time: add this jar to the classpath of your closed.nsf after sync with the OnDisk Closed project
  10. Copy the .xsp-config files of the custom controls you processed in the previous 4 steps into the /WebContent/WEB-INF/ directory (the same directory your faces-config.xml is living in)
  11. Sync the OnDisk Closed project with closed.nsf
  12. Build your closed.nsf - done
With a little skill those steps can be automated.

Read more

Posted by on 24 July 2013 | Comments (4) | categories: XPages

NotesSessions, XPages and Threads


When you build an XPages application, long running operations will time out. After all there is a timing limit how long a browser (or XPiNC) client (and the user) will wait for a response. You could resort to launching an agent via a console command, but besides the security consideration it is quite a hack and headache. Mixing XPages and agents doesn't have a happy ending (and should be confined to the upgrading phase of your classic application).
Enter Java multi-threading. I call it " Teenage-mode": your code does multiple things at the same time and doesn't seem to get distracted.
When you read the classics you will find threading hard and rather threatening (pun intended), but thanks to progress in Java6 and above it isn't too hard.
There is the Executor framework that handles all your Runnables. There are 2 challenges to overcome: one specific to Notes and one general to all concurrency programming. The former: The Notes Session class is not threadsave and can't be shared between threads, the later: you have to get your queueing right:

(Image courtesy of Lee Chee Chew)
To get a new Session object, we have the session cloner, thanks to the extension library. All your threads implement the Runnable interface that is later called by the executor framework. There are 2 approaches for background task: one where you expect the thread to return some value at some time and the other where a thread runs its course and terminates silently when done. For a current project I opted for the later. The threads contain a callback class where they report back what they have to say. To make it all work we need:
  1. A management class that executes the threads for us. Preferably long living (like session or application beans)
  2. A cloned Notes Session
  3. One or more classes implementing the runnable interface
Let's look at some code, first the class that runs in the background:
package com.notessensei.demo ;

import java.util.Collection ;
import java.util.Map ;
import lotus.domino.Database ;
import lotus.domino.NotesException ;
import lotus.domino.Session ;
import com.ibm.domino.xsp.module.nsf.NSFComponentModule ;
import com.ibm.domino.xsp.module.nsf.NotesContext ;
import com.ibm.domino.xsp.module.nsf.SessionCloner ;

public abstract class AbstractNotesBackgroundTask implements Runnable {
    protected final Session                     notesSession ;
    protected final Collection < String >          callBackMessages ;
    private SessionCloner                   sessionCloner ;
    private NSFComponentModule              module ;
   
    public AbstractNotesBackgroundTask ( final Session optionalSession, final Collection < String > messageHandler ) {
        this. callBackMessages = messageHandler ;
        // optionalSession MUST be NULL when this should run in a thread, contain a session when
        // the class is running in the same thread as it was constructed
        this. notesSession = optionalSession ;
        this. setDominoContextCloner ( ) ;
    }

    public void run ( ) {
        this. callBackMessages. add ( "Background task run starting: " + this. getClass ( ). toString ( ) ) ;
        try {
            Session session ;
            if ( this. notesSession == null ) {
                NotesContext context = new NotesContext ( this. module ) ;
                NotesContext. initThread (context ) ;
                session = this. sessionCloner. getSession ( ) ;
            } else {
                // We run in an established session
                session = this. notesSession ;
            }
           
            /* Do the work here */
            this. runNotes ( ) ;
           
        } catch ( Throwable e ) {
            e. printStacktrace ( ) ;
        } finally {
            if ( this. notesSession == null ) {
                NotesContext. termThread ( ) ;
                try {
                    this. sessionCloner. recycle ( ) ;
                } catch (NotesException e1 ) {
                    e1. printStacktrace ( ) ;
                }
            }
        }
        this. callBackMessages. add ( "Background task run completed: " + this. getClass ( ). toString ( ) ) ;
    }

    private void setDominoContextCloner ( ) {
        // Domino stuff to be able to get a cloned session
        if ( this. notesSession == null ) {
            try {
                this. module = NotesContext. getCurrent ( ). getModule ( ) ;
                this. sessionCloner = SessionCloner. getSessionCloner ( ) ;
            } catch ( Exception e ) {
                e. printStacktrace ( ) ;
            }
        }
    }
   
    protected abstract void runNotes ( ) ;
}
You only need to subclass it and implement runNotes() to get started. However you probably want to hand over more parameters, so you need to modify the constructor accordingly. Please note: you can't bring Notes objects across thread boundaries. Next the class that controls all the threads. It should live in the session or application.

Read more

Posted by on 23 July 2013 | Comments (7) | categories: XPages

XPiNC development insights


I am developing a rather large XPiNC application for IBM internal use. One application and parameter database pushes data around 10 backend databases. Part of the databases might be local (depending on the user), some might be on the server and not all users have access to all databases. Most of my business logic lives in Java code, both in classes, beans and managed beans. In the course of the development I stumbled over a series of insights a XPiNC developer should be aware of. Here it goes, in no specific order:
  • FacesContext.getCurrentInstance() might return null when called in your Java class, depending on when you call it. I had calls to the FacesContext in some of my managed bean property calls which failed on page load with a Error 500 that the standard error page couldn't trap. You need the FacesContext to resolve any variable e.g. getting a handle on a different managed bean. In conclusion I rearchitected my beans to have only one managed bean per context modeled as Facade pattern that provides a single access point to all classes needed
  • The ExtlibUtil.getCurrentSession() uses the FacesContext under the hood, so there is a distinct risk of getting a null session. So using the dependency injection pattern all calls requiring a NotesSession have a NotesSession as calling parameter. This requires a bean design that is populated elsewhere (e.g. DataContext, ObjectDataSource), so you can have getters and setters without parameters
  • Longer running operations (stuff we typically do in an agent) will time out. You don't have the luxury in XPiNC to use agent.runOnServer(), so you need a different approach (and anyway I despise mixing XPages and agents). With the help of a friend and the ever impeccable Dublin team I figured a way to run Notes backend accessing code as a thread (subject to another story)
  • Debugging is way less comfortable than in XPages. You have to put the whole client into debug mode, not just connect to the http task. So I tend to start my Notes client with -RPARAM -consoleso I see what System.out and System.err are producing. I also tend to keep my code in Java to make it more testable easily
  • The XPages Debug Toolbar works well in XPiNC
  • There is no way to restart the XPiNC task like you can restart the http task or the web preview task in Designer. When you use external jars and update them, you have to restart your client to test it successfully
  • In a data source you can specify databases by replica id. This is nice since you might not know where a local replica is located. However you only can specify the replica id and unless NotesDatabase.openByReplicaId() not the server. So you never know which database you might get (local or one of the known server). So I compute once (and keep it in the sessionScope) the actual filepath, trying local first, and use server/filepath instead of replica id for data sources
  • The Java Collection framework is your friend. You can use them nicely as source for a repeat control. Just keep in mind: a list will give you the entry in your instance variable, while a map will give you they key only. Of course a key can be any Java object, so some creative key creation goes a long way
  • A very popular way in Notes applications was the use of NotesDocument.computeWithForm(). When connecting to existing applications you will be tempted (or actually required) to use it too. When the computeWithForm tries to trigger some UI interaction in the @Formula, your XPiNC tab will lose focus to a native tab (I haven't figured out which one gets selected) and a externalContext.redirect() will simply not happen
  • In an XPiNC application sessionScope and applicationScope are local to the current user. So status exchange via applicationScope won't work in XPiNC. After all you run a local JEE server instance in your local Expeditor framework (a.k.a Notes client). This even applies when you load a server based NSF (hardly a good idea in XPiNC). This changes when you configure XPiNC to run on the server, then the applicationScope is for all users.
  • Since XPiNC applications load all Java classes from the NSF into the JEE server memory, its best to load them from a local NSF, even for applications that need to have current data for all users. Here XPages' ability to use data from a different database comes in handy and you would load your data NSF from the server - I'll prepare some code snippets for future posts
As usual YMMV

Posted by on 21 July 2013 | Comments (6) | categories: XPages

The Science of Gastronomy - Week 1


To understand a new phenomenon, often a good way to get a grasp is full immersion. So I decided to participate in a MOOC at Coursea namely The Science of Gastronomy. The course description reads quite appealing:
This course introduces students to elements of science behind cooking, cuisine preparation and the enjoyment of food. The ultimate goal is to help students recognise the importance of scientific principles being applied in everyday life, so that they will appreciate and be able to apply some of these principles in their future cooking practice, including the manipulation of human perception. (emphasis mine)
The first week went of to an enjoyable start and I'll report on progress. One of the topics was the categorisation of cooking methods, here is my summary:
How we cook
(Click on the image for a larger version) - Stay tuned

Posted by on 18 July 2013 | Comments (0) | categories: After hours Coursera

MAMIL gone mad


Don't worry, there won't be another Mamil picture in this post. Yesterday I went on a nice joy ride with a group of outdoor enthusiasts.
Thanks to the planning efforts of T.S. Chua we navigated 100km of Singapore's Park Connector Network and a few connecting roads. Including getting from and too the meeting point that was 120km for me, the longest bike ride I ever done in a single day (so much for can't teach an old dog a new trick).
100 KM Ride
I found the company a rather pleasant and cheerful lot and I'm looking forward to the big round.
P.S.: I didn't get the Montague X70, but the Paratrooper pro from the friendly folks (all bikers themselves) at Mighty Velo. Next stop: Ride home from the office.

Posted by on 01 July 2013 | Comments (0) | categories: After hours Cycling