Karsten Lehmann 7 February 2011 09:03:00

The 10th article of the XPages series deals with the first sample I demo'ed for the Lotusphere session BP212 - Deep Dive into IBM XPage Expression Language Syntax: It shows how you can develop and test most of your XPages code outside of DDE. So why should you development code outside of DDE? 1. Workaround for classloader issues Well, you can experience a very nasty caching behaviour when you develop Java code in the Java perspective of DDE and test it live in an XPages application on the client. This might result in ClassCastExceptions like "com.company.packagename.MyClass cannot be cast to com.company.packagename.MyClass", which is caused by the JSF servlet: It still has a cached instance of the first bean in memory, created with one classloader while it tries to run the XPages application after code changes with a new classloader which is incompatible with the first one used.

This behaviour is discussed in an article in the Lotus Notes and Domino Application Development Wiki and IBM recommends to restart the http task in this case (only a viable option if you develop on a local Domino server), because this shuts down and restarts the JVM used to load the classes. Unfortunately this is not an option for development in the Notes Client, since you really don't want to restart your Notes Client/DDE every time you change code. In addition to classloader issues, the JSF runtime also seems to cache Java classes and XPages intermittently, a problem that I faced just recently when I worked on the demos for BP212 and tested them in the client. 2. Version control for source code Another reason why you should develop code outside of DDE is that you can use version control systems like Subversion or CVS. With 8.5.2 and an additional source control plugin from OpenNTF this aspect is less important than before, but I still prefer to have my code outside of a virtual file system that is stored in an NSF. I just don't trust that stuff and don't want to lose my code, e.g. if DDE does a rebuild on two developer machines and then they replicate with the same db instance on a server. 3. Use code in other Non-XPages projects We do not only develop XPages apps, we develop libraries to be used in agents, standalone Eclipse RCP/Swing applications, server-side OSGi plugins and for many other areas. That's why we want to share most of the code between multiple development projects. 4. Run unit tests on the code for quality checks To ensure that our code still produces the expected results after a code change, we use JUnit test cases on a manual or scheduled basis.

And that is exactly what this sample is about. Workspace content The download archive for this blog article contains two directories: a sample XPages application and an exported Eclipse workspace.

All it does is read the name information of the current user from a managed bean "currentUser" and display it on screen. The managed bean class is not directly part of the NSF, but has been added in a "lib" folder below the WebContent/WEB-INF folder as a JAR file and then added to the classpath of the NSF project in DDE (after that, it's not visible in the "lib" folder anymore, but below "Referenced Libraries").

After you import the projects of the "workspace" directory into your personal Eclipse workspace, it should look like this:

Please note that you might get compile errors and need to change the path of the referenced "Notes.jar" file in the com.ls11.dominohelper project to fit your Notes configuration.

There are four projects in the workspace:

com.ls11.dominohelper

The project contains helper classes to execute a piece of code in the context of a Notes session (see below for details)

com.ls11.externalcodesample

This project contains the bean implementation to read the current username

com.ls11.externalcodesample.build

We use the ANT script of this project to create the JAR file with the necessary content to work in an XPages app

com.ls11.externalcodesample.test

Finally this project contains a JUnit test case to check that our bean is working correctly

Running the same code standalone and from an XPages request Let's dive into the class CurrentUserInfo.java. The method .getCommonName() looks like this:

/** * Returns the common name of the current user * * @return common name */ public String getCommonName() { if (m_commonName==null) { IDominoCallable<String> callable=new IDominoCallable<String>() {

Now that looks interesting: We are using an IDominoCallable implementation to retrieve the common name part of the username. We do this in order to run the same code from an XPages environment and from a standalone application. In the DominoExecution there's some magic that detects whether it gets executed from XPages code or not.

In standalone mode, DominoExecution currently launches a new NotesThread for every call, but that could easily be changed to have a permanent thread and just feed it with the IDominoCallable's one after another.

The run method of DominoExecution returns a Future object of Java's concurrency framework. Calling its .get() method let's the executing code wait for the result to be computed. In XPages mode, the IDominoCallable is executed synchronously, because the code is already running in a Domino enabled thread. Running JUnit in Eclipse The project com.ls11.externalcodesample.test contains a JUnit test case that checks whether the methods of the bean are working correctly: For example it ensures that abbreviated name and common name are both not null and compares the first part of the abbreviated name with the common name.

You can launch the test case by first creating a JUnit run configuration:

On the Environment tab, please add a variable "PATH" that contains the path to your Lotus Notes program directory:

Now you can execute the test case and get the following result:

The method testCommonNameAndAbbrNameConsistent could be executed without errors, but calling testOrganisation led to an error. It checks that CurrentUserInfoTest.getOrganisation() is not null, but unfortunately, it seems like we forgot the method's implementation: public String getOrganisation() { // TODO Auto-generated method stub return null; }

After fixing that, the JUnit succeeds:

Now that we know that the bean works, we can package everything by right clicking on the ANT script of project com.ls11.externalcodesample.build, choose "Run As/Ant Build", then do a refresh on the "lib" folder and replace the JAR in the XPages application database with the new one.

Thanks for taking the time to document this. It was a great article to get me to dig into JUNIT and ANT. However, when I implemented the bean in my xpage, it produced only the server's name for both common and abbreviated. Any ideas on what I could be doing wrong?

I should note that this works correctly when previewed in the Notes Client. But even after changing the lines that reference .getUsername to getEffectiveUsername it still fails. I am hoping it is somekind of caching problem on the server.