Via benpoole I discovered Nik Shenoy’s “Java Agent Performance in Domino R5”. Nik is also a Toronto based Domino Developer. His blog entry is particularly interesting given the number of people interested in running my own RSS News reader on the server.
Java Agent Performance in Domino R5
I was chatting with Weidong Huang from Reader’s Digest a few days ago about some weird behaviour in the Domino Java class loader. It’s actually pretty interesting, but it requires some historical context first.
NOTE: It’s possible that most of this applies to Domino R6, but I’ve only personally tried this in R5.0.12.
A couple of years ago, we were developing a system to manage magazine article editing workflow. We made a design decision to implement some software in Domino, using XML and Java. We came up with a set of classes to represent the problem domain, rendered the data into XML format and then used XSLT to display the data in HTML format. In essence, this would allow us to implement an MVC architecture and keep things fairly clean, while simultaneously providing data export functionality via XML.
The architecture actually worked fairly well, but we ran into some serious performance problems. The built-in XML handling provided by XML4J was insufficient for the task, so we decided to use Xerces for XML parsing and Xalan for the transforms using XSLT. To keep things simple, we were using standard Domino Java agents to render the results to the browser. This is where we hit our first major hurdle.
The Java class libraries for Xerces and Xalan are large. You can attach them to an agent, but that incurs a rather severe run-time hit to load the libraries. It seems that Domino stores these as attachments in the agents and detaches them to create the run-time environment each time. In any case, response time was unacceptable.
The good news is that you can get around this performance bottleneck by installing the Java libraries on the server and telling Domino about them via the JavaUserClasses variable in the NOTES.INI file on the server. In order to compile agent code that uses the system-loaded libraries, you’ll have to do the same thing on the client side so that Designer can see the same class environment. Pre-loading the classes on the server greatly improves the performance of agent execution, but it comes with a price. If you want to change those libraries, you’ll have to have direct access to the server. Also, modifying those libraries generally requires a server restart. Finally, you’ll have to make sure that the paths to those libraries are relatively short since Java has a 256 character limit on the classpath variable and Domino tacks on a whole bunch of it’s own libraries when the server loads.
That solved the biggest performance issue, but we still had another problem because of all the XML generation going on. It takes some time to generate our XML files, and it was unacceptably long to do this every time the client made a request. We needed to cache the data in many cases so that navigation was faster. This is where it would have been nice to have been working with servlets, because there didn’t seem to be a way to keep an in-memory cache for the XML data across requests using agents. Once the agent was done, all the data and context disappeared. I eventually implemented a cache that wrote XML data to Notes documents. That worked, but it still took a second or two to read the data and create the DOM tree.
It always bothered me that there was no way around that, because we didn’t have time to do a servlet implementation of all the user interface code. Well, Wei discovered that classes loaded via the JavaUserClasses statement having static members are cached across agent calls. In our original implementation, we included our home grown classes as a small JAR file in each agent. It was small enough so that load peformance wasn’t a big deal, and it allowed us to update the template in development without having to restart the server each time. This implementation broke when we moved the class to the server because some of our other caching code assumed that everything could be recycled when the agent was finished. Unfortunately, that assumption was no longer valid when the data was being shared across agents.
So, it looks like it is possible to share in-memory data across subsequent runs of a Domino Java agent. I have reproduced this behaviour in a test example in R5.0.12, and Wei has shown this to be true in R6. I can’t guarantee that the class will remain loaded, but it’s something to look at if you’re having performance issues with Java agents in Domino. This also means that using static variables in classes loaded via JavaUserClasses may result in threading problems if you’re not careful.
Nik has some other content worth reading too.