From Blogsphere to a Static Site (Part 4) - Comment backend
The blog needed a comment function. While there are social options around (Facebook, Disqus etc), I decided I want to roll my own. Partly because I want tighter control and partly, well, because I could. My comment backend would:
- Provide a REST API to create comments in a JSON structure. The comment body will be Markdown. Reading would provide comments in ready to use HTML (I hear howling from the API crowd). No delete or update functionality
- Cleanup content considered harmful (code injection) and optional sport Captcha
- Store all content in a NoSQL database, in my case CouchDB (or Cloudant with its 20G free plan)
- Cache all queries for comment in an online cache to limit calls to the database
- Initially run on Domino, later on liberty or the raw JVM
- Initially also update Domino using a web service - so during transition no comments would get lost
In its initial incarnation the Comment servlet is a OSGi plugin that listens to the /comments
URL implemented as Wink servlet. So the class of interest is the one defining the service. We have one method for post, one for get and a helper function
/**
* Wink implementation of Comment service
*/
@Workspace(workspaceTitle = "Blog Comments", collectionTitle = "Create or display comments")
@Path(value = "/comments")
@Produces(MediaType.TEXT_HTML)
@Consumes(MediaType.APPLICATION_JSON)
public class CommentService extends CommentResponse {
private final Logger logger = Logger.getLogger(this.getClass().getName());
@POST
public Response createComment(@Context HttpServletRequest request) {
final Monitor mon = MonitorFactory.start("CommentService#createComment");
String result = "Sorry I can't process your comment at this time";
ResponseBuilder builder = Response.ok();
try {
InputStream in = request.getInputStream();
BlogComment comment = BlogComment.load(in);
in.close();
if (comment != null) {
this.captureSubmissionDetails(request, comment);
result = CommentManager.INSTANCE.saveComment(comment, true);
} else {
builder.status(Status.NOT_ACCEPTABLE);
}
builder.entity(result).type(MediaType.TEXT_HTML_TYPE);
} catch (Exception e) {
String errorMessage = e.getMessage();
builder.entity((((errorMessage == null) || errorMessage.equals("")) ? "Undefined error" : errorMessage)).type(
MediaType.TEXT_HTML_TYPE);
Utils.logError(this.logger, e);
}
mon.stop();
return builder.build();
}
@GET
public Response getComments(@QueryParam("parentid") final String parentid) {
Response response = null;
final Monitor mon = MonitorFactory.start("CommentService#getComments");
final ResponseBuilder builder = Response.ok();
final Collection<BlogComment> bc = CommentManager.INSTANCE.loadComments(parentid);
if ((bc == null) || bc.isEmpty()) {
builder.status(Status.NO_CONTENT);
} else {
response = this.renderOutput(bc, "comment.mustache");
}
mon.stop();
return (response == null) ? builder.build() : response;
}
private void captureSubmissionDetails(HttpServletRequest request, BlogComment comment) {
final Monitor mon = MonitorFactory.start("CommentService#captureSubmissionDetails");
try {
@SuppressWarnings("rawtypes")
Enumeration hn = request.getHeaderNames();
if (hn != null) {
while (hn.hasMoreElements()) {
String key = hn.nextElement().toString();
comment.addParameter(key, request.getHeader(key));
}
}
@SuppressWarnings("rawtypes")
Enumeration pn = request.getParameterNames();
if (pn != null) {
while (pn.hasMoreElements()) {
String key = pn.nextElement().toString();
String[] values = request.getParameterValues(key);
comment.addParameters(key, values);
if (key.equals("referer")) {
comment.setReferer(values[0]);
} else if (key.equals("user-agent")) {
comment.setUserAgent(values[0]);
}
}
}
@SuppressWarnings("rawtypes")
Enumeration an = request.getAttributeNames();
if (an != null) {
while (an.hasMoreElements()) {
try {
String key = an.nextElement().toString();
comment.addAttribute(key, String.valueOf(request.getAttribute(key)));
} catch (Exception e) {
// No action here
}
}
}
comment.addParameter("REMOTE_HOST", request.getRemoteHost());
comment.addParameter("REMOTE_ADDR", request.getRemoteAddr());
comment.addParameter("REMOTE_USER", request.getRemoteUser());
// Needed for Captcha
comment.setRemoteAddress(request.getRemoteAddr());
} catch (Exception e) {
Utils.logError(this.logger, e);
// But no further action here!
}
mon.stop();
}
}
Read more
Posted by Stephan H Wissel on 04 May 2017 | Comments (2) | categories: Blog