'Spring Web Flow: add view state information to URL
I'm using spring web flow 2.5.1.RELEASE.
I need to show info about the current view state in the URL (I need it for customer journey tracking with google analytics).
Let's say I have a flow (named flow) with two view states (named one and two).
The format of the spring web flow generated URL is
PROTOCOL://DOMAIN/CONTEXT/SERVLET/FLOW?execution=e?s?
ie) https://example.com/project/spring/flow?execution=e1s2
I need the URL to be
https://example.com/project/spring/flow/one?execution=e1s2
or
https://example.com/project/spring/flow?execution=e1s2&viewState=one
when the flow is in view state one and I need it to be
https://example.com/project/spring/flow/two?execution=e1s2
or
https://example.com/project/spring/flow?execution=e1s2&viewState=two
when the flow is in view state two.
Any help will be greatly appreciated.
Regards
Solution 1:[1]
After some research and trial and error I managed to find a solution, it's a little bit complicated but ... hope someone find it useful ...
1 .- Extend DefaultFlowUrlHandler, override the method getFlowId to deal with custom URLs and configure it
@Bean
public FlowHandlerMapping flowHandlerMapping() {
FlowHandlerMapping mapping = new FlowHandlerMapping();
mapping.setFlowUrlHandler(customFlowUrlHandlerForFlowHandlerMapping());
...
2 .- Extend DefaultFlowUrlHandler, override the methods getFlowExecutionKey (to extract executionKey from cookie) and createFlowExecutionUrl (to set request attribute executionKey and return custom URL) and configure it
@Bean
public FlowHandlerAdapter flowHandlerAdapter() {
JsfFlowHandlerAdapter adapter = new JsfFlowHandlerAdapter();
adapter.setFlowExecutor(this.webFlowConfig.flowExecutor());
adapter.setFlowUrlHandler(customFlowUrlHandlerForFlowHandlerAdapter());
return adapter;
}
3 .- Create a filter that sets a CustomResponseWrapper that take the 'executionKey' attribute value from the request and set a cookie with that value.
4 .- As a side note, this technique can be used to get rid of the execution URL parameter.
5 .- Java code
public class FlowUrlhandlerForFlowHandlerMapping extends DefaultFlowUrlHandler /* CustomDefaultFlowUrlHandler */ {
private final Logger logger = LoggerFactory.getLogger(FlowUrlhandlerForFlowHandlerMapping.class) ;
// pathInfo: /landing-planes/oferta
private String PATH_INFO_PREFIX = "/landing-planes/" ;
/**
* Return correct flowId for custom URL
*/
@Override
public String getFlowId(HttpServletRequest request) {
String pathInfo = request.getPathInfo();
logger.info("getFlowId - pathInfo: " + pathInfo) ;
if (pathInfo != null) {
if (pathInfo.startsWith(PATH_INFO_PREFIX)) {
logger.info("getFlowId - Retornando landing-planes como flowId!!!");
return "landing-planes" ;
}
}
String flowId = super.getFlowId(request);
logger.info("getFlowId(" + request + "): " + flowId);
return flowId ;
}
}
public class FlowUrlHandlerForFlowHandlerAdapter extends DefaultFlowUrlHandler /* CustomDefaultFlowUrlHandler */ {
private final Logger logger = LoggerFactory.getLogger(FlowUrlHandlerForFlowHandlerAdapter.class) ;
// pathInfo: /landing-planes/oferta
private String PATH_INFO_PREFIX = "/landing-planes/" ;
private static final String PATH_INFO_FLOW_INIT = "/landing-planes/init";
/**
* Extract and return flowExecutionKey from cookie.
*/
@Override
public String getFlowExecutionKey(HttpServletRequest request) {
String flowExecutionKey = super.getFlowExecutionKey(request);
logger.info("getFlowExecutionKey - super.getFlowExecutionKey: " + flowExecutionKey);
String pathInfo = request.getPathInfo();
logger.info("getFlowExecutionKey - pathInfo: " + pathInfo) ;
if (!pathInfo.startsWith(PATH_INFO_PREFIX)) {
return flowExecutionKey ;
}
if (pathInfo.equals(PATH_INFO_FLOW_INIT)) {
return flowExecutionKey ;
}
Cookie cookieSwf = null;
for (Cookie cookie : request.getCookies()) {
if (cookie.getName().equals("swf")) {
cookieSwf = cookie ;
}
}
if (cookieSwf != null) {
String value = cookieSwf.getValue() ;
logger.warn("El valor de la cookie es " + value) ;
if (value == null) {
logger.warn("El valor de la cookie es null") ;
} else {
String fek = value ;
logger.info("fek: " + fek);
flowExecutionKey = fek ;
}
}
return flowExecutionKey ;
}
/**
* If custom url, set request attribute 'executionKey' and return new custom url (indicated by application logic in 'view' attribute of the request)
*/
@Override
public String createFlowExecutionUrl(String flowId, String flowExecutionKey, HttpServletRequest request) {
String flowExecutionUrl = super.createFlowExecutionUrl(flowId, flowExecutionKey, request);
// /suscripciones-ventas/spring/landing-planes/oferta?execution=e2s2
logger.info("createFlowExecutionUrl(" + flowId + ", " + flowExecutionKey + ", request): " + flowExecutionUrl);
String pathInfo = request.getPathInfo();
logger.info("createFlowExecutionUrl - pathInfo: " + pathInfo) ;
// /suscripciones-ventas/spring/landing-planes/init
if (pathInfo.startsWith(PATH_INFO_PREFIX)) {
String vista = (String) request.getAttribute("view") ;
logger.info("vista: " + vista);
if (vista == null) {
vista = pathInfo.replace(PATH_INFO_PREFIX, "") ;
logger.info("vista tomada del path info: " + vista);
}
return procesarUrl(flowExecutionUrl, request, "/suscripciones-ventas/spring/landing-planes/" + vista) ;
}
return flowExecutionUrl ;
}
private String procesarUrl(String flowExecutionUrl, HttpServletRequest request, String customUrl) {
String executionKey = extractExecutionKey(flowExecutionUrl) ;
logger.info("executionKey: " + executionKey);
if (executionKey != null) {
request.setAttribute("executionKey", executionKey);
}
logger.info("procesarUrl - customUrl: " + customUrl) ;
return customUrl ;
}
private String extractExecutionKey(String string) {
if (string == null) {
logger.warn("El valor pasado como parametro es null") ;
} else {
int index = string.indexOf("?execution=") ;
if (index == -1) {
logger.warn("El valor pasado como parametro no contiene execution '" + string + "'") ;
} else {
String fek = string.substring(index + "?execution=".length()) ;
logger.info("fek: " + fek);
return fek ;
}
}
return null ;
}
}
@Component
public class CustomSpringFilter extends GenericFilterBean {
private static Logger logger = LoggerFactory.getLogger(CustomSpringFilter.class) ;
private static String SWF_CUSTOM_URI_PREFIX = "/suscripciones-ventas/spring/landing-planes/" ;
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest httpServletRequest = ((HttpServletRequest)request) ;
String requestURI = httpServletRequest.getRequestURI() ;
boolean processRequest = processRequest(requestURI) ;
if (!processRequest) {
chain.doFilter(request, response);
return ;
}
logger.info("**************************");
logger.info("requestURI: " + requestURI);
if (requestURI.startsWith(SWF_CUSTOM_URI_PREFIX)) {
HttpServletResponse httpServletResponse = ((HttpServletResponse)response) ;
procesarRequest(chain, httpServletRequest, httpServletResponse);
return;
}
chain.doFilter(request, response);
}
private String[] ignoreExtensionArray = {".js", ".css", ".jpg", ".png"} ;
private boolean processRequest(String requestURI) {
for (int i = 0; i < ignoreExtensionArray.length; i++) {
if (requestURI.endsWith(ignoreExtensionArray[i])) {
return false ;
}
}
if (requestURI.endsWith("pase/onLogin")) {
return false ;
}
return true ;
}
private void procesarRequest(
FilterChain filterchain, HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse
) throws IOException, ServletException {
CustomResponseWrapper responseWrapper = new CustomResponseWrapper(httpServletResponse, httpServletRequest);
filterchain.doFilter(httpServletRequest, responseWrapper);
}
}
public class CustomResponseWrapper extends HttpServletResponseWrapper {
private static Logger logger = LoggerFactory.getLogger(CustomResponseWrapper.class) ;
private HttpServletRequest httpServletRequest ;
private boolean checked = false ;
public CustomResponseWrapper(HttpServletResponse response, HttpServletRequest httpServletRequest) {
super(response);
this.httpServletRequest = httpServletRequest ;
}
@Override
public void sendRedirect(String location) throws IOException {
logger.info("sendRedirect: " + location);
checkAttributeAndSetCookie();
super.sendRedirect(location);
}
@Override
public PrintWriter getWriter() throws IOException {
checkAttributeAndSetCookie();
return super.getWriter();
}
private void checkAttributeAndSetCookie() {
if (checked) {
return ;
}
checked = true ;
String executionKey = (String) httpServletRequest.getAttribute("executionKey");
logger.info("executionKey: " + executionKey);
if (executionKey != null) {
Cookie cookie = new Cookie("swf", executionKey) ;
super.addCookie(cookie);
}
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | user68459 |