I want to set up some basic REST services for my XPage application. So I added the xe:restService control on an xpage and choose the xe:customRestService where I refer to a Java class:
<xe:restService id="restService1" pathInfo="json" state="false">
<xe:this.service>
<xe:customRestService contentType="application/json"
serviceBean="se.banking.desk.CustomSearchHelper">
</xe:customRestService>
</xe:this.service>
</xe:restService>
The CustomSearchHelper class it self is still pretty empty but I am wondering if I am on the right track?
Here is the code for the class:
package se.banking.desk;
import java.io.IOException;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
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 CustomSearchHelper extends CustomServiceBean {
@Override
public void renderService(CustomService service, RestServiceEngine engine) throws ServiceException {
HttpServletRequest request = engine.getHttpRequest();
String method = request.getMethod();
HttpServletResponse response = engine.getHttpResponse();
response.setHeader("Content-Type", "text/javascript; charset=UTF-8");
if(method.equals("GET")){
this.get(engine);
}
else if(method.equals("POST")){
this.post(engine,request);
}
else{
this.other(engine);
}
}
public void get(RestServiceEngine engine){
HttpServletResponse response = engine.getHttpResponse();
try {
response.getWriter().write("get()");
response.getWriter().close();
return;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public void post(RestServiceEngine engine,HttpServletRequest request){
HttpServletResponse response = engine.getHttpResponse();
Map parameters = request.getParameterMap();
try {
response.getWriter().write("post()");
response.getWriter().write( request.getParameter("form"));
String[] form = (String[])parameters.get("form");
String val = form[0];
response.getWriter().write(val);
response.getWriter().close();
} catch (Exception e) {
// TODO: handle exception
}
}
public void other(RestServiceEngine engine){
HttpServletResponse response = engine.getHttpResponse();
try {
response.getWriter().write("other()");
response.getWriter().close();
return;
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
Questions I have: Is this a good way to write a custom REST service? Are there alternatives? Where can I find more examples/information that start at the starter level?
You've asked a rather complex question, one which has been on my mind for the last couple of years. My assessment is that finding the "good way" comes down to the developer(s) and conventions used within the app. I've included links to sources of the alternatives I see available, a few mine, those of which attempted to tackle some of the concepts from the ground up, like my series on http servlets.
[Update]I've edited this answer to include some code examples, as there is always the possibility of links eventually not working; which should preserve the intent of the answer.[/Update]
Your implementation is a great example of how the xe:restService control can be easily bound to an XPage, with the wide variety of options available to use within the XPages runtime and Domino server.
So far as I can tell, there are about 5 unique(-ish) ways of implementing a RESTful API/endpoint for operation within an XPages context. In general order of ease of implementation (depending on the person):
<?xml version="1.0" encoding="UTF-8"?>
<xp:view
xmlns:xp="http://www.ibm.com/xsp/core"
rendered="false"
viewState="nostate">
<xp:this.afterRenderResponse>
<![CDATA[#{javascript:com.demo.DataProvider.myCustomDataServiceAsJson();}]]>
</xp:this.afterRenderResponse>
XAgent. This will not render as a page, but as application/json data.
</xp:view>
<?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="beers">
<xe:this.service>
<xe:customRestService
contentType="application/json"
requestContentType="application/json">
<xe:this.doGet><![CDATA[${javascript:var resp = {
"data": [
{ "key": "value" }
],
"error": false
};
return toJson(resp);}]]></xe:this.doGet>
</xe:customRestService>
</xe:this.service>
</xe:restService>
</xp:view>
bluemix
branch) can be seen running on Bluemix; the sample here follows Jesse's blog postpublic class SampleServlet extends DesignerFacesServlet implements Serializable {
private static final long serialVersionUID = 1L;
@SuppressWarnings("unchecked")
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
// Set up handy environment variables
HttpServletRequest req = (HttpServletRequest)servletRequest;
HttpServletResponse res = (HttpServletResponse)servletResponse;
ServletOutputStream out = res.getOutputStream();
FacesContext facesContext = this.getFacesContext(req, res);
try {
res.setContentType("text/plain");
// write some amazing code!
out.println("done");
} catch(Exception e) {
e.printStackTrace(new PrintStream(out));
} finally {
out.close();
// It shouldn't be null if things are going well, but a check never hurt
if(facesContext != null) {
facesContext.responseComplete();
facesContext.release();
}
}
}
}
As for what is a "good way", I think they all have their use, especially when factoring in the concerning developer's skill level. For those looking to get started with such a thing in an XPages app, I recommend what you're doing, an xe:restService with either a CustomServiceBean extending class, or a simple class or bean which has its one-off methods
[Update]
Shean P. McManus and I gave a session for the ICONUS (fka- IamLug) two day virtual event on "Normalizing XPages Development". Much of the subject of what options are available when creating a RESTful API for use with an XPages application were covered. Slides are available both from Shean's blog and the git project repository on GitHub; the latter contains the application code, as well as a pre-built, stand alone NSF.
[/Update]