Java in Notes/Domino Explained: Test agents in Eclipse by extending AgentBase (part 3)


Suppose we have this new AgentBase implementation – what we really like is for it to revert back to the lotus.domino.AgentBase functionality whenever the agent is run inside Notes/Domino and only use our new AgentBase look-a-like whenever we run inside Eclipse. Part 2 discussed how to detect where the code is running so by now we have a member variable called pIsNotes that is true if inside Notes/Domino and false otherwise.

To “fool” the agent when running inside Eclipse we need to override the getSession() and the getPrintWriter() methods of the AgentBase class with an implementation that works in Eclise. If we don’t the agent will fail with a NullPointerException whenever the agent is run.

Using our pIsNotes member variable it is almost trivial to create a java.io.PrintWriter that maps to System.out and create a lotus.domino.Session using lotus.domino.NotesFactory whenever we are outside Notes/Domino. When inside Notes/Domino we simply call the super-implementation that is the code of the AgentBase class supplied by IBM:

public abstract class EclipseAgentBase extends AgentBase {
   // declarations
   private boolean pIsNotes = false;
   private Session pSession = null;

   {
      try {
         Class.forName("lotus.domino.servlet.DominoSessionContext");
         this.pIsNotes = true;
      } catch (ClassNotFoundException e) {
         // ignore - we're not in Notes/Domino
      }
   }

   public abstract void NotesMain();

   protected boolean isNotes() {
      return this.pIsNotes;
   }

   public PrintWriter getAgentOutput() {
      try {
         if (this.pIsNotes) {
            return super.getAgentOutput();
         } else {
            return new PrintWriter(new OutputStreamWriter(System.out, "ISO-8859-1"));
         }
      } catch (Exception e) {
         throw new RuntimeException("Unable to convert System.out to PrintWriter", e);
      }
   }

   public Session getSession() {
      try {
         if (this.pIsNotes) {
            return super.getSession();
         } else {
            if (null == this.pSession) {
               this.pSession = NotesFactory.createSession();
            }
            return this.pSession;
         }
      } catch (NotesException e) {
         throw new RuntimeException("Unable to create session", e);
      }
   }
   ...
   ...
}

In the next part we’ll dive a little deeper by looking at a crucial part we are missing – the AgentContext returned by the Session object. At present it simply returns null which won’t work when testing agents.

Stay tuned.

Java in Notes/Domino Explained: Test agents in Eclipse by extending AgentBase (part 2)


Part 1 discussed the need for a new base class for “Imported Java”-agents by extending the lotus.domino.AgentBase class to allow writing, testing and debugging agents from within Eclipse. Suppose we have this new AgentBase implementation – what we really like is for it to revert back to the lotus.domino.AgentBase functionality whenever the agent is run inside Notes/Domino and only use our new AgentBase look-a-like whenever we run inside Eclipse. To do this we need to detect where the code is running.

There are really not that many ways to detect whether the code is running inside Notes/Domino or Eclipse for that matter. My first choice was to look for a System property and found a property called package.restrict.definition.lotus.notes that would allow me to detect whether the code was running inside Notes/Domino. Using this property I could set a flag in the base class that I could query later on:

public abstract class EclipseAgentBase extends AgentBase {
   // declarations
   private boolean pIsNotes = false;

   {
      String prop_name = "package.restrict.definition.lotus.notes";
      this.pIsNotes = (null != System.getProperty(prop_name));
   }
   ...
   ...
}

The “strange” construct I use to initialize this pIsNotes member variable is called an initializer. If you would like to learn more about initializers I can recommend an article at Javaworld.com.

There is a problem with using System properties in Notes/Domino since you are only allowed to query a subset of them without having access to restricted operations. Strange but true… This meant I had to scratch that approach since I would really like an approach viable for all agents without requiring restricted access.

I finally settled on using the availability of a class for my detection. Since the Domino Servlet Container classes are only available inside Notes/Domino I could use a class from there. I settled on the DominoSessionContext class from the lotus.domino.servlet-package. The Domino Servlet Container classes are also available in agents since the dservlet.jar is placed in jvm/lib/ext. The detection is now as simple as trying to load the class:

public abstract class EclipseAgentBase extends AgentBase {
   // declarations
   private boolean pIsNotes = false;

   {
      try {
         Class.forName("lotus.domino.servlet.DominoSessionContext");
         this.pIsNotes = true;
      } catch (ClassNotFoundException e) {
         // ignore - we're not in Notes/Domino
      }
   }
   ...
   ...
}

In this case a java.lang.ClassNotFoundException simply means that the classloader cannot find the class hence we are outside Notes/Domino. Simple but it works.

Change of hide-when formula for HTTPPassword field in Domino Directory

In Domino 7 the HTTPPassword field in the Domino Directory has been changed from an editable to a computed when composed field which doesn’t allow you to edit its value. From a security standpoint this is great but it does make it more difficult to change the password to a known value to test authorization issues. Previously, since the field was editable, you could simply cut the users current password to a text file, paste your known password and test away. Once done you would paste the users password back in. Simple and easy.

Of cause one could always modify the design of the Domino Directory to revert to the old style or implement it as an agent.

Subscribe to comments directly from FeedDemon – nice!

Isn’t that cool! A way to directly subscribe to the comments to a specific post directly from the RSS reader (FeedDemon in my case). Sweet! I took a look at the RSS feed source and it is due to the use of the Comment API (http://wellformedweb.org/CommentAPI/) and the <wfw:commentRSS /> tag. This is a new feature added in FeedDemon 2.0.24 just out…

Java in Notes/Domino Explained: Test agents in Eclipse by extending AgentBase (part 1)


This post way sparked by a post in the Notes/Domino forum on developerWorks.

Let me start by saying that you could probably use the AgentRunner if you can figure it out and if it still works. I haven’t used it for a LONG time and I doubt that it still works with the changes made to the Java API in Notes/Domino 6.x and 7.x. The Using Document of the AgentRunner database hasn’t been updated since 1999!! Anyways – there are other options.

Write the code in a separate class hierarchy

The first one is to write the agent functionality in separate classes and then have the agent code call these classes to do its job. This is a viable approach though it means you have to maintain the actual business code and the agent code in separate places (the business code in Eclipse and the agent code in Notes/Domino).

Extend lotus.domino.AgentBase

Another option is to extend the AgentBase class and implement some of the intializing functionality yourself for testing. The approach isn’t optimal and still makes you implement some boilerplate code but it’s a start and a viable option for writing and testing your agents in Eclipse. The new AgentBase class can be made to detect whether it runs inside Eclipse or inside Notes/Domino so it’s easy to move the code into Notes/Domino once it works.

The boilerplate code has to do with initializing a thread to Notes in a main() method before calling the NotesMain() method of your agent class. The below example shows the required boilerplate code for the ExampleAgent1 class:

public static void main(String[] args) {
   try {
      NotesThread.sinitThread();
      new ExampleAgent1().NotesMain();

   } catch (Exception e) {
      e.printStackTrace();
   } finally {
      NotesThread.stermThread();
   }
}

The new AgentBase class (called EclipseAgentBase) means you can write agents in Eclipse. The only change is that you extend EclipseAgentBase instead of AgentBase:

import lotus.domino.NotesThread;
import lotus.domino.Session;

public class ExampleAgent1 extends EclipseAgentBase {

   public static void main(String[] args) {
      try {
         NotesThread.sinitThread();
         new ExampleAgent1().NotesMain();

      } catch (Exception e) {
         e.printStackTrace();
      } finally {
         NotesThread.stermThread();
      }
   }

   public void NotesMain() {
      try {
         Session session = this.getSession();
         System.out.println("Username: " + session.getUserName());
      } catch (Exception e) {
         e.printStackTrace();
      }
   }
}

In the next part of the post I’ll dive into how do build the EclipseAgentBase class.

XInclude

As previously mentioned here and here I’m using external entities at present to redue the amount of dublicate data when sharing data among XML documents. A better choice than entities would be to use XInclude which is a recommendation from W3C on how to include XML documents in other XML documents. The difference betwwen entities and XInclude is that XInclue is an extension to the core XML and that it integrates nicely with the other XML extensions such as XSLT. An example of using XInclude is show below:

<code>
  <xi:include href="MyFile.xml" />
</code>

Unfortunately XInclude isn’t supported in the LotusScript XML classes – probably because it’s only a recommendation. It isn’t supported in the Java included in Notes/Domino either since Notes 7 has Java 1.4.x which in turn has JAXP 1.2 (Java API for XML Processing) which is too old. XInclude is however included in JAXP 1.3 so if you need it you can upgrade your XML processing libraries to the latest Xerces from Apache.

A caveat might be however that Notes/Domino is shipped with a combined Xalan (XSLT) and Xerces (XML parser) library (/jvm/lib/xml.jar). I haven’t tried updating this library but would like to hear from people having tried. An alternative is off cause to include the new xerces.jar in the agent or similar so the new classes are used.

Resources:

Inside The Net 24: The Two Mikes from Firefox

The new episode of the Inside The Net podcast is about Firefox and the Two Mikes. Show description from the TWIT homepage: “Hard on the heels of the successful launch of Firefox 1.5 comes the first beta of Firefox 2.0. Mike and Mike talk about Firefox Flicks and what’s new in 2 including lookahead searching, RSS support, and faster browsing.”

Show ‘n Tell Thursday: Using XSLT on DXL documents (8 June 2006)


Using XSLT on XML is a great way to filter the data before parsing the result document. With DXL this is even more true to the VERY verbose nature of DXL. An advantage of using XSLT to filter the DXL document before parsing is that it is much easier to filter based on XPath queries rather than walk the DOM tree or using SAX parsing and writing and handling all the events that would arise from parsing a DXL document.

If you are not familiar with XPath or XSLT I suggest you read up on the technologies. Although I haven’t used the tutorials myself the tutorials provided on w3cschools.com tends to be quite good. At the site you’ll find tutorials on both XPath and XSLT.

I would also like to plug my upcoming article in the May/June issue of THE VIEW where I also discuss this subject and how I used the techniques in LotusScript.doc… 🙂

Well basically using XSLT with DXL is quite simple if you know how (isn’t everything). The only caveat is that you have to remember that the DXL documents exported from Lotus Notes use the dxl-namespace so you’ll have to import the namespace and use the namespace in all XPath queries. Normally a XSLT stylesheet starts like this:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

When using XSLT on DXL documents you’ll have to import the dxl-namespace as well (change in bold):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:dxl="http://www.lotus.com/dxl" version="1.0">

Once you have done this you can use XPath as normally and make your templates match their target. The below example will create a bullet list of the script library names from a complete export of a Notes database (notice the use of the dxl-namespace in bold):

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet
     xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
     xmlns:dxl="http://www.lotus.com/dxl"
     version="1.0">
     <xsl:output method="html" version="1.0" encoding="iso-8859-1" />

     <xsl:template match="/">
       <ul>
         <xsl:apply-templates select="/dxl:database/dxl:scriptlibrary" />
       </ul>
     </xsl:template>

     <xsl:template match="//dxl:scriptlibrary">
       <li>
         <xsl:value-of select="@name" />
       </li>
     </xsl:template>

</xsl:stylesheet>

Applying the above style sheet to an export of the LotusScript.doc database will produce a bullet list like the one below:

  • CLASS: Script
  • CLASS: ScriptParser
  • CLASS: ScriptElement
  • CLASS: Collections
  • CLASS: InputStream
  • CLASS: ScriptFactory
  • CLASS: OutputStream
  • CLASS: DocumentationWriter
  • CLASS: Index
  • CLASS: OutputStreamFactory
  • CLASS: ClassHierarchyIndex
  • CLASS: ScriptSource
  • DBDesign
  • CLASS: DocumentationBuilder
  • CLASS: LSDocConstants
  • CLASS: Comment
  • CLASS: DecisionMaker
  • CLASS: ConfigDocuments
  • CLASS: TemplateVersion

An easy way to test XSLT style sheets if you do not have an IDE capable of helping you is to associate the style sheet with the DXL document and then preview using a browser. To see how see my post called Apply XSLT style sheet to an XML document for browser friendly display.

Hope the above helps someone…