This article is an addition to "
Build servlets in JavaScript" that describes how you can develop your own servlet implementation using the XPages2Eclipse API in JavaScript language.
Instead of using JavaScript, you may want to develop the http request handler in Java language.
While you can already pass thru the servlet request/response objects from the invoked JavaScript servlet method to your own Java class, the initialization of the Rhino JavaScript engine for each HTTP request does have a performance impact that you can avoid: It produces unnecessary load on the Domino server and slightly extends execution time for the request processing.
It's actually pretty easy to declare a Java based servlet handler stored in an NSF database. Just create a small xml file called "x2erpc.xml" in the base directory of the database's design (screenshot taken from the Java perspective of Domino Designer):
Please note that you need to flush the classloader cache and (if used) the class cache file after any change in the x2erpc.xml file and in Java files stored in the database design. See the article about the NSF classloader for details how to do this.Servlet URL syntax
Similar to JavaScript servlets, there are two formats for the URL to call the servlet handler.
On a Domino server, the syntax looks like this:
http://-hostname_of_server-/-path_to_database-/x2rpc/
j/-alias-
e.g.
http://www.mindoo.com/office/address.nsf/x2erpc/j/rest/persons
to call a handler for the alias "/rest" and pass the pathinfo value "/persons".
For the Notes client, the syntax is a bit more complicated:
http://127.0.0.1:-dynamic_XPages_Port-/x2erpc/-UTF8_encoded_server-!!-UTF8_encoded_dbpath-/
j/-alias-
e.g.
http://127.0.0.1:1234/x2erpc/Server1%5cMindoo!!office%5caddress.nsf/j/rest/persons
As for the JavaScript servlets, you can use the following snippet to calculate the URL for the current environment:
//use type "j" for Java based handler:
String url=com.mindoo.xpages2eclipse.tools.URLHelper.getRpcUrl(database, "j");
Sample application
We took the sample application of the
JavaScript servlet article and replaced the JavaScript REST service implementation with the corresponding Java code.
Here is a screenshot of the Ext JS 4 based grid that loads data from a Java servlet handler:
When you navigate through the entries in the sample database, the grid automatically fetches new chunks of data (200 entries per request) in JSON format from our REST service. Loading new entries in Java feels faster than the JavaScript implementation, navigating through the entries is snappier.
Here is the Java code of the request handler:
package com.mindoo.rest;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import lotus.domino.Database;
import lotus.domino.Session;
import lotus.domino.View;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerator;
import com.mindoo.cms.tools.extlib.DefaultViewParameters;
import com.mindoo.cms.tools.extlib.RestViewNavigator;
import com.mindoo.remote.api.IRemoteEclipseConnection;
import com.mindoo.xpages2eclipse.rpc.IRPCHandler;
import com.mindoo.xpages2eclipse.tools.ServletHelper;
/**
* RPC Handler implementation that provides a REST service to read the persons documents
* in this database
*
* @author Karsten Lehmann
*/
public class RestHandler implements IRPCHandler {
public void serviceRPC(HttpServletRequest req, HttpServletResponse response,
Session session, Database database, IRemoteEclipseConnection conn)
throws ServletException, IOException {
String method=req.getMethod();
if (!"GET".equals(method)) {
System.out.println("Unsupported http method: "+method);
ServletHelper.sendHttpError(response, 500, "Unsupported http method: "+method,
null);
return;
}
String pathInfo=req.getPathInfo();
if (pathInfo==null) {
System.out.println("Missing pathinfo value");
ServletHelper.sendHttpError(response, 500, "Missing pathinfo value", null);
return;
}
if (pathInfo.equals("/persons")) {
//we only support the /persons service for now
try {
handlePersonRequest(req, response, session, database, conn);
} catch (Exception e) {
e.printStackTrace();
ServletHelper.sendHttpError(response, 500,
"Could not read data from database", e);
return;
}
}
else {
System.out.println("Unsupported pathinfo value: "+pathInfo);
ServletHelper.sendHttpError(response, 500,
"Unsupported pathinfo value: "+pathInfo, null);
return;
}
}
/**
* Java method that handles the persons rest API request
*
* @param req servlet request
* @param response response
* @param session current Notes session
* @param database current Notes database
* @param conn XPages2Eclipse connection or <code>null</code> if running on the server
* @throws Exception
*/
private void handlePersonRequest(HttpServletRequest req,
HttpServletResponse response, Session session, Database database,
IRemoteEclipseConnection conn) throws Exception {
//read start and limit parameter sent by the Ext JS 4 grid
int start=ServletHelper.getParamInt(req, "start", 0);
int limit=ServletHelper.getParamInt(req, "limit", -1);
//open People view in database
View peopleView=database.getView("People");
//autoUpdate=false needed for the accelerated ViewNavigator
//used in the RestViewNavigatorFactory class (uses
//new buffered mode in 8.5.2)
peopleView.setAutoUpdate(false);
//to read the view content, we are using a few classes from
//the Domino Extension Library (Apache License) that unify
//several different sorts of view data access
DefaultViewParameters params=new com.mindoo.cms.tools.extlib.DefaultViewParameters();
RestViewNavigator nav=com.mindoo.cms.tools.extlib.RestViewNavigatorFactory
.createNavigator(peopleView, params);
//send json mimetype to browser
response.setContentType("application/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache");
response.setCharacterEncoding("UTF-8");
//instantiate Jackson library that is used to stream out the JSON code
JsonFactory jsonFactory=new org.codehaus.jackson.JsonFactory();
JsonGenerator jg = jsonFactory.createJsonGenerator(response.getWriter());
jg.writeStartObject();
//send the total number of entries as "totalCount"
jg.writeBooleanField("success", true);
int totalCount=nav.getTopLevelEntryCount();
if (limit==-1)
limit=totalCount;
jg.writeNumberField("totalCount", totalCount);
//now iterate through the view data and write an array entry for each row
jg.writeArrayFieldStart("data");
int idx=0;
for( boolean b=nav.first(start,limit); b && idx<limit; b=nav.next(), idx++) {
String unid=nav.getUniversalId();
Object lastName=nav.getColumnValue("LastName");
Object firstName=nav.getColumnValue("FirstName");
jg.writeStartObject();
jg.writeStringField("id", unid);
jg.writeStringField("firstname", firstName==null ? null : firstName.toString());
jg.writeStringField("lastname", lastName==null ? null : lastName.toString());
jg.writeEndObject();
}
jg.writeEndArray();
jg.writeEndObject();
jg.flush();
nav.recycle();
}
}