Wednesday, June 12, 2013

JSONP response from Spring MVC

How to get a JSONP response from Spring MVC

The best and easy way to do this is attach a filter to HTTP response.

Here is a working example for this


  • Create a filter class and its supported utility classes


public class JsonpBoundryFilter implements Filter {   
public void init(FilterConfig fConfig) throws ServletException {} 
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpRequest = (HttpServletRequest) request;
HttpServletResponse httpResponse = (HttpServletResponse) response; 
@SuppressWarnings("unchecked")
Map<String, String[]> parms = httpRequest.getParameterMap(); 
if(parms.containsKey("callback")) {
OutputStream out = httpResponse.getOutputStream(); 
GenericResponseWrapper wrapper = new GenericResponseWrapper(httpResponse); 
chain.doFilter(request, wrapper);            
byte[] bytes1 = new String(parms.get("callback")[0] + "(").getBytes();
byte[] bytes2 = wrapper.getData();
byte[] bytes3 = new String(");").getBytes();            
ByteBuffer byteBuffer = ByteBuffer.allocate(bytes1.length + bytes2.length + bytes3.length);
byteBuffer.put(bytes1);
byteBuffer.put(bytes2);
byteBuffer.put(bytes3);            
byte[] jsonpResponse = byteBuffer.array();
wrapper.setContentType("text/javascript;charset=UTF-8");
wrapper.setContentLength(jsonpResponse.length);
out.write(jsonpResponse); 
out.close();
} else {
chain.doFilter(request, response);
}

public void destroy() {}
}

public class GenericResponseWrapper extends HttpServletResponseWrapper { 
private ByteArrayOutputStream output;    
private FilterServletOutputStream filterStream;
private PrintWriter printWriter;    
private int contentLength;
private String contentType;
 
public GenericResponseWrapper(HttpServletResponse response) {
super(response); 
output = new ByteArrayOutputStream();
filterStream = new FilterServletOutputStream(output);
printWriter = new PrintWriter(output, true);
}  
public byte[] getData() {
return output.toByteArray();
}  
public ServletOutputStream getOutputStream() {
return filterStream;
}  
public PrintWriter getWriter() {
return printWriter;
}  
public void setContentLength(int length) {
this.contentLength = length;
super.setContentLength(length);
}  
public int getContentLength() {
return contentLength;
}  
public void setContentType(String type) {
this.contentType = type;
super.setContentType(type);
}  
public String getContentType() {
return contentType;
}
}

public class FilterServletOutputStream extends ServletOutputStream {
private DataOutputStream stream;
public FilterServletOutputStream(OutputStream output) {
stream = new DataOutputStream(output);
}
public void write(int b) throws IOException {
stream.write(b);
}
public void write(byte[] b) throws IOException {
stream.write(b);
}
public void write(byte[] b, int off, int len) throws IOException {
stream.write(b, off, len);
}
}


  • Add the following configurations to your web.xml

<filter>
<filter-name>jsonpCallbackFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>jsonpBroudryFilter</filter-name> // this name should be in your spring config
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value> /WEB-INF/dispatch-servlet.xml</param-value>
</context-param>
 <listener> 
<listener-class>
                  org.springframework.web.context.ContextLoaderListener
   </listener-class>
</listener>


  • Add following to your spring config as


<bean id="jsonpCallbackFilter" class="com.....JsonpBoundryFilter" />

Sound easy.............  :)