A lot people are into
GWT when it comes to AJAX. No offense, but I found it disturbing to create a servlet for each AJAX action. So for my projects, I wrote one universal servlet for all my AJAX uses.
I know there are all kinds of AJAX resources out there. But I started out with
prototype and
JSON's
Json-lib. So, my servlet is also based on these two.
Let's first take a look at the Servlet:
SpringBasedAjaxServlet.java
package com.mycompany.ajax.servlet;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.web.servlet.FrameworkServlet;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
/**
* @author KWang
*
*/
public class SpringBasedAjaxServlet extends FrameworkServlet {
private static final long serialVersionUID = 1L;
protected static Logger log = Logger.getLogger(SpringBasedAjaxServlet.class);
/* (non-Javadoc)
* @see org.springframework.web.servlet.FrameworkServlet#doService(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
String json = null;
try {
json = getJSONContent(request, response);
} catch (Exception ex) {
// Send back a 500 error code.
log.debug(ex);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Can not create response");
return;
}
response.setContentType("text/json; charset=UTF-8");
response.setHeader("Cache-Control", "no-cache");
// this will be the second parameter in onXxxxxx function, already
// parsed as object
// unfortunately this header won't work with i18n
// if(StringUtils.isNotBlank(json))
// response.setHeader("X-JSON", json);
PrintWriter pw = response.getWriter();
// has to set json here if it's not null
// add security comment delimiters defined by prototype 1.5.1
pw.write("/*-secure-\n" + json + "\n*/");
pw.close();
}
/**
* Each child class should override this method to generate the specific
* JSON content necessary for each AJAX action.
*
* @param request
* the {@javax.servlet.http.HttpServletRequest} object
* @return a {@java.lang.String} representation of the JSON response/content
*/
private String getJSONContent(HttpServletRequest request, HttpServletResponse response) throws Exception {
String action = request.getParameter("$action");
String m = request.getParameter("$method");
log.debug("calling method name: " + m);
if (StringUtils.isNotBlank(action)) {
Object actionClass = this.getWebApplicationContext().getBean(action);
Method method = actionClass.getClass().getMethod(m, HttpServletRequest.class);
if (method == null)
throw new NoSuchRequestHandlingMethodException(m, getClass());
return (String) method.invoke(actionClass, new Object[] { request });
} else {
//for use with servlets that extend this class
Method method = this.getClass().getMethod(m, HttpServletRequest.class);
if (method == null)
throw new NoSuchRequestHandlingMethodException(m, getClass());
customSetting();
return (String) method.invoke(this, new Object[] { request });
}
}
protected void customSetting(){
//if you extend this class, wire your beans here
}
}
web.xml
...
<servlet>
<servlet-name>ajax</servlet-name>
<servlet-class>com.mycompany.ajax.servlet.SpringBasedAjaxServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
...
<servlet-mapping>
<servlet-name>ajax</servlet-name>
<url-pattern>/AjaxServlet</url-pattern>
</servlet-mapping>
...
This servlet can be used in two ways. First, the servlet will get an action parameter from the request. If the parameter exists, then the servlet will retrieve the action class bean, invoke the specified method, and return the result as a String. Note that the method's name should also be passed in as a request parameter. And, the method should take in an HttpServletRequest (or any kind of request wrapper as you need) as argument. This should be used when there are a lot AJAX actions that cannot be hold in one class. One good reason for me to use the servlet this way is that I can use one action for both AJAX and non-AJAX calls. All I need to do is pass in a request parameter indicating whether it's an AJAX call or not and return different form of object, or return nothing at all, accordingly.
The second way is extending this SpringBasedAjaxServlet and write all your AJAX calls inside the servlet. The customSetting method needs to be overwritten if you need to wire additional spring beans. It's a great way to use this servlet if you only have a few AJAX calls which are used widely across the application.
That's a wrap for my 2 cents.