Java in Notes/Domino Explained: The difference between a class, an abstract class and an interface


Normal, or concrete, classes in Java can be instantiated using the new keyword. Concrete classes both define the functionality, that is what the class does, and the interface, that is what methods are available for you to call. Most of the classes you write will be concrete classes like the one below.

public class MyConcreteClass {
   public void perform() {
      ...
   }
}

At the other end of the spectrum we have an interface which only defines which methods we can call on it – there is no functionality in there. The interface in the example below specifies that a class implementing the interface will at least have the 4 listed methods. Notice that there is no method bodies (the compiler will enforce this).

public interface Car {
   public void increaseSpeed();
   public void decreaseSpeed();
   public void turnRight();
   public void turnLeft();
}

By creating a class and inserting the keyword “abstract” after the class visibility modifier (public, private, “friendly”) makes the class into an abstract class. An abstract class is somewhere in between an interface and a class, that is a class where some of the functionality is specified but not all. Think of an abstract class as a class with some blanks that needs to be filled in by any, non-abstract, class that extends it. An abstract class cannot be instantiated using the new keyword.

public abstract class MyAbstractClass {
   public class perform() {
      this.doStuff();
   }
   public abstract void doStuff();
}

How do I use an interface?

To use an interface you have your class implement it. Implementing an interface means making sure that the class specifies method bodies for all the methods in the interface. If you do not implement all the methods in the interface the compiler will make you mark the class as abstract.

public class Ford implements Car {
   public void increaseSpeed() {
      ...
   }
   public void decreaseSpeed() {
      ...
   }
   public void turnRight() {
      ...
   }
   public void turnLeft() {
      ...
   }
}

How do I use an abstract class?

An abstact class cannot be instantiated because some of the functionality (for example the doStuff() method in the example above) isn’t implemented. To use the class we need to extend it and implement any missing, abstract, methods.

public class MyClass extends MyAbstractClass {
   public void doStuff() {
      ...
   }
}

So when should I use which?

Well the real answer is it depends…

An abstract class is a good choice when you do most of the programming in the abstract class and leave only one or a couple of methods to sub-classes. An example could be a class that reads data from a file. The abstract class defines the actual reading and processing of the file and concrete sub-classes define how to get the file (from the local disk, via HTTP, via FTP etc.):

public abstract class FileProcessor {
   // declarations
   private File file = null;

   public process() {
      // get file
      this.file = this.getFile();

      // open file
      this.openFile();

      // decrypt file
      this.decryptFile();

      // do other stuff with the file
      ...
      ...
   }
   private void openFile() {
      ...
   }
   private void decryptFile() {
      ...
   }
   private abstract File getFile() throws Exception;
}

public class HttpFileProcessor extends FileProcessor {
   private abstract File getFile() throws Exception {
      // get file via HTTP and return it
      ...
   }
}

public class FtpFileProcessor extends FileProcessor {
   private abstract File getFile() throws Exception {
      // get file via FTP and return it
      ...
   }
}

An interface is really nice when all you need to do it define what methods are available but you want to leave the entire implementation up to the implementer. The interface in the example below only describes that a class implementing the interface knows how to process a Document object. You don’t care how the class does it but you know that you can call the process() method supplying the Document that should be processed.

public interface DocumentProcessor {
   public void process(Document doc) throws Exception;
}

Another really neat thing about interfaces is that a class may implement multiple interfaces whereas it can only extend a single class. This means you can use an interface simply to signal something (for an example of this look at the java.lang.Cloneable interface in the JDK).

public class MyCar implements Car, Cloneable, Serializable {
   ...
}

Java in Notes/Domino Explained: Java Collection API for LotusScript programmers


The Java Collection API was introduced in Java 1.2 and is a really nice API for handling dynamic arrays, sets, lists etc. The classes for the Collection API can be found in the java.util package. There is no built-in equivalent to the Collection API in LotusScript.

When coming from to Java from LotusScript the Collection API may be a little overwhelming to get cracking on. To kind of ease into it lets look at how to use the basic parts of the API for do kind of the same stuff in Java as you do in LotusScript.

Note: All the collection classes of the Collection API return references of type java.lang.Object which means that references oftentimes needs to be cast back to the desired type.

Dynamic arrays

In LotusScript you can use the Redim and Redim Preserve keywords to change the dimensions of an array at runtime. This isn’t possible in Java. Instead you can use an ArrayList which is an array-like structure that automatically resizes. If you are coming from Java in Notes/Domino 5.x the ArrayList is like a Vector only a little faster.

LotusScript
Dim my_array() As String
Dim i as Integer
For i=0 To 10
   Redim Preserve my_array(i)
   my_array(i) = "Index: " & i
Next

Java
import java.util.*;

ArrayList l = new ArrayList();
for (int i=0; i<11; i++) {
   l.add("Index: " + i);
}

As you can see there is no need to resize the ArrayList in the for-loop.

Lists

LotusScript has the concept of a List which is a key/value datastructure. In Java this kind of collection is called a Map and the most used type is the HashMap. For people coming from Java in Notes/Domino 5.x a HashMap is basically the same as a Hashtable only a wee bit faster.

LotusScript
Dim my_list List As String
my_list("one") = "1"
my_list("two") = "2"

Java
import java.util.*;

HashMap my_map = new HashMap();
my_map.put("one", "1");
my_map.put("two", "2");

Looping an array

All classes of the Collection API implementing the List/Set interface has an iterator() method that returns an Iterator object you can use to loop the collection.

LotusScript
Dim some_array(10) As String
Dim i As Integer
For i=0 To UBound(some_array)
   Print some_array(i)
Next

Java
import java.util.*;

ArrayList l = new ArrayList();
Iterator ite = l.iterator();
while (ite.hasNext()) {
   System.out.println(ite.next());
}
...or the slightly more condensed version I prefer...
ArrayList l = new ArrayList();
for (Iterator ite = l.iterator(); ite.hasNext(); ) {
   System.out.println(ite.next());
}

Looping a List

All classes of the Collection API implementing the Map interface has an keySet() method that returns a Set containing the keys used in the Map. From that Set you use the iterator() method to obtain an Iterator for looping.

LotusScript
Dim my_list List As String
my_list("one") = "1"
my_list("two") = "2"
Forall v In my_list
   Print Listtag(v) + " = " + v
End Forall

Java
import java.util.*;

HashMap my_map = new HashMap();
for (Iterator ite=my_map.keySet().iterator(); ite.hasNext(); ) {
   Object key = ite.next();
   System.out.println(key + " = " + my_map.get(key));
}

As you can see in the Java example the caveat here is to use the my_map.keySet() method to get a Set containing the keys in the Map. From the Set we can get an Iterator that we use to loop the keys of the Map.

Java in Notes/Domino Explained: Handling arrays


Probably the biggest source of fustration for LotusScript programmers moving to Java is the fact that arrays cannot be resized in Java once they have been declared. Where in LotusScript we have the Redim keyword there is no equivalent for Java when talking arrays. If you need resizable arrays you should look into the Java Collection API (see the upcoming post “Java Collection API for LotusScript programmers” for more information on the Collection API).

Note: All references to arrays in Java are objects – also references to arrays of the privitive data types (int, long, char etc.).

As mentioned arrays dimensions are fixed in Java once declared and arrays always start at index 0. Notice how the bound has to be one higher than the number of places you need in the array. To declare a simple array with room for 5 Strings you write:

String[] my_strings = new String[5];
my_strings[0] = "abc";
my_strings[1] = "def";
my_strings[2] = "ghi";
my_strings[3] = "jkl";
my_strings[4] = "mno";

Alternatively you may also separate the declaration of the reference from the actual memory allocation.

String[] my_other_strings = null;
my_other_strings = new String[2];
my_other_strings[0] = "abc";
my_other_strings[1] = "def";

A cool thing with Java arrays is that you can use a more condensed syntax and fill the array as you declare it. It is a great way to avoid manually having to specify the dimensions of the array. The below code produces the same array as above (notice the use of curly braces).

String[] my_strings = {"abc", "def", "ghi", "jkl", "mno"};

This doesn’t just work for Strings but can be equally applied to other types of objects:

Object[] my_objects = {"abc", new java.util.Date(), new java.lang.Object()};

The reference to an array is kind of special in Java since it has a special member variable called length to help in looping the array. The basic construct for looping an array in Java is a for-loop just like in LotusScript. In the Java versions employed by Notes/Domino 7 and below there is no forall type loop.

String[] my_strings = {"abc", "def", "ghi", "jkl", "mno"};
for (int i=0; i<my_strings.length; i++) {
   System.out.println("Index " + i + ": " + my_strings[i]);
}

When working with arrays you really should be using the length member variable when looping arrays. If not you’ll most likely at some point run into the java.lang.ArrayIndexOutOfBoundsException which is an unchecked exception.

Java in Notes/Domino Explained: The story on recycle()


As with everything what you see is typically only the tip of the iceberg. This is also true for the Java objects for Notes/Domino since they are only thin wrappers around the underlying, more heavyweight, C++ objects. Since the Java garbage collector has no knowledge of this, all classes in the Notes/Domino API inherit a method called recycle() from the lotus.domino.Base class. The purpose of this method is to explictly tell the C++ object to free any memory it is using. A part from freeing its own memory, the object will also call recycle() on any object that it created. This means that calling recycle() on a database object will also recycle() a view object obtained opened through it.

If you do not realize that calling recycle() will cascade down through the object hierarchy like this you’re going to experience some “interesting” exceptions and unexpected behaviour at runtime.

It is important to note that the only reason for calling recycle() is to release memory while the agent (or other Java program) is running.

If your agent only does a small piece of work it’s probably not worth the effort of calling recycle() since people normally get into a lot more trouble calling recycle() than what they actually accomplish. My recommendation is to recycle document objects when you loop a document collection or view and otherwise not spend time thinking of recycle() unless you start experiencing java.lang.OutOfMemoryError errors.

To recycle() documents while looping a document collection or view you can use a loop-construct like the one below:

DocumentCollection dc = agentContext.getUnprocessedDocuments();
Document doc = dc.getFirstDocument();
Document docTemp = null;
while (null != doc) {
   // do stuff with the document

   // get next document into docTemp
   docTemp = dc.getNextDocument(doc);

   // recycle doc
   doc.recycle();

   // transfer reference to keep loop going
   doc = docTemp;
}

Update: As mentioned on this comment recycling is more important in servlets than in agents. When I post on servlets I’ll give some more pointers.

Java in Notes/Domino Explained: Stack, heap and references


Memory-wise a program running on a computer uses two discrete pieces of memory – the stack and the heap. The stack is a small(er) piece of memory specific to the method being executed. The heap is much larger and may be shared between programs. No need to worry about the implementation details more than this.

When you instantiate an object in Java you are actually allocating two pieces of memory – one on the stack and one on the heap. The memory of the stack is used to store a reference and the memory on the heap is used to store the actual object. The primitive datatypes (int, long, char etc.) are only stored on the stack.

Have you ever been debugging a Java agent using System.out.println and seen something like “java.lang.Object@bbcab38” show up in the Java debug console? What you are seeing is actually the memory address of the object and not the object itself. This happens because there is no toString() method in the object so the JVM prints out the memory address since that’s the last resort to show something unique about the object.

The below figure shows that’s on the stack and the what’s on the heap from the perspective of the simple agent shown below running in Notes/Domino. Notice how the declared int only exists on the stack.

import lotus.domino.*;

public class JavaAgent extends AgentBase {
   public void NotesMain() {
      try {
         Session session = getSession();
         AgentContext agentContext = session.getAgentContext();
         int count=20;

      } catch(Exception e) {
         e.printStackTrace();
      }
   }
}

Of cause the above picture isn’t totally accurate since it doesn’t show all the objects created and references held by the objects on the heap but you get the picture. The lotus.domino.Database pointed to by the lotus.domino.AgentContext object is the current database, that is agentContext.getCurrentDatabase().

By reference vs. by value

In Java you only have direct access to what’s on the stack which means that what you manipulate in your agents are the references (i.e. on the stack) and not the objects themselves. This means that you in Java always program by reference and not by value. In fact there is no equivalent of the ByVal keyword in LotusScript. If you need to work on a copy of an object (i.e. ByVal) you’ll need to copy the object yourself – in Java-speak this is called cloning the object.

Java in Notes/Domino Explained: Tricks in the Java Debug Console


