Skip to content

Instantly share code, notes, and snippets.

@edm00se
Last active December 23, 2019 05:02
Show Gist options
  • Save edm00se/7d1abeb5ee555631b638b3299cd66998 to your computer and use it in GitHub Desktop.
Save edm00se/7d1abeb5ee555631b638b3299cd66998 to your computer and use it in GitHub Desktop.
servlet implementation blog post code
String reqStr = IOUtils.toString(is);
Gson g = new Gson();
// create the tmp HashMap
Map<String,Object> tmpNwHouse = new HashMap<String,Object>();
// fill the values via Gson, self-referencing the HashMap class
tmpNwHouse = g.fromJson(reqStr, tmpNwHouse.getClass());
// iterate the values and put them into the proper HouseModel object
HouseModel nwHouse = new HouseModel();
Iterator<Map.Entry<String,Object>> it = tmpNwHouse.entrySet().iterator();
nwHouse.setEditMode(true);
while (it.hasNext()) {
Map.Entry<String,Object> pair = it.next();
String curProp = pair.getKey();
String curVal = (String) pair.getValue();
nwHouse.setValue(curProp, curVal);
it.remove();
}
// any additional validations, balances, notifications, etc.
nwHouse.save();
// 201 = "Created", should include "Location" header
res.setStatus(201);
res.addHeader("Location", "/xsp/houses/"+nwHouse.getUnid());
package com.hello.factory;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import com.ibm.designer.runtime.domino.adapter.ComponentModule;
import com.ibm.designer.runtime.domino.adapter.IServletFactory;
import com.ibm.designer.runtime.domino.adapter.ServletMatch;
/**
* The factory (a provider) implements IServletFactory and creates
* two maps, for key to package.class and key to servletname matching.
*/
public class ServletFactory implements IServletFactory {
private static final Map<String, String> servletClasses = new HashMap<String, String>();
private static final Map<String, String> servletNames = new HashMap<String, String>();
private ComponentModule module;
/**
* init adds the classes and servlet names, mapping to the same key.
*/
public void init(ComponentModule module) {
servletClasses.put("exhttpservlet", "com.hello.servlets.ExampleHttpServlet");
servletNames.put("exhttpservlet", "Example HttpServlet");
servletClasses.put("exdesignerfacesservlet", "com.hello.servlets.ExampleDesignerFacesServlet");
servletNames.put("exdesignerfacesservlet", "Example DesignerFaces Servlet");
servletClasses.put("exabstractservlet", "com.hello.servlets.ExampleAbstractedServlet");
servletNames.put("exabstractservlet", "Example AbstractXSP Servlet");
this.module = module;
}
/**
* The ServletMatch matches the path to the correctly identified servlet;
* by the routed key.
*/
public ServletMatch getServletMatch(String contextPath, String path)
throws ServletException {
try {
String servletPath = "";
// iterate the servletNames map
Iterator<Map.Entry<String, String>> it = servletNames.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, String> pairs = 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);
}
}
public class HouseCollection {
private static String colAllowMethods = "GET, POST";
public static void doGet(HttpServletRequest req, HttpServletResponse res,
FacesContext facesContext, ServletOutputStream out) throws IOException {
try {
// the HashMap will represent the main JSON object
HashMap<String, Object> myResponse = new HashMap<String, Object>();
// the ArrayList will contain the JSON Array's data elements
// which is another HashMap
ArrayList<HashMap<String,String>> dataAr = new ArrayList<HashMap<String,String>>();
Database db = Utils.getCurrentDatabase();
View vw = db.getView("houses");
ViewNavigator nav = vw.createViewNav();
ViewEntry ent = nav.getFirstDocument();
while( ent != null ) {
Vector<String> colVals = ent.getColumnValues();
HashMap<String,String> curOb = new HashMap<String,String>();
curOb.put("name", colVals.get(0));
curOb.put("description", colVals.get(1));
curOb.put("words", colVals.get(2));
curOb.put("unid", colVals.get(3));
dataAr.add(curOb);
ViewEntry tmpEnt = nav.getNext(ent);
ent.recycle();
ent = tmpEnt;
}
myResponse.put("dataAr", dataAr);
myResponse.put("error", false);
// IBM commons way of toJson
/*
* out.println(JsonGenerator.toJson(JsonJavaFactory.instanceEx,
* myResponse));
*/
// GSON way of toJson
Gson g = new Gson();
out.print(g.toJson(myResponse));
res.setStatus(200); // OK
res.addHeader("Allow", colAllowMethods);
} catch (Exception e) {
res.setStatus(500); // something pooped out
res.addHeader("Allow", colAllowMethods);
out.print( "{error: true, errorMessage: \""+e.toString()+"\"}" );
}
}
public static void doPost(HttpServletRequest req, HttpServletResponse res,
FacesContext facesContext, ServletOutputStream out) throws IOException {
try {
String unid;
ServletInputStream is = req.getInputStream();
// not that I'm using it, but the ServletRequestWrapper
// can be quite helpful
// ServletRequestWrapper srw = new ServletRequestWrapper(req);
String reqStr = IOUtils.toString(is);
// com.ibm.commons way
/*
JsonJavaFactory factory = JsonJavaFactory.instanceEx;
JsonJavaObject tmpNwHouse = (JsonJavaObject) JsonParser.fromJson(factory, reqStr);
Iterator<String> it = tmpNwHouse.getJsonProperties();
HouseModel nwHouse = new HouseModel();
nwHouse.setEditMode(true);
while( it.hasNext() ) {
String curProp = it.next();
String curVal = tmpNwHouse.getAsString(curProp);
nwHouse.setValue(curProp, curVal);
it.remove();
}
*/
// GSON way
Gson g = new Gson();
/*
* Direct reflection to the HouseModel breaks, as it
* extends AbstractSmartDocumentModel :'-(.
*
* To get around that issue, as I know that the House
* model is really a bunch of String key to String value pairs.
* The AbstractSmartDocumentModel class basically adds some helper
* methods to wrap a Map<String,Object> (representing the Notes
* Document's Field to Value nature) with things like an edit
* property, load (by unid) method, and save (for the obvious).
*/
Map<String,Object> tmpNwHouse = (Map) g.fromJson(reqStr, HashMap.class);
HouseModel nwHouse = new HouseModel();
nwHouse.setEditMode(true);
for (Map.Entry<String, Object> pair : tmpNwHouse.entrySet()) {
String curProp = pair.getKey();
String curVal = (String) pair.getValue();
nwHouse.setValue(curProp, curVal);
}
nwHouse.save();
unid = nwHouse.getUnid();
res.setStatus(201);
res.addHeader("Allow", colAllowMethods);
res.addHeader("Location", "/xsp/houses/"+unid);
}catch(Exception e) {
HashMap<String,Object> errOb = new HashMap<String,Object>();
errOb.put("error", true);
errOb.put("errorMessage",e.toString());
res.setStatus(500);
res.addHeader("Allow", colAllowMethods);
Gson g = new Gson();
out.print(g.toJson(errOb));
}
}
public static void handleUnexpectedVerb(HttpServletRequest req,
HttpServletResponse res, FacesContext facesContext,
ServletOutputStream out) {
res.setStatus(405);
res.addHeader("Allow", colAllowMethods);
}
}
// req is the passed in HttpServletRequest
ServletInputStream is = req.getInputStream();
Gson gson = new Gson();
MyBean myBean = (MyBean) gson.fromJson(IOUtils.toString(is), MyBean.class);
public class HouseModel extends AbstractSmartDocumentModel {
private static final long serialVersionUID = 1L;
private String name;
private String description;
private String coatOfArms;
private String words;
private String seat;
private String currentLord;
private String region;
private String title;
private String heir;
private String overlord;
@Override
protected String getFormName() {
return "house";
}
@Override
public void load(final String unid) {
super.load(unid);
}
@Override
protected boolean querySave() {
return true;
}
}
public class HouseRecord {
private static String recAllowedMethods = "GET, PUT, DELETE";
public static void doGet(String unid, HttpServletRequest req, HttpServletResponse res,
FacesContext facesContext, ServletOutputStream out) throws IOException {
try {
// create a House model object in memory and load its contents
HouseModel myHouse = new HouseModel();
myHouse.load(unid);
//return the contents in JSON to the OutputStream
// com.ibm.commons way
/*
* out.print(JsonGenerator.toJson(JsonJavaFactory.instanceEx,
* myHouse));
*/
// GSON way
Gson g = new Gson();
out.print( g.toJson(myHouse) );
res.setStatus(200);
res.addHeader("Allow", recAllowedMethods);
} catch(Exception e) {
res.setStatus(500);
res.addHeader("Allow", recAllowedMethods);
Map<String,Object> errOb = new HashMap<String,Object>();
errOb.put("error", true);
errOb.put("errorMsg", e.toString());
Gson g = new Gson();
out.print(g.toJson(errOb));
}
}
public static void doPut(String unid, HttpServletRequest req, HttpServletResponse res,
FacesContext facesContext, ServletOutputStream out) throws IOException {
try {
// GET existing
HouseModel exHouse = new HouseModel();
exHouse.load(unid);
ServletInputStream is = req.getInputStream();
String reqStr = IOUtils.toString(is);
Gson g = new Gson();
// setting the keys/values into the tmpNwHouse Map
Map<String,Object> tmpNwHouse = (Map) g.fromJson(reqStr, HashMap.class);
// suppressing just this warning throws an error on tmpNwHouse
tmpNwHouse = g.fromJson(reqStr, tmpNwHouse.getClass());
HouseModel nwHouse = new HouseModel();
nwHouse.setEditMode(true);
// compare/update
for(Map.Entry<String, Object> pair : tmpNwHouse.entrySet() {
String curProp = pair.getKey();
String curVal = (String) pair.getValue();
if( exHouse.getValue(curProp) != curVal ) {
exHouse.setValue(curProp, curVal);
}
}
// done setting new values back into the existing object
exHouse.save();
res.setStatus(200);
res.addHeader("Allow", recAllowedMethods);
} catch(Exception e) {
res.setStatus(500);
res.addHeader("Allow", recAllowedMethods);
Map<String,Object> errOb = new HashMap<String,Object>();
errOb.put("error", true);
errOb.put("errorMsg", e.toString());
Gson g = new Gson();
out.print(g.toJson(errOb));
}
}
public static void doDelete(String unid, HttpServletRequest req, HttpServletResponse res,
FacesContext facesContext, ServletOutputStream out) throws IOException {
Session s = (Session) facesContext.getApplication().getVariableResolver().resolveVariable(facesContext, "session");
Document houseDoc;
try {
houseDoc = s.getCurrentDatabase().getDocumentByUNID(unid);
houseDoc.remove(true);
houseDoc.recycle();
res.setStatus(200);
res.addHeader("Allow", recAllowedMethods);
} catch (NotesException e) {
res.setStatus(500);
Gson g = new Gson();
Map<String,Object> errData = new HashMap<String,Object>();
errData.put("error", true);
errData.put("errorMessage", e.toString());
errData.put("stackTrace", e.getStackTrace());
out.print(g.toJson(errData));
}
}
public static void handleUnexpectedVerb(HttpServletRequest req,
HttpServletResponse res, FacesContext facesContext,
ServletOutputStream out) {
res.setStatus(405);
res.addHeader("Allow", recAllowedMethods);
}
}
public class HouseServlet extends AbstractXSPServlet {
@Override
protected void doService(HttpServletRequest req, HttpServletResponse res,
FacesContext facesContext,
ServletOutputStream out) throws Exception {
// Accommodate two requests, one for all resources, another for a
// specific resource
Pattern regExAllPattern = Pattern.compile("/houses");
Pattern regExIdPattern = Pattern.compile("/houses/([0-9A-Za-z]{32})");
// set content type, cache, and Access-Control headers
res.setContentType("application/json");
res.setHeader("Cache-Control", "no-cache");
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
String pathInfo = req.getPathInfo();
// regex parse pathInfo
Matcher matchRecord = regExIdPattern.matcher(pathInfo);
Matcher matchCollection = regExAllPattern.matcher(pathInfo);
// Method invoking the URI
String reqMethod = req.getMethod();
/*
* Specific Document, by UNID. Allowed are GET,
* PUT, and DELETE.
*/
if (matchRecord.find()) {
String unid = matchRecord.group(1); // .group(1);
if (reqMethod.equals("GET")) {
// GET the single record
HouseRecord.doGet(unid, req, res, facesContext, out);
} else if (reqMethod.equals("PUT")) {
// PUT to update, in whole or part, a single record
HouseRecord.doPut(unid, req, res, facesContext, out);
} else if (reqMethod.equals("DELETE")) {
// DELETE single record
HouseRecord.doDelete(unid, req, res, facesContext, out);
} else {
// unsupported request method
HouseRecord.handleUnexpectedVerb(req, res, facesContext, out);
}
} else if (matchCollection.find()) {
/*
* Collection, allows only GET for the View equivalent or POST for
* creating a new Document
*/
if (reqMethod.equals("GET")) {
HouseCollection.doGet(req, res, facesContext, out);
} else if (reqMethod.equals("POST")) {
HouseCollection.doPost(req, res, facesContext, out);
} else {
// unsupported request method
HouseCollection.handleUnexpectedVerb(req, res, facesContext, out);
}
}
}
}
// in a method to parse route params
// Accommodate two requests, one for all resources, another for a specific resource
private Pattern regExAllPattern = Pattern.compile("/collection");
// a UNID is 32-char hex, /collection/{:unid}
// UNID ref: http://www-01.ibm.com/support/docview.wss?uid=swg21112556
private Pattern regExIdPattern = Pattern.compile("/collection/([0-9a-fA-F]{32})");
// regex parse pathInfo
Matcher matcher;
// Check for ID case first, since the All pattern would also match
matcher = regExIdPattern.matcher(pathInfo);
if (matcher.find()) {
unid = Integer.parseInt(matcher.group(1));
System.out.println("do something with this document, the id is: "+unid);
}
matcher = regExAllPattern.matcher(pathInfo);
if (matcher.find()) {
System.out.println("do something with the collection");
}
throw new ServletException("Invalid URI");
String reqPath = req.getPathInfo();
out.println("pathInfo: " + reqPath);
List<String> routeParm = Arrays.asList(reqPath.split("/(.*?)"));
if(routeParm.size() > 3) {
// /nsf/xsp/servletname is the base, so the fourth is the first routeParm
for( int i=3; i<routeParm.size(); i++ ) {
out.println( "routeParm: " + routeParm.get(i) );
}
} else {
// didn't have any route parameters after the base
out.println("routeParm: " + "none");
}
String curProp = pair.getKey();
String curVal = (String) pair.getValue();
if( exHouse.getValue(curProp) != curVal ) {
exHouse.setValue(curProp, curVal);
}
$.ajax({
beforeSend: function(xhr) {
xhr.setRequestHeader('X-HTTP-Method-Override', 'PUT');
},
type: 'POST',
url: '/someurl',
success: function(data) {
// do something...
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment