For the purpose of identifying each submitted form uniquely, I wrote a custom tag as shown below:
package jsf.addon.tag;
import java.io.IOException;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.JspWriter;
import javax.servlet.jsp.tagext.TagSupport;
import jsf.addon.navigation.RequestController;
public class RequestIdTag extends TagSupport
{
private static final long serialVersionUID = 5L;
private String paramName = "requestId"; //the default value
public String getParamName()
{
return paramName;
}
public void setParamName(String name)
{
this.paramName = name;
}
public int doStartTag() throws JspException
{
JspWriter out = pageContext.getOut();
try
{
RequestController rc = null;
rc = (RequestController) pageContext.getSession().getAttribute("requestController");
if(rc == null)
{
rc = new RequestController();
pageContext.getSession().setAttribute("requestController", rc);
}
rc.setExpectedRequestId(System.currentTimeMillis());
out.write("<input type=\"hidden\" name=\"" + paramName + "\" value=\"" + rc.getExpectedRequestId() + "\" />");
}
catch (IOException e)
{
throw new JspException("RequestIdTag", e);
}
return SKIP_BODY;
}
public int doEndTag()
{
return EVAL_PAGE;
}
}
The following is the <tag> element definition in the TLD file:
<tag>
<name>requestId</name>
<tag-class>jsf.addon.tag.RequestIdTag</tag-class>
<body-content>empty</body-content>
<description>
Tag to identidy the request.
This tag creates a hidden input with current time in mills as the value.
</description>
<attribute>
<name>paramName</name>
<required>false</required>
<rtexprvalue>false</rtexprvalue>
<description>
Name to be assigned to the requestId. If not specified "requestId" is used.
</description>
</attribute>
</tag>
Next, the Filter implementation is as follows:
package jsf.addon.filter;
import java.io.IOException;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jsf.addon.navigation.RequestController;
public class RequestControlFilter implements Filter
{
protected FilterConfig config;
public void init(FilterConfig config) throws ServletException
{
this.config = config;
}
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException
{
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
RequestController rc = (RequestController) request.getSession().getAttribute("requestController");
if(rc == null)
{
rc = new RequestController();
request.getSession().setAttribute("requestController", rc);
}
rc.control(request, response, chain, config);
}
public void destroy()
{
}
}
Include the following in the web.xml file:
<filter>
<filter-name>requestControlFilter</filter-name>
<filter-lass>
jsf.addon.filter.RequestControlFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>requestControlFilter</filter-name>
<servlet-name>Faces Servlet</servlet-name>
</filter-mapping>
The following is the implementation of the RequestController:
package jsf.addon.navigation;
import java.io.IOException;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jsf.addon.response.ResponseWrapper;
public class RequestController
{
private long expectedRequestId;
private long previousRequestId;
private String responseContent;
public long getExpectedRequestId()
{
return expectedRequestId;
}
public void setExpectedRequestId(long expectedRequestId)
{
this.expectedRequestId = expectedRequestId;
}
public long getPreviousRequestId()
{
return previousRequestId;
}
public void setPreviousRequestId(long previousRequestId)
{
this.previousRequestId = previousRequestId;
}
public String getResponseContent()
{
return responseContent;
}
public void setResponseContent(String responseContent)
{
this.responseContent = responseContent;
}
public void control(HttpServletRequest request, HttpServletResponse response,
FilterChain chain, FilterConfig config)
throws IOException, ServletException
{
long currentRequestId = 0;
String requestId = request.getParameter("requestId");
if(requestId != null)
{
currentRequestId = Long.parseLong(requestId);
}
if(currentRequestId <= 0)
{
//this may be the request for the very first page of this application
//hence, just continue
chain.doFilter(request, response);
return;
}
try
{
synchronized(this)
{
if(currentRequestId != previousRequestId)
{
//this is the first and valid request for the current page
setResponseContent(null);
ResponseWrapper responseWrapper = new ResponseWrapper(response);
chain.doFilter(request, responseWrapper);
//by now we must have a valid response
//get the response string from the response wrapper
String responseContent = responseWrapper.getResponseContent();
setResponseContent(responseContent);
//save the current request-id
setPreviousRequestId(currentRequestId);
}
}
}
catch(Exception ex)
{
ex.printStackTrace();
}
finally
{
config.getServletContext().getRequestDispatcher("/loopBack").forward(request, response);
}
}
}
The implementation of the RequestWrapper:
package jsf.addon.response;
import java.io.PrintWriter;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class ResponseWrapper extends HttpServletResponseWrapper
{
private ServletOutputStreamWrapper sosw = new ServletOutputStreamWrapper();
private PrintWriter pw = new PrintWriter(sosw);
public ResponseWrapper(HttpServletResponse response)
{
super(response);
}
public ServletOutputStream getOutputStream() throws java.io.IOException
{
return sosw;
}
public PrintWriter getWriter() throws java.io.IOException
{
return pw;
}
public String getResponseContent()
{
return sosw.toString();
}
}
The implementation of the ServletOutputStreamWrapper:
package jsf.addon.response;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import javax.servlet.ServletOutputStream;
public class ServletOutputStreamWrapper extends ServletOutputStream
{
private ByteArrayOutputStream baos = new ByteArrayOutputStream();
public void write(int character) throws IOException
{
baos.write(character);
}
public String toString()
{
return baos.toString();
}
}
Finally, the implementation of the LoopBackServlet:
package jsf.addon.servlet;
import java.io.IOException;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import jsf.addon.navigation.RequestController;
public class LoopBackServlet extends HttpServlet
{
private static final long serialVersionUID = 6L;
public void init(ServletConfig config) throws ServletException
{
super.init(config);
}
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
super.service(request, response);
}
public void destroy()
{
super.destroy();
}
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
//retrieve the request controller from the session
RequestController rc = (RequestController) request.getSession().getAttribute("requestController");
ServletOutputStream out = response.getOutputStream();
out.print(rc.getResponseContent());
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException
{
doGet(req, resp);
}
}
Include the following elements in the web.xml:
<servlet>
<servlet-name>LoopBackServlet</servlet-name>
<servlet-class>jsf.addon.servlet.LoopBackServlet</servlet-class>
<load-on-startup>3</load-on-startup>
</servlet>
…
<servlet-mapping>
<servlet-name>LoopBackServlet</servlet-name>
<url-pattern>/loopBack</url-pattern>
</servlet-mapping>
In all your JSPs include the tag <ad:requestId/> anywhere inside the <h:form> element.
Let me know if you need any more help.
Partager