THE VIEW article: Extend the power of LotusScript with DXL and OOP

The second part of my articles on LotusScript.doc titled “Extend the power of LotusScript with DXL and OOP” will be out in the May/June issue of THE VIEW. The article is a great deal more technical and explains in-depth how the application is built using object-oriented LotusScript, design patterns, DXL and XSLT.

The article will supposedly be posted online today (Friday) and should be available at a newsstand near you soon!! ๐Ÿ™‚

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


By now (I assume you read part 1, part 2 and part 3) we have a working AgentBase extension called EclipseAgentBase which is capable of detecting whether the code is running inside Notes/Domino (and hence use the AgentBase functionality supplied by IBM) or outside Notes/Domino. If running outside Notes/Domino we use some custom functionality to “bootstrap” the runtime environment.

In part 3 I showed how to return a Session object from the getSession() method and a PrintWriter from the getAgentOutput() method. The problem with the getSession() method is that the returned Session is unaware of the runtime environment and a call to getAgentContext() will return null which isn’t what we want. Since lotus.domino.Session is an interface we can simply create our own implementation to wrap the returned Session.

To do this we simply create a wrapper for our Session and return the wrapper. Having the wrapper implement the lotus.domino.Session interface makes the caller oblivious to the change (change from part 3 in bold):

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

The declaration of the SessionWrapper class is as follows:

private class SessionWrapper implements Session {
   // declarations
   private Session pSession = null;

   public SessionWrapper(Session session) {
      this.pSession = session;
   }
   ...
   ...
}


The only caveat is that we have to implement all the methods of the lotus.domino.Session interface (all 62 of them) which is quite tedious. But here Eclipse comes to the rescue. Once the class and the pSession member variable has been defined you can simply right click and select “Generate Delegate Methods” from the Source-submenu. This will make Eclipse generate stubs for all methods and make the actual call go to the pSession member variable.

In this way we only need to override the methods we atually need to. This is a big time saver.

Once we return a SessionWrapper object instead of the Session from NotesFactory.createSession() we can do what we need to in relation to the AgentContext. The only method I choose to override from the lotus.domino.Session interface is the getAgentContext() method. Again we are in luck since AgentContext is an interface so we can do this using an anonymous inner class (in bold):

public AgentContext getAgentContext() throws NotesException {
   return new AgentContext() {
      ...
   };
}

We have to implement the 16 methods of the AgentContext interface but we can simply use the “Override/Implement Methods” functionality to generate the stubs to save some time and typing. Once we have done this all the methods return either null or 0 which isn’t what we want. Instead delegate all method calls to callback-methods (starting with “callback”) in the top EclipseAgentBase class as shown below for the getCurrentAgent() method which delegates to the callbackGetCurrentAgent() method:

public abstract class EclipseAgentBase extends AgentBase {
   ...
   ...

   protected Agent callbackGetCurrentAgent() throws NotesException {
      return null;
   }

   private class SessionWrapper implements Session {
      // declarations
      private Session pSession = null;

      public SessionWrapper(Session session) {
         this.pSession = session;
      }

      public AgentContext getAgentContext() throws NotesException {
         return new AgentContext() {

            public Agent getCurrentAgent() throws NotesException {
               return callbackGetCurrentAgent();
            }
         };
      }
      ...
      ...
   }
}

The result of using the callback and just returning null is presently the same but using the callback gives us a lot of functionality as you’ll see in the next and final part of the series.

Webcast: First Look Web conference: IBM Lotus Sametime 7.5

From IBM PartnerWorld News: ISV edition 6 June 15, 2006:

“First Look Web conference: IBM Lotus Sametime 7.5
IBM Lotus Sametime 7.5 is more than an innovative real-time collaboration tool. It’s also a development platform that offers significant opportunity for customer value and services opportunities. To learn more, join us 23 June at 10 a.m. (EST) for a first look at Sametime 7.5.”

Direct link to registration

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.