The Java Debug Console is found under FileToolsShow Java Debug Console in the menu and this is where the output of System.out and System.err goes. There are a number of secret keyboard commands you can use to show various information. Below is a listing of these commands.

Note: The cursor has to be in the output field (the big one on the top) for the commands to work.

Key Description
p Print a list of the system properties (an enumeration of System.getProperties()
d Toggle debug info.
m Show memory information
g Run garbage collection
h Show help (list commands)
f Finalize objects in queue
v Show Java version information
0-9 Set applet error level

Java in Notes/Domino Explained: Creating your own packages


The purpose of a package is to create a namespace for the classes to make similarly named classes from different packages distinguishable to the compiler and the JVM. Using packages you can have a class called MyClass in two packages but have the compiler know the difference between them. This really comes in handy when starting to work with third-party libraries since all libraries tend to have a Context or Document class.

Looking at the core Java API you’ll find two classes called Document. The Notes/Domino Java API also has a class called Document. The compiler and JVM are however able to distinguish between these three classes since they are in different packages:

  • javax.swing.text.Document
  • org.w3c.dom.Document
  • lotus.domino.Document

Creating packages

You create a package by using the package directive at the top of your class. The below class is in the mypackage package:

package mypackage;

public class Echo {
  public String echo(String s) {
    return "Echo: " + s;
  }
}

To use this class you have to import the package using the import directive (first example below) or specify the fully qualified name of the class (second example below).

import mypackage;

public class SomeClass {
  public void run() {
    Echo e = new Echo();
    System.out.println(e.echo("HelloWorld"));
  }
}
public class SomeOtherClass {
  public void run() {
    mypackage.Echo e = new mypackage.Echo();
    System.out.println(e.echo("HelloWorld"));
  }
}

Classes automatically have access to the classes in the same package so there’s no need to import the package into itself.

Package naming


Packages are normally named using a hierarchical naming scheme sort of like domain names. Domain names are in fact very often used to make sure that packages from a certain company are uniquely named. At my company we always start our package names with dk.it-inspiration for example.

When discussing package naming two points of confusion are common:

  1. Package name doesn’t have to unique in the World but simply within the scope of the code you write. You are free to think up any package name you like. You can even choose to start your packages with “com.ibm” if you are so inclined.
  2. Although package names are hierarchical they will not be imported in a hierarchical manner.

Point number 2 is often confusing to programmers new to Java so let me explain it in a little more detail. If you look inside the notes.jar file you’ll see that it contains a lot of packages as shown on the left. However even though you import lotus.domino.* in your code the classes from lotus.domino.cso, lotus.domino.local and lotus.domino.util will not be imported. You have to import these packages explicitly (although there is no need to do so).

If you come to Java from LotusScript I guess packages will be a welcome addition. Since LotusScript has no concept of namespaces for functions, you often run into issues with function and sub naming. For example you cannot write a function called Split since LotusScript already has a function of that name. In fact this was a major pain upon upgrading to Notes/Domino 6 where the function was introduced since I had already written a Split function in our Notes/Domino 5 applications. As part of the upgrade all our LotusScript code using this custom Split function had to be rewritten.

This wouldn’t have happened if LotusScript had had the concept of namespaces.

Java in Notes/Domino Explained: Dynamic class loading


When coding in Java you do not need to know the class you want to instantiate at compile time. You can have the program decide which class to instantiate, ask the user or let it be a configurable parameter. The only caveat with dynamic class loading is that the class you instantiate has to have a no-argument, public constructor.

To use the functionality you need two core Java language features:

  • Classloading
  • The ability to cast an object reference

Dynamic class loading is a much used feature when creating frameworks in Java since it allows you to specify the class to use in a property file outside the code. In the Notes/Domino world it is even easier since you can let the user choose the class to use in a database profile. To use dynamic class loading you use the forName() method of the java.lang.Class class (highlighted in bold):

Class clazz = Class.forName("java.util.Date");
Date date = (Date)clazz.newInstance();
System.out.println(date);

The above example is a little stupid since it simply creates a Date object and prints the date to the console but it shows how to use dynamic class loading.

In the Notes/Domino world you can use dynamic class loading to let the user select the behaviour he/she wants from a profile document. To to this you would write the different behaviours as a couple of different classes all implementing a common interface and then having the user choose which class to use in a profile document. Your agent could then read the classname from the profile document and instantiate the class specified there:

import lotus.domino.*;

public class JavaAgent extends AgentBase {

   public void NotesMain() {

      try {
         Session session = getSession();
         AgentContext ac = session.getAgentContext();

         // get database and profile document
         Database db = ac.getCurrentDatabase();
         Document doc_profile = db.getProfileDocument("JavaClassLoading", null);

         // get classname as a string
         String classname = doc_profile.getItemValueString("DBProfileClassname");

         // have Java load the class
         Class clazz = Class.forName(classname);

         // instantiate the class (we assume the class implements
         // the NameLoader interface specified in the agent)
         NameLoader nl = (NameLoader)clazz.newInstance();
         nl.loadNames();

      } catch(Exception e) {
         e.printStackTrace();
      }
   }
}

public interface NameLoader {
   public void loadNames();
}

I have used this approach many times by designing an interface and having multiple classes implement the interface. Then the actual class to use is selectable on a profile document making the application very configurable.

Another example of where you use dynamic class loading “in the wild” is for JDBC (database access from Java). Here you use the dynamic classloading and dynamic object instantiation of Java to tell the DriverManager (the class creating the actual connection to the database) which class to use for the JDBC connection. The below example creates a connection the SAMPLE database that comes with DB2 using the JDBC Application driver.

// load DB2 JDBC driver
Class.forName("COM.ibm.db2.jdbc.app.DB2Driver");

// create connection to sample database
Connection con = DriverManager.getConnection("jdbc:db2:sample");

// create statement
Statement stmt = con.createStatement();

// execute query
ResultSet rs = stmt.executeQuery("select * from employee");
...
...

Java in Notes/Domino Explained: More in depth on classloaders


When you run an agent in Notes/Domino the agent class is loaded into memory by a classloader (for more information about what a classloader actually is please refer to this previous post). In the case of Notes and Domino this classloader is the AgentLoader class from the lotus.domino package (that is lotus.domino.AgentLoader).
This is also the classloader which is responsible for loading all the other classes your agent and auxillary classes may use during their execution.

You can query any class in Java about which classloader actually loaded the class into memory. You do this using the getClassLoader()-method of the class instance as shown below:

import lotus.domino.*;

public class JavaAgent extends AgentBase {

   public void NotesMain() {

      try {
         Session session = getSession();
         AgentContext agentContext = session.getAgentContext();

         // get class for the current object
         Class clazz = this.getClass();

         // get classloader that loaded this agent class
         ClassLoader cl = clazz.getClassLoader();
         System.out.println("Default ClassLoader: " + cl.getClass().getName());

      } catch(Exception e) {
         e.printStackTrace();
      }
   }
}

The output of this agent in the Java Debug Console will be:

Default ClassLoader: lotus.domino.AgentLoader

In Java every classloader may be part of a hierarchy and hence a classloader may have a parent classloader. The only classloader that never has a parent is the top classloader called the bootstrap classloader which is installed by the JVM when initialized. The bootstrap classloader cannot be substituted in the case of Notes/Domino.

If a classloader is asked to load a class and it cannot find it, the request is delegated to the parent classloader if one is present. To demonstrate this we first need an extra classloader – let’s write our own classloader that loads classes from outside the Notes/Domino system classpath. Writing ones own classloader is actually VERY simple. You simply have to extend the java.lang.ClassLoader class and override one method. The code below shows how to write a classloader that loads classes from the root of the C-drive:

import java.io.*;

public class MyClassLoader extends ClassLoader {
   public MyClassLoader() {
      super();
   }
   public MyClassLoader(ClassLoader cl) {
      super(cl);
   }

   public Class findClass(String name) throws ClassNotFoundException {
      try {
         // use utility method to load class bytes from
         // the root of the C-drive
         byte[] b = this.loadClassData(name);
         return this.defineClass(name, b, 0, b.length);
      } catch (Exception e) {
         throw new ClassNotFoundException();
      }
   }

   private byte[] loadClassData(String name) throws Exception {
      // open a the requested file and read the bytes into an array
      InputStream in = new BufferedInputStream(
                       new FileInputStream("c:\" + name + ".class"));
      byte[] raw_classdata = new byte[1024];
      int length = in.read(raw_classdata);

      // since the byte array must be exactly the
      // correct length we copy the bytes into a
      // new array
      byte[] classdata = new byte[length];
      for (int i=0; i<length; i++) {
         classdata[i] = raw_classdata[i];
      }

      // return the byte array
      return classdata;
   }
}

As you can see it is quite easy and only the imagination sets limits as to where you could load classes from in your agents. You could actually write a classloader to load classes from a central class repository using some network protocol of your choosing.

Since we need some classes to load using our new classloader I have written two very simple classes called OutsideClasspath and OutsideClasspath2 as shown below. The classes should be compiled using the javac-tool or using Eclipse and placed in the root of your C-drive. If you do not want to compile the classes yourself you can download them using the links below.

public class OutsideClasspath {
   public OutsideClasspath() {
      ClassLoader cl = this.getClass().getClassLoader();
      System.out.println("Constructor of OutsideClasspath class " +
         "(loaded by: " + cl.getClass().getName() + ")...");

      // load another class
      OutsideClasspath2 oc2 = new OutsideClasspath2();
      System.out.println(oc2.echo("Hello from OutsideClasspath"));
   }
}

public class OutsideClasspath2 {
   public OutsideClasspath2() {
      ClassLoader cl = this.getClass().getClassLoader();
      System.out.println("Constructor of OutsideClasspath2 class " +
         "(loaded by: " + cl.getClass().getName() + ")...");
   }

   public String echo(String echo) {
      return "Echo: " + echo;
   }
}

Using our new classloader is also simple. We simply instantiate it and pass the bootstrap classloader as the parent classloader in the constructor.

import lotus.domino.*;
import java.util.*;

public class JavaAgent extends AgentBase {

   public void NotesMain() {

      try {
         Session session = getSession();
         AgentContext agentContext = session.getAgentContext();

         // get bootstrap classloader
         ClassLoader cl = this.getClass().getClassLoader();
         System.out.println("Default ClassLoader: " + cl.getClass().getName());

         // get classloader of the agent and show class name
         System.out.println("Agent class loaded by: " +
                this.getClass().getClassLoader().getClass().getName());

         // create our own classloader passing the
         // bootstrap classloader in the constructor
         cl = new MyClassLoader(cl);
         Class c = cl.loadClass("OutsideClasspath");
         Object o = c.newInstance();

         // create a java.util.Date object using our own classloader
         // to demonstrate the hierarchical nature and delegation
         // model of classloaders
         Class clazz_date = cl.loadClass("java.util.Date");
         Date d = (Date)clazz_date.newInstance();
         System.out.println(d);

      } catch(Exception e) {
         e.printStackTrace();
      }
   }
}

The output of the agent is as follows:

Default ClassLoader: lotus.domino.AgentLoader
Agent class loaded by: lotus.domino.AgentLoader
Constructor of OutsideClasspath class (loaded by: MyClassLoader)...
Constructor of OutsideClasspath2 class (loaded by: MyClassLoader)...
Echo: Hello from OutsideClasspath
Sat Apr 01 22:13:49 CEST 2006

The output demonstrates a couple of things:

  • The agent class is loaded by the bootstrap classloader.
  • That we are able to load classes using our own classloader what is we can load and instantiate an instance of the OutsideClasspath class.
  • That classes use the classloader that loaded them to load any classes they need. This is demonstrated by the fact that the OutsideClasspath2 class is automatically loaded by MyClassLoader when the OutsideClasspath class creates and instance of it.
  • That classloaders delegate the loading of classes to their parent if they cannot find the class. This is demonstrated by the fact that the request to load java.util.Date is delegated to the bootstrap classloader.

I hope this post has demonstrated the power of classloaders and how you can easily write one that suits your purpose.

Please note:

  • Even if we had not supplied the bootstrap classloader in the constructor of the MyClassLoader class a ClassNotFoundException exception would not have been thrown at runtime when asked to load the java.util.Date class. This is because the bootstrap classloader is always asked as a last resort. Setting the parent classloader is only important if you build your own hierarchy of classloaders as is the case in J2EE application servers.
  • The MyClassLoader class should be part of the agent if you decide to try the code out for yourself in Domino Designer.

Java in Notes/Domino Explained: Classloading


When doing Java development you write your code in classes and hence deal with classes. Each Java class in your source code is compiled into a binary class file.

JavaAgent.java (text) -(compile)-> JavaAgent.class (binary)

The compilation is done automatically for you when you write Java in Notes/Domino or if programming in Eclipse. It does however still help to be aware of what actually goes on under the covers when your agent is run.


“The process of finding the binary classes and loading them into memory is called classloading…”

The fact that each class is compiled into a separate file means that a Java agent consisting of 4 classes will contain 4 compiled classes. Since all the code isn’t combined into a separate file (like an EXE-file on Windows) the Java Virtual Machine (JVM) needs to locate these compiled classes at runtime and load them into memory so it can use them hence the word classloading.

The default classloader in Notes/Domino (lotus.domino.AgentLoader) is capable of loading classes from the classpath of the current agent which consists of:

  • Classes that are part of the agent itself.
  • Classes and JAR-files added to the agent using the “Edit Project”-button.
  • Java Script libraries added to the agent using the “Edit Project”-button.
  • Classes present on the Notes/Domino system classpath.

See the post “Managing external Java dependencies in Domino Designer” for more information on the Notes/Domino system classpath.

The exceptions

There are three exceptional situations that can and do occur frequently when working with Java in relation to classloading. These are the java.lang.ClassNotFoundException, the java.lang.NoClassDefFoundError and the java.lang.UnsupportedClassVersionError. Below are short excerpts from their explanation in the JDK. When programming in Notes/Domino you will probably encounter the first two at some point. The third I do not wish upon you… 🙂

ClassNotFoundException Thrown when an application tries to load in a class through its string name using the forName method in class Class.
NoClassDefFoundError Thrown if the Java Virtual Machine or a ClassLoader instance tries to load in the definition of a class (as part of a normal method call or as part of creating a new instance using the new expression) and no definition of the class could be found.
UnsupportedClassVersionError Thrown when the Java Virtual Machine attempts to read a class file and determines that the major and minor version numbers in the file are not supported.

The ClassNotFoundException exception is encountered when you try to dynamically instantiate a class using a string name (java.lang.Class.forName()) and the class cannot be found in the classpath. This is a checked exception you must catch when doing a Class.forName() call.

The NoClassDefFoundError is raised if you use a class in your agent which in turn uses another class that cannot be loaded at runtime. The most common scenario for this is when using third-party library consisting of multiple JAR-files. In this case you may only have included one of the JAR-files in your agent to make it compile. The agent may compile but if classes in the included JAR-file uses classes in a second JAR-file, that wasn’t included in the agent, this error will occur.

The UnsupportedClassVersionError is less likely to occur if you are on Notes/Domino 6.x or 7.x but does occur quite a lot if you are using third-party libraries on older Notes/Domino versions. The binary Java class format changes slightly over time (much like the On-Disk-Structure of Notes databases) and since the JVM cannot be forward compatible it needs a way to tell you that you are trying to load a class into the JVM it doesn’t know how to handle (the version of the class format is wrong). The way around this is to try and compile the classes using a Java 1.1.8 compiler or using “classic” mode with newer compilers.

So why is this important?

To really appreciate the concept of classloading you need to realize that:

  1. Java classes are not loaded into memory before actually used.
  2. Java supports dynamic classloading.
  3. You can write your own classloader.
  4. Classloaders in Java are hierarchical and thus can delegate classloading to other classes.

You don’t have to worry too much about item 1. I’ll explain item 2 and 3 in detail in subsequent posts to avoid making this post too long. Item 4 deals with delegation of classloading in case a specific classloader cannot find a given class. Suffice it to say that these four concepts allows the JVM to conserve memory and you to write very powerful and highly dynamic agents/classes.

All this fuss about classloading might seem overly complex but consider the fact that many of the classes that you use in your Java agent aren’t some you have written. They are part of the Java Development Kit (the java.lang packages) or part of third-party libraries that you program and execute your agents against.

If there were no classloaders you could not have dynamic classloading and all classes would need to be loaded into memory before starting an agent. This could/would take up a lot of memory and be highly inefficient.