
One of the major advantages of developing in Java is the abundance of high quality open source libraries available to help you dramastically cut development time. Apache Jakarta and OpenSymphony are just two examples of sites that hosts high quality components you can leverage in your day-to-day developments (make sure to look into Jakarta Commons). The libraries are most often distributed in the form of one or multiple JAR files that you need link to when developing and compiling your code.
If you are unfamiliar with JAR files they are bascially ZIP files with a special directory structure. They are used to package compiled classes and let you handle one file instead of a whole directory tree. Much easier. For more information on JAR files refer to the page Packaging Programs in JAR Files at java.sun.com.
With external dependencies come complexity and issues as to where to place the JAR files so Domino Designer, the Notes client and the Domino server can find the code at design and runtime. When having external dependencies you basically have four options:
- Include the classes in the agent/web service you are writing.
- Put the classes in a Java script library and include the script library.
- Use the JavaUserClasses notes.ini setting.
- Put the classes in the jvm/lib/ext folder.
Below I’ll explain a little about each approach.
Include the classes in the agent/web service you are writing
This means using the “Edit Project” button in the Domino Designer Java IDE to include JAR files from the local file system in the agent/web service itself. This approach is valid and very easy to use but can mean dublicating the classes across multiple agents/web services in the same database. It is perfect for not having any dependencies outside the database.
Put the classes in a Java script library and include the script library
This means importing the JAR file(s) into a Java script library and then including the script library from agents/web services. This approach is for most cases the preferred way of using JAR files with Domino Designer since you avoid dublicating the code across design elements while avoiding dependencies outside the database. It also means that the code is easily shared among developers working on different agents concurrently.
Use the JavaUserClasses notes.ini setting
The JavaUserClasses notes.ini setting is the equivalent of the CLASSPATH setting used when developing in Java outside Notes. The setting makes the classes universally available so you can use them just like the classes from the java.*-packages lotus.domino.*-package. The setting is restricted in length (due to the limits imposed on notes.ini settings) and isn’t sharable across clients.
Put the classes in the jvm/lib/ext folder
Putting external JAR files in the jvm/lib/ext folder under your binary Notes directory affords you the same universal availability of the classes as the JavaUserClasses setting but without having to manually edit the notes.ini file. This possibility was introduced with Notes 6. Putting the classes in the jvm/lib/ext folder has some additional advantages when it comes to security but that’s for another post.
The table below shows the applicability of the different approaches.
td.center {
text-align: center;
}
| Designer | Notes | Domino | Replication | Sharable | Agent | Web service | Servlet | |
|---|---|---|---|---|---|---|---|---|
| Include in agent | + | + | + | + | – | + | – | – |
| Include in web service | + | + | + | + | – | – | + | – |
| Script library | + | + | + | + | + | + | + | – |
| JavaUserClasses | + | + | + | – | + | + | + | + |
| jvm/lib/ext | + | + | + | – | + | + | + | + |
As the table shows it is important to note that the JavaUserClasses and jvm/lib/ext approach are specific to the local machine that is it doesn’t replicate. This means that all installations (clients and servers) must maintain the setting manually. This can really be a hassle and doesn’t lend itself for use in applications deployed to end users. The approaches are very usable for servers though.
The table also shows that classes imported into a specific agent are not sharable that is you have to import the JAR file in every agent that uses the classes thus you have to remember updating the JAR file in every agent if upgradering to a new version of the JAR file. It can be a strength though if different versions are required. I recommend avoiding this approach whenever possible. Use script libraries instead.
Finally only the JavaUserClasses and jvm/lib/ext approaches can be used to load classes for servlets running under the Domino servlet manager.
Conclusion
In conclusion you should pay attention to what you are trying to accomplish when choosing the location for the JAR files you depend on. Each approach has its strengths and weeknesses.
For most cases the preferred way is using Java script libraries.
Hi Lekkim
Thanks for touching the topic,no one else touched.
I have one more question.
I have a database A where I am running the agent and I have other database B where I have user profile,database A have a profile document which a field called LocalReplica and in this field user can get the name of database B, which is local replcia, now when I will schedule the agent how the java agent will know that where the database B is.
Thansks
Well I find it a little difficult to understand the question but you should use the profile document I guess.
Mikkel,
I tried including the .JAR files in the agent using the “Edit Project” button. Do I have to use the “import” statement (in the Java agent) when I include the .JAR files in the project? I get compile errors and runtime errors without the import statement.
Yes. Including JAR-files via the “Edit Project” button only puts the JAR-files on the “CLASSPATH” of Notes/Domino (i.e. makes Notes/Domino aware of the files). You still need to import the packages you use.
I’m importing some of the WRQ Verastream Java classes and all goes well until I attempt to connect to a Verastream model. The following works fine:
AppConnSession VHIconn;
AppConnRecordSet records;
AppConnRecord record;
List vOPList = new LinkedList();
Map vFilters = new HashMap();
Map vInputs = new HashMap();
Vector row;
This part, however, causes a runtime error:
//Connect to the VHI Model
VHIconn = new AppConnSession();
VHIconn.connectToModel(, ,,, null);
The error I get is “java.lang.NoClassDefFoundError.”
Well the java.lang.NoClassDefFoundError is thrown by the JVM if the class definition cannot be found by the classloader when the class definition is required. If you are able to compile the code but you get the specified error at runtime my guess is that you imported a JAR-file with source code and not compiled classes. The easiest way to check this is to rename the JAR-file to .zip and check the contents using Winzip. Way easier than using the JAR command line tool.
I already checked, the .JAR contains only compiled class files. The funny thing is that the error seems to reference a path I’m unfamiliar with:
“java.lang.NoClassDefFoundError: com/wrq/apptrieve/include/EnumCommands”
It should be “com.wrq.apptrieve.appconn”
Thanks.
If you cannot find the com.wrq.apptrieve.include.EnumCommands class in the imported JAR-file you probably need to import another JAR-file with supporting classes. A classname like “EnumCommands” could be some kind of shared utility class placed in a separate JAR.
Thanks, Mikkel. I think I’m on the right track now. I found the .JAR file containing that class an imported it as well. I’m getting an error associated with a properties file now, but I am making progress.
Thanks again.
have played around with implementing java in notes. tried including .jar files in an agent via the “Edit Project” button but this still requires me to include .jar files in a path that notes recognizes. under “agent/web service” option in this article I was under the impression that if I included the .jar file in the agent this way, then it would actually be stored in notes so that the .jar file would not have to physically reside on the local file system anymore. that does not appear to be the case. am I mistaken?
Well it should be sufficient to include the jar file in the agent or on the classpath of Notes (e.g. JavaUserClasses) but it has been my experience (at least in earlier versions of Notes) that the jar file would be “dropped” from the agent. The only solution I have found to this is to recreate the agent fra scratch and copy/paste the code over.
I experience the same, at least with the jar files for connecting to DB2. Including the jar file with the agent gives error on server, but putting that same jar file in ext folder works fine.
Does adding jar files require a reboot of the server or just the HTTP service?
My experience is that a complete server restart is necessary for jar-files in the jvm/lib/ext directory whereas classes to the servlet manager only requires a HTTP task restart.
Thanks Lekkim for intiating this topic and valuable contribution.
I have followed the first approach of adding Jar files directly to agent project itself.
The code is succefully compiled but while running the code I am getting error as “Error loading Agent Base Class”.
I could run this code perfectly on J2SDK 1.3.1. But unable to run the same as Java agent.
Please suggest the solution.
Sounds like you are using imported Java instead of writing the code in the Domino Designer IDE – am I right? If that’s the case you might want to check out my post on using imported Java in Notes.
Is there a stacktrace?
Hi Mikkel,
Great article on this issue!
I’ve a problem: I copied the JCO-jar file for connection with SAP in “jvm/lib/ext”. This works fine when testing locally, but on the server I get:
“HTTP JVM: java.lang.NoClassDefFoundError: com/sap/mw/jco/JCO”
What could be the difference? Any idea? Thanks in advance!
The most common issue is that jvm/lib/ext and JavaServerClasses are local settings so you have to apply the setting both on the server and on any development workstation.
Hi Mikkel,
Thanks for your answer. I can’t find anything about JavaServerClasses, so I presume you meant JavaUserClases? I didn’t touch the JavaUserClasses, but I copied the necessary jar-file to jvm/lib/ext, locally as well as on the Domino server. Anything else I might have forgotten?
Yes sorry – meant JavaUserClasses… Silly me. Did you restart the Domino server after putting the jar-file in jvm/lib/ext? (that’s required) If restarting the Domino server isn’t an option you can attach the jar-file to the agent itself using the Edit Project button.
Hi Mikkel, and thanks for all those informations about Java in Notes/Domino.
I got a question on this subject, perhaps also related to the classloader (I’ve already read your articles about the classloader).
I’m not working with Java agent but with an applet embedded in a form. I’m currently stucked on a problem which seems to be related to the “classpath”.
First case, the main class (which is in a JAR file) of my applet is placed in an applet resource of my database. Then, the embedded applet object refers to the applet resource. When I open a document containing the applet, everything is fine and even getting the classloader class name returns “COM.ibm.JEmpower.applet.AppletClassLoader”.
Then, second case, to enhance the loading time of my applet (per example, in low-bandwith network environment), I place my JAR file somewhere on the local machine and then store a reference to this file in JavaUserClasses variable of my local notes.ini. Doing this also works fine, except that asking for the class name of the classloader returns “null”.
In fact, I’ve noticed this difference between the two approachs when I’ve tried to instanciate a JSObject to make liveconnect communication between my Java applet and the Javascript stored in the underlying form. In the first case, it is possible to get a JSObject, in the second case, this object is null.
I’ve also noticed something interesting by loading two applets in the same form, considering that the first applet has its class files stored in an applet resrouce in the database and that the second applet has its main class files stored locally and accessed through the JavaUserClasses. By doing this, I’ve seen that static objects are not shared as I’ve expected they would be, because I though they were loaded in the same JVM… It seems that I’m wrong…
Did you, or anyone else, have an experience with that and perhaps a solution to provide?
I have noticed that there is an issue with instance fields when you put all your java classes in a .jar file in the ext directory and reference them from a java agent. In my example, I have an agent extending an abstract class in the the .jar file, which in turn extends AgentBase. The abstract class has some fields with package level access (i.e. no modifier), which should obviously be available to the agent. However, when the agent tries to run, you get a java.lang.IllegalAccessError error, since the agent cannot see the fields; this doesn’t happen if the classes are stored in script libraries or are imported into the agent. I fixed this by making the fields protected, and thus explicitly available to extending classes. I found a very old post on notes.net which suggested this might be to do with ClassLoaders, but I would like a definitive answer.
It sure sounds strange. I tried to reproduce in Notes 7.0.1 and was able to at first but on subsequent tries I wasn’t able to reproduce any more. I think it was because I inadvertantly loaded an old jar from jvm/lib/ext. After resolving that I could consistantly reproduce the issue.
I haven’t experienced the issue before but it certainly looks like a bug in how the built-in JVM works. However I wouldn’t consider it a classloader bug since it really hasn’t got anything to do with classloading. It’s more an issue with how the JVM determines access to the fields declared in the bytecode and hence an incompability between the compiler in Domino Designer and the JVM the AMgr uses.
I think you should report the issue to Lotus Support as a bug. You are welcome to tell them that developers, independently from you, can reproduce the issue.
What you can do as a workaround is to create an accessor method with protected access:
public abstract class MyAgentBase extends AgentBase { // declarations Session session = null; protected Session getMySession() { return this.session; } }Well I haven’t been playing too much with applets on forms so I’m at a loss here. I would however also have thought that the applets would be loaded by the same JVM and hence static fields should be accessible from the two applets. You’re sure it’s not a code problem – it has been seen before… π If you post some code I’ll be happy to look at it for you.
As to your other issue with JSObject (it sure seems like you have been messing with it for a while) I do not think it is necessarily a classloading issue. How do you obtain the JSObject?
Hi, and thanks for the answer.
Well, I hope it’s not a code problem, but I can’t be sure… Perhaps I’ve missunderstood something and have it all wrong since the beginning of my tests π
Anyway, to help you or anybody who wants to have a look at this, I’ve prepared this example based on what I try to do in my real application.
The file “test_applets.zip” contains the source code of the two applets, two JAR files containing each of the compiled applet and a Notes database with the implementation of those applets.
To be sure the example will run in the same kind of conditions, you first have to place the file “localclasses.jar” somewhere in your file system and then to reference the path to this file in the JavaUuseClasses (or JavaUserClassesExt) variable in the notes.ini. Once done, you can (re)start your Notes client 6.x or 7.x.
Then open the database and create a document based on form “Local and DB applets”. This will start the applets in the same form. After that, I suggest to open files “WindowJSObjectApplet.java” (source of the “database applet”) and “MainApplet.java” (source of the “local applet”) and have a look at the code to understand what’s happening and what should be displayed in the output console.
What I try to do is to set the window JSObject from the “database applet” into the “local applet”. The “database applet” seems to have no problem to do that, but once in the “local applet”, the window JSObject is still null and so it is not possible to call some Javascript. You’ll also notice that trying to create a window JSObject directly from the “local database” is not a solution as this also returns always a null object…
I hope there is enough information here to help you start understanding what I’m trying to do and perhaps found something new π
One more thing, if you start changing the source code of the “MainApplet” class, remember that once compiled in the “localclasses.jar” file and you’ll each time have to replace this file where you initialy copy it in your file system. Then you’ll have to restart your Notes client to make the changes available.