Creating a basic Domino Servlet

All of the code will be provided in a database I have to figure out how to make it available since free WordPress.com accounts don’t allow the storing of zip files.  Perhaps I could share a Dropbox folder?

If you’ve come here directly there is a short introduction available here: Creating a Servlet for IBM Domino

Apologies for the death by screenshot, I know the modern way is YouTube but I’m a sadist apparently 🙂  Some credit for the stuff in this post belongs to:

Sven HasselBach (via his blog http://hasselba.ch/blog/?p=746 which shares a link to a Chinese Developerworks article http://www.ibm.com/developerworks/cn/lotus/xpage-servlet/index.html)

Begin at the beginning…

I’m going start with a new blank application, you don’t have to.

Create a new application

It’s all java from now on.  You could open the Java Perspective or like me the Package Explorer view.

2-open-pack-exp-menu

This bit is optional…

This next little section isn’t necessary to get the Servlet running but I normally want to create variables whose values will not change during the lifecycle of the Servlet. If I create a mechanism for using them at the beginning I’m more likely to use it.

If you want to skip it look below for text < resume here 🙂 > below.

Right click on the Code/Java source folder (highlighted in the image above) and choose new.

Open the Package Explorer view

Expand the Java Folder and choose package.

3-new-pkg-misc

Name the package in reverse domain format. I’m going to use this package for classes that support the Servlet like Constants.

3-name-pkg-misc

Right click the new package and choose new. From the Java folder choose Class.

4-new-class-constants

Name the new Class. I’m calling this one Constants and I’ll use it to create static final variables that will be used every time the Servlet is instantiated (like global variables in Lotuscript).

4-nm-class-constants

The new Class will open in the Editor.  In the image I’m adding a new variable DEBUG. The convention I’m adopting is to have variables in this file named in uppercase.

5-new-debug-var

< resume here 🙂 >

Making a package, and Servlet Class

Right click the Code/Java folder and choose new. Create a new Package and name it.  I’ve called mine com.jho.servlets.

6-new-pkg

6-nm-pkg-servlets

Create a new Class by right clicking in the servlets package and choosing New and click OK.

7-nm-class-servlets

The new class will extend javax.servlet.HttpServlet

7-the-class

Making the Servlet talk

I’ve added some methods signatures.  Most take  HttpRequest and HttpResponse arguments. We’re going to work on the doGet method which will provide a response when we request http://<your server>/servlets.nsf/XSP/exampleservlet

7-more-methods

In the doGet method I create a PrintWriter that we’ll use to send data back to the requester. I’ve set the content-type on the response object to “text/plain”.  For other uses it might be set the “application/xml” or “text/html”.  The PrinterWriter responds with “Servlet Running” before we close it.

7-more-doGet

Our Servlet Class is now ready to do work but before it can be created by the Server we need to create a mechanism by which the it can map the request url to the Servlet and instantiate it.

Adding an external jar

Open the Project Properties, choose Java Build Path and click “Add External JARs”

project-properties-libraries

The jar we’re after is lwpd.domino.adapter.jar <Notes program directory>\osgi\shared\eclipse\plugins\com.ibm.domino.xsp.adapter_8.5.3.20110915-2025

project-properties-add-jar

Click Open, and then OK to close the Project Properties.

Building a Servlet Factory:

Create a new package and name it com.jho.factory or your domain.factory

8-new-pkg-factory

Right click on the package and create a new class.  Name the class ServletFactory and click Browse beside Interfaces.

In the choose Interface field type IServlet, and because we added that jar earlier Notes should know about the IServletFactory interface.  Choose that and click add.

8-servlet-factory-class

Click Finish to create the new Class which will open in the editor.

8-servlet-factory-class-code1

This class will match the the incoming url with a Servlet, and return a new instance of the Servlet.  I’ve added the code for the Class below.

8-servlet-factory-class-code3

Why am I using a HashMap for just one Servlet? Let’s say I wanted to create two or three Servlets and have this factory create instances of those. I can simply add to the HashMap. It’s not terribly efficient code, particularly if I had loads of Servlets but I don’t at the moment.

package com.jho.factory;
import javax.servlet.Servlet;
 import javax.servlet.ServletException;
 import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
import com.ibm.designer.runtime.domino.adapter.ComponentModule;
 import com.ibm.designer.runtime.domino.adapter.IServletFactory;
 import com.ibm.designer.runtime.domino.adapter.ServletMatch;
public class ServletFactory implements IServletFactory {
private static final Map<String,String> servletClasses = new HashMap<String,String>(1);
 private static final Map<String,String> servletNames = new HashMap<String,String>(1);
 private ComponentModule module;
public void init(ComponentModule module){
      servletClasses.put("exampleservlet","com.jho.servlets.ExampleServlet");
      servletNames.put("exampleservlet","Example Servlet");
 this.module = module;
 }
public ServletMatch getServletMatch(String contextPath, String path)
throws ServletException {
try {
 String servletPath = "";
 //iterate the servletNames map
 Iterator it = servletNames.entrySet().iterator();
 while(it.hasNext()){
 Map.Entry<String, String> pairs = (Map.Entry<String, String>)it.next();
 if (path.contains("/" + pairs.getKey())){
 String pathInfo = path;
 return new ServletMatch(getWidgetServlet(pairs.getKey()),servletPath,pathInfo);
 }
 }
 } catch(Exception e) {
    e.printStackTrace();
 }
    return null;
}

 public Servlet getWidgetServlet(String key) throws ServletException {
        return module.createServlet(servletClasses.get(key),servletNames.get(key),null);
 }
}

Save and close the class.

There’s just one more important step.

Expand the Code/Java folder and right click the folder.  Choose new and find “Folder” in the wizard.

Name the folder META-INF and ensure it’s being created in <your database>/Code/Java

9-new-folder-1

Create another folder within META-INF and call it services.

Create a new file within services and call it com.ibm.xsp.adapter.servletFactory. The file should open in the editor, add the fully qualified name of your ServletFactory class. In this example the only line in the file should read:

com.jho.factory.ServletFactory

9-new-meta-inf-file

Save and close the file.

Edit the database ACL and make the default reader, add an entry for anonymous and make that reader.  Build your project (because you have Build Automatically turned off 🙂 ). Copy or replicate the database to your server.

Open a browser, and go to the location:

http://<servers fqdn>/<path to database>/XSP/exampleservlet

For example http://www.jho.com/servlets.nsf/XSP/exampleservlet

Your server should return the text “Servlet Running”

browser

In the next post we’ll add some logging before making it a wee bit more functional.

Advertisements
Tagged with: , ,
Posted in Coding, Tips, XPages
17 comments on “Creating a basic Domino Servlet
  1. Genachka says:

    Thanks for this write up! It was very helpful and thanks to you I have a working servlet. But I found that for what I’m doing this approach isn’t working to work because of java.policy security issues and after much googling found that the recommended approach is creating a OSGI Plugin Servlet which allows the Java code run with full permissions.

    Was wondering if you could do a blog with a nice step by step on creating on Domino 8.5.3 using the OSGI Plugin Servlet approach?

    • 8b30b0 says:

      That’s good news (you have a working servlet!). I’ve just put an OSGi servlet into production. I could create a blog for that but the slides from Show112 (Lotusphere 2012) are what you need. I searched and didn’t find them shared anywhere. Do you have access to that presentation?

      • Genachka says:

        I don’t and too wasn’t able to find the slides. 😦 I’ve now seen the video that’s on OpenNTF by Niklas Heidloff and tried the sample (http://www.openntf.org/internal/home.nsf/project.xsp?action=openDocument&name=Servlet%20Sample) without success. And have also tried following the one from Karsten Lehmann on how he did a NoSQL MongoDB one (http://www.mindoo.com/web/blog.nsf/dx/27.04.2012190009KLEMXN.htm) also without success. Maybe it’s me but I’m just not getting it. 😦

        Maybe you’ll offer some advice. My domino server is 8.5.3, running on Linux and my development environment is a Notes with Designer 8.5.3 client running on a Windows 7 VM with Eclipse Juno SR1 also installed . From everything I can tell, I shouldn’t need to install the extension library or update site to get it to work. When I open the sample db from both Niklas and Karsten they complain of missing libraries:
        import lotus.domino.Database;
        import lotus.domino.NotesException;
        import lotus.domino.Session;
        import com.ibm.domino.osgi.core.context.ContextInfo;

        In the project’s build path, I added the external jars com.ibm.domino.osgi.core_8.5.3.20110915-2025.jar and notes.jar which satifies those errors. I then exported the project as a jar and manually copied it over to the domino server’s domino/workspace/applications/eclipse/plugins folder and restarted the domino http task from the domino console and then did a ‘tell http osgi ss ‘. From what I’ve seen in the videos and read, it should come back with a response something like:
        Framework is launched.
        id State Bundle
        RESOLVED

        But I don’t get the RESOLVED and of course when I try to access it via a browser, I don’t get anything back.

  2. 8b30b0 says:

    I think the slides will help you. I’ve tried to get in touch with one of the presenters to ask him if he could share the slides.

    You don’t need to add Notes.jar to your build path (but you will create a small project that references notes.jar in addition to your actual project).

    You’ll need to [Right Click your project] and choose Export your plugin project as “Plug-in Development\Deployable plugins and fragments”. On the options tab tick “Use class files compiled in the Workspace” which I think automatically ticks “Allow for binary cycles….”

    I’ll chase up the slidesharing piece. If I can’t progress it I’ll create a post that will cover the steps including the Domino debug plugin.

  3. Bruce Stemplewski says:

    Can we get a working example please? My issue is that for some reason the ServletFactory:getServletMatch
    does not always get called but I do see it being called sometimes.

    • 8b30b0 says:

      I don’t see why not. The free WP is a little restrictive but I could put something on Dropbox and link to that.

      I’m ridiculously busy at work but as soon as I can….

  4. Nutrideath says:

    Thanks Jason. I followed your directions & it works right out of the gate.

    However, I am not familiar with the syntax of the HashMap the way you are using it. Could you explain how to add a second (& third, fourth, etc.) servlet? I tried as follows:

    public void init(ComponentModule module){
    servletClasses.put(“exampleservlet”,”com.deepforestapplabs.servlets.ExampleServlet”);
    servletNames.put(“exampleservlet”,”Example Servlet”);

    servletClasses.put(“exampleservlet2″,”com.deepforestapplabs.servlets.ExampleServlet2”);
    servletNames.put(“exampleservlet2″,”Example Servlet 2”);
    this.module = module;
    }

    I created another servlet based upon the first example & named the class as above (exampleservlet2). I changed the output for “2nd Servlet Running” so I would know the difference. When I change the URL from “…XSP/exampleservlet” to “…XSP/exampleservlet2” it doesn’t work. I still get the output from the first servlet (“Servlet Running”).

    What am I doing wrong? Thanks in advance!

    • 8b30b0 says:

      I’m assuming that your code looks pretty much like mine below. You are using the method for creating Servlets that are within the .nsf.

      In theory (and for me in practice, in one application I have four servlets in the same nsf) this is how it works.

      A request is made to either /your.nsf/XSP/example1 or /your.nsf/XSP/example2 or /your.nsf/XSP/example3. When the server looks to see what will handle that request it uses the factory class to map the url to a class in the database.

      In the code below I used two maps servletClasses and servletNames to create that mapping.

      When the ServletMatch function is called (by the Server) the path is passed into the function and we iterate the servletNames map. If the path given to the function is contained within the key of an entry in the map (and the first one it finds) the function calls getWidgetServlet which returns a servlet (or throws an exception) by instantiating a class for the servletClasses map which has a matching key to the one found in the servletNames map.

      It’s not the most elegant solution but does work.

      package com.myorg.factory;

      import javax.servlet.Servlet;
      import javax.servlet.ServletException;
      import java.util.HashMap;
      import java.util.Iterator;
      import java.util.Map;

      import com.ibm.designer.runtime.domino.adapter.ComponentModule;
      import com.ibm.designer.runtime.domino.adapter.IServletFactory;
      import com.ibm.designer.runtime.domino.adapter.ServletMatch;

      public class CallBackFactory implements IServletFactory {

      private static final Map servletClasses = new HashMap(3);
      private static final Map servletNames = new HashMap(3);
      private ComponentModule module;

      public void init(ComponentModule module) {

      servletClasses.put("example1","com..myorg.ExampleServlet1");
      servletNames.put("example1","My First Servlet Example");

      servletClasses.put("example2","com..myorg.ExampleServlet2");
      servletNames.put("example2","My Second Servlet Example");

      servletClasses.put("example3","com..myorg.ExampleServlet3");
      servletNames.put("example3","My Third Servlet Example");

      this.module = module;
      }

      public ServletMatch getServletMatch(String contextPath, String path) throws ServletException {
      System.out.println("CallBackFactory::getServletMatch::"+path);

      try {
      String servletPath = "";
      //iterate the servletNames map
      Iterator it = servletNames.entrySet().iterator();
      while(it.hasNext()){
      Map.Entry pairs = (Map.Entry)it.next();
      if (path.contains("/" + pairs.getKey())){
      System.out.println("found a match::" + pairs.getKey());
      String pathInfo = path;
      return new ServletMatch(getWidgetServlet(pairs.getKey()),servletPath,pathInfo);
      }
      }
      } catch(Exception e) {
      e.printStackTrace();
      }
      return null;
      }

      public Servlet getWidgetServlet(String key) throws ServletException {
      return module.createServlet(servletClasses.get(key),servletNames.get(key),null);
      }

      }

      If you were using OSGi then things are easier. As you define extension points within your plugin.xml you create one for each servlet you create within your project.

      Apparently wordpress.com doesn’t like me inserting XML into the comments. So here’s a link to a text file.

      [Hope that helps. I’ll be offline for a week now probably]

      • Nutrideath says:

        Thanks again Jason. Yes, I got that to work… sort of.

        Yesterday after I posted my question to you I just kept trying different combinations. I got it to work. Evidently, I was using the correct syntax all along. But I had named the first servlet “exampleservlet” and the second “exampleservlet2”. I kept getting the first servlet only, and could never get to the second. I added a different servlet named “anotherservlet” and it worked. I believe the problem has something to do with the way ServletMatch compares the strings (uses “path.contains(…)). I think it was getting a hit on the first “exampleservlet” even when looking for “exampleservlet2”.

        Anyway, I got it to work last night. Life was wonderful. (Yay.) But this morning it doesn’t. I have not changed anything, but for some reason the only servlet that will ever get called is the first one in the HashMaps. Any other servlet, when called, returns only a blank page.

        Server console has no errors either. I am stumped. Any ideas anyone?

  5. Genachka says:

    Has anyone tried an OSGi servlet with Domino 9 and tried using the remote debug?

    • 8b30b0 says:

      Really you need to have a local server. The remote thing just doesn’t or hasn’t worked up to now.

      • 8b30b0 says:

        In the factory class if you put in some logging or Write to the console does it appear that the factory is receiving the path info and instantiating a servlet in all cases?

        What happens if you call a servlet that doesn’t exist don’t you get a blank page?

        You’ve cleaned and built your project I guess.

        OSGi is much better I think and there’s not much work to port your code into a plugin from a servlet in an NSF.

      • Genachka says:

        Sorry, should have been more clear.

        With the help of the slides that you referenced (thank you again so much for that btw!) I was able to get the OSGi servlet working perfectly on 8.5.3! I did follow the advice of a local Domino server on same box (in my case a VM) as the Domino Designer. I gave up on trying to getting it working with a remote server that was running on Linux. So I’ve been good on 8.5.3 for a couple weeks now.

        My reference to “remote debug”, I should have been more clear, I mean in eclipse the debug config “remote application server”, is connecting to a local Domino 9 server.

        I took my working 8.5.3 environment’s VM, cloned it and upgraded Notes, Designer, and the local Domino server to 9. And my OSGi servlet started having issues. Doing a “tell http osgi ss ‘servletname'” at the Domino console would show it, trying to connect to it via web browser would yield error 500. I also couldn’t get the eclipse debugger connect to the OSGi servlet on the local Domino server. I’m thinking that maybe there’s an incompatibility with the Debug Plugin or it’s something else I missed that needs to be done with OSGi servlet on Domino 9?

      • Jason hook says:

        I haven’t tried to work with 9.0 except during the beta. I tried to create an OSGi style servlet and designer ate all my code.

        I’ll have another look at 9 later if time allows or more likely the first fix pack comes out..

        But for now you are the pioneer :-)*

        Jason

        * I bet the guys at Red Pill Development are way ahead of us.

        Sent from my iPad

  6. Jeff B says:

    Thanks for a great article. I put it together this morning and it worked as advertised. I’m trying to expand on the idea and get the current session and database but I can’t seem to figure it out. Can you please pot some follow up code showing how to move this forward and do some work.

    Thanks,

    — Jeff

  7. Luka says:

    Im not sure if this is working properly. I put Thread.currentThread.getName() in init() and output result later in doGet(). Result are two different threads. I think init() must be called only once.

    • 8b30b0 says:

      I haven’t looked in to the servlet lifecycle on Domino in any detail. In my use cases so far I’ve been using them RESTfully all of the calls to the servlet contain all of the information needed to complete the operation. I expect the servlet container to create instances of the servlet as requests to that resource are made. What I’d like to know is how results of expensive operations can be persisted between calls to the servlet.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: