Part 4 :: Create a REST api to read data from a Notes database

To connect the newly created Vaadin application to our existing Notes application, we have to talk about the api. I strongly recommend to use REST as your api choice and don’t do it with notes java api or any other proprietary things. IBM gives us a possibility to access data with view based REST services, but I also recommend to not go this way.
My recommendation is to create your own api. It is quite easy and you have the full control to create a nice looking and readable json format and don’t have to struggle with this weired format provided from built in view rest services. It is quite easy to build your own api and you can use it also to write data, convert data, execute backend functions, agents or do whatever you want.

For our projects, we have created a universal connector. This connector provides a universal interface for accessing any Notes database, read data, change data, create documents, run agents and more. In this demo, I want to show you the basics and therefore we will create a small and easy REST api especially for those functions we need. If you plan to use those APIs to access multiple databases, I would also encourage you to create such a re-usable universal connector for your modernisation projects. For sure, you can also ask me for a price of our connector… 😉

Let’s start to create your api. You find the “tasks demo” Notes application in the repository together with the Vaadin application. This application includes already the finished api, so you can just use it or create your own api. The next steps describe how this api can be created.

Create an empty Xpage and call it api. Switch to the Xpage perspective to see all controls on the right side. Drag & drop the REST Service control to your Xpage.

designer_client_rest_api_01

Now, click on your new rest service control to show its properties. Enter readData as path info, select xe:customRestService as service type, application/json as contentType and enter api.DataService as name for our service Bean.

Or you can switch to source mode and simply paste this XML to your Xpage:

<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xe="http://www.ibm.com/xsp/coreex">

<xe:restService id="restService1" pathInfo="readData">
<xe:this.service>
<xe:customRestService serviceBean="api.DataService" contentType="application/json">
</xe:customRestService>
</xe:this.service>
</xe:restService></xp:view>

The service Bean simply defines a Java class which will handle all incoming requests to this REST api. So, we have to create this java class now. Go to the Java view and create a new class called DataService. Package name for this example should be api (api.DataService was the name defined in the previous step as service bean name). Extend your class from the superclass com.ibm.xsp.extlib.component.rest.CustomServiceBean.

designer_client_rest_api_03

Below you see the first running “hello api world” code for your DataService class. Just add the code to your class and you will have a running hello world api.

package api;

import java.io.IOException;

import javax.servlet.http.HttpServletResponse;

import com.ibm.domino.services.ServiceException;
import com.ibm.domino.services.rest.RestServiceEngine;
import com.ibm.xsp.extlib.component.rest.CustomService;
import com.ibm.xsp.extlib.component.rest.CustomServiceBean;

public class DataService extends CustomServiceBean {

	@Override
	public void renderService(CustomService service, RestServiceEngine engine) throws ServiceException {
		try {
			//Create a hello world response
			HttpServletResponse response = engine.getHttpResponse();
			response.getWriter().write("Hello api world!");
			response.getWriter().close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
}

The method renderService will be called on each request. This is the entry point where we will start to code. Now, we just create a response and write the text “Hello api world!” to get the first running api.

Before we can test our api, just add an Anonymous entry to the ACL of your notes application. This disables security for this demo so that we don’t have to bother with authentication:

designer_client_rest_api_04

That’s it! Use any REST client or browser to access your api: http://dominoserver/pathtoyourapp.nsf/api.xsp/readData

Don’t get nervous when your api loads quite long on the first try. The Xpage and java code has to be compiled first. The subsequent calls are much faster.


Ok, let’s extend this running api example to be a little bit more useful.
Go back to your java class DataService and replace your code with the following code:

package api;

import javax.servlet.http.HttpServletResponse;
import lotus.domino.Database;
import lotus.domino.Document;
import lotus.domino.Session;
import lotus.domino.View;
import org.json.JSONArray;
import org.json.JSONObject;
import com.ibm.domino.services.ServiceException;
import com.ibm.domino.services.rest.RestServiceEngine;
import com.ibm.xsp.extlib.component.rest.CustomService;
import com.ibm.xsp.extlib.component.rest.CustomServiceBean;
import com.ibm.xsp.extlib.util.ExtLibUtil;

public class DataService extends CustomServiceBean {

@Override
public void renderService(CustomService service, RestServiceEngine engine) throws ServiceException {
	try {
		//Create an empty JSONArray. This array will contain all data objects.
		JSONArray jsonResponse = new JSONArray();

		//Create a Domino session
		Session session = ExtLibUtil.getCurrentSessionAsSignerWithFullAccess();
		Database thisdb = session.getCurrentDatabase();
		View view = thisdb.getView("tasks");

		//Loop through all documents found in the view
		Document doc = view.getFirstDocument();
		while (doc != null) {
			//TODO: You should do some checks here... is value empty, etc...

			//Create a separate JSON Object for each document
			JSONObject jsonObject = new JSONObject();

			//Add properties to the JSONObject, you want to return
			jsonObject.put("status", doc.getItemValueString("status"));
			jsonObject.put("topic", doc.getItemValueString("topic"));
			jsonObject.put("description", doc.getItemValueString("description"));
			jsonObject.put("dueDate", doc.getFirstItem("dueDate").getDateTimeValue());
			jsonObject.put("universalID", doc.getUniversalID());

			//Add the JSONObject to the JSONArray
			jsonResponse.put(jsonObject);

			//process next document in view
			doc = view.getNextDocument(doc);
		}
		//Create a response
		HttpServletResponse response = engine.getHttpResponse();

		//Change response content type to JSON
		response.setHeader("Content-Type", "application/json; charset=UTF-8"); 

		//Send the created JSONObject as response
		response.getWriter().write(jsonResponse.toString());
		response.getWriter().close();
	} catch (Exception e) {
		e.printStackTrace();
		}
	}
}

This is a straight forward example for this demo. In more complex applications, you should probably split this functionality into multiple methods or classes. It simply iterates through all existing documents and creates JSONObjects for them. All created JSONObjects will be returned as a JSONArray.

To get this code running, you have to import the package org.json into your notes database. Download the latest jar, for example from this location: http://www.java2s.com/Code/Jar/o/Downloadorgjsonjar.htm. Switch to the Jars view in your Designer client and use the Import Jar button to import the downloaded jar file.

designer_client_import_jar

Or you can just use the sample database provided in my repository.

Accessing your api again with the same url, should now look like this (don’t forget to create some tasks in your database):

rest_viewdata

That’s all we need to read data from the Notes application. We need another api to save or create new tasks. This will be explained in part 5