Tuesday, September 11, 2018

Redirects with parameters

Use Case: If you try to create 301 redirect for e.g. lets say /samples/defaultPage --> /samples/home, and then try to hit the url with parameters like http://www.example.com/samples/defaultPage?param1=param1value&param2=param2value, then the page gets redirect to the target page but the parameters are lost and thus, it is very inconvenient for customers who have generated various 301/302 redirects from old site to new re-design website or new navigation structure as redirects work but they fail to retain parameters. Customers might have shared their urls to various social media platforms or as marketing campaigns along with some extra parameters; in order to serve personal content to the end users and for analytics purposes as well or other. This issue is known in 11g8 WCS version and currently there is no resolution as of now until Patch 19. Issue is already resolved in WCS 12c v2.

Resolution: In order to tackle this issue, one of the simplest way is to write a Java EE filter which can help to retain the parameters by passing the parameters as it is to the target url along with response headers and thus, achieving the goal.

Here, I present a simple Java EE filter which we are already using in our project and works flawlessly. It is exactly similar solution to the implementation provided in WCS 12c v2.

package COM.FutureTense.Servlet;
import com.fatwire.cache.WebReference;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.Properties;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.owasp.esapi.ESAPI;
import org.owasp.esapi.Validator;
import org.owasp.esapi.errors.IntrusionException;
import org.owasp.esapi.errors.ValidationException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class CustomWebReferenceFilter
implements Filter
{
private static final String LOCATION = "Location";
private static final String LOOKUPHOST = "lookuphost";
private static final String LOOKUPPAGE = "lookuppage";
private static final String WebReference = "webreference.xml";
private static final Pattern pattern = Pattern.compile("\\s");
private static BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("webreference.xml"));
private static final Log logger = LogFactory.getLog("oracle.fatwire.sites.webreference");
private Resolver resolver;
private static String contentType;
private static String encoding;
public void init(FilterConfig filterConfig)
throws ServletException
{
this.resolver = ((Resolver)beanFactory.getBean("WebreferenceResolver"));
Properties properties = new Properties();
try
{
properties.load(getClass().getClassLoader().getResourceAsStream("ServletRequest.properties"));
contentType = (properties.get("cs.contenttype") != null ? (String)properties.get("cs.contenttype") : "text/html; charset=utf-8").toLowerCase().trim();
int charsetIndex = contentType.lastIndexOf("charset=");
encoding = charsetIndex > -1 ? contentType.substring(charsetIndex + 8).trim() : "utf-8";
int delimiterIndex = encoding.indexOf(";");
if (delimiterIndex > -1) {
encoding = encoding.substring(0, delimiterIndex).trim();
}
}
catch (FileNotFoundException e)
{
throw new ServletException(e);
}
catch (IOException e)
{
throw new ServletException(e);
}
}
public void destroy() {}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)
throws IOException, ServletException
{
request.setCharacterEncoding(encoding);
response.setContentType(contentType);
if (logger.isTraceEnabled())
{
logger.trace("encoding = " + encoding);
logger.trace("contentType = " + contentType);
}
HttpServletRequest httpRequest = (HttpServletRequest)request;
HttpServletResponse httpResponse = (HttpServletResponse)response;
if (this.resolver == null)
{
if (logger.isDebugEnabled()) {
logger.debug("Resolver is not defined");
}
filterChain.doFilter(request, response);
return;
}
String lookupPage = httpRequest.getParameter("lookuppage");
String lookupHost = httpRequest.getParameter("lookuphost");
if (logger.isDebugEnabled())
{
logger.debug("lookupPage: " + lookupPage);
logger.debug("lookupHost: " + lookupHost);
}
if (StringUtils.isNotEmpty(lookupPage))
{
WebReference webreference = this.resolver.resolve(httpRequest, lookupPage, lookupHost);
if (logger.isDebugEnabled()) {
logger.debug("webreference: " + webreference);
}
if (webreference != null)
{
if (webreference.isRedirect())
{
redirect(httpResponse, webreference, httpRequest);
return;
}
if (webreference.isNotFound())
{
if (logger.isDebugEnabled()) {
logger.debug("Setting 404");
}
httpResponse.setStatus(404);
httpResponse.sendError(404);
return;
}
String resolvedURL = webreference.getResolvedURL();
if (logger.isDebugEnabled()) {
logger.debug("forwardURL: " + resolvedURL);
}
RequestDispatcher requestDispatcher = httpRequest.getRequestDispatcher(resolvedURL);
requestDispatcher.forward(request, response);
return;
}
if (logger.isDebugEnabled()) {
logger.debug("Setting 404");
}
httpResponse.setStatus(404);
httpResponse.sendError(404);
return;
}
filterChain.doFilter(request, response);
}
public void redirect(HttpServletResponse httpResponse, WebReference webreference, HttpServletRequest httpRequest)
{
Map<String, String[]> paramMap = httpRequest.getParameterMap();
String otherParams = "";
boolean firstParam;
if (paramMap.size() > 2)
{
Set<String> keys = paramMap.keySet();
firstParam = true;
for (String key : keys) {
if ((!key.equals("lookuphost")) && (!key.equals("lookuppage"))) {
if (firstParam)
{
otherParams = otherParams + "?" + key + "=" + ((String[])paramMap.get(key))[0];
firstParam = false;
}
else
{
otherParams = otherParams + "&" + key + "=" + ((String[])paramMap.get(key))[0];
}
}
}
}
if (logger.isDebugEnabled()) {
logger.debug("Redirecting to URL: " + webreference.getResolvedURL());
}
if (webreference.isPermanentRedirect())
{
if (logger.isDebugEnabled()) {
logger.debug("Setting 301");
}
httpResponse.setStatus(301);
}
else if (webreference.isTemporaryRedirect())
{
if (logger.isDebugEnabled()) {
logger.debug("Setting 302");
}
httpResponse.setStatus(302);
}
String strippedName = replaceLinearWhiteSpace("Location");
String strippedValue = null;
try
{
strippedValue = replaceLinearWhiteSpace(URLDecoder.decode(webreference.getResolvedURL(), "UTF-8"));
}
catch (UnsupportedEncodingException e)
{
strippedValue = replaceLinearWhiteSpace(webreference.getResolvedURL());
}
//add other params as well
if (!otherParams.equalsIgnoreCase("")) {
strippedValue = strippedValue + otherParams;
}
try
{
String safeName = ESAPI.validator().getValidInput("setHeader", strippedName, "HTTPHeaderName", 20, false);
String safeValue = ESAPI.validator().getValidInput("setHeader", strippedValue, "HTTPHeaderValue", 500, false, false);
httpResponse.setHeader(safeName, safeValue);
}
catch (ValidationException e)
{
logger.error("Error redirecting ", e);
return;
}
catch (IntrusionException e)
{
logger.error("Error redirecting ", e);
return;
}
}
private String replaceLinearWhiteSpace(String input)
{
return pattern.matcher(input).replaceAll(" ");
}
}
Once you have created this class, shutdown WCS and deploy as jar file to WEB-INF/lib folder where assetapi.jar file recides. Add the following entry in web.xml just above "WebReferenceFilter" entry as shown below and save.

<filter>
<filter-name>CustomWebReferenceFilter</filter-name>
<filter-class>COM.FutureTense.Servlet.CustomWebReferenceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>CustomWebReferenceFilter</filter-name>
<url-pattern>/Sites/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
<filter>
<filter-name>WebReferenceFilter</filter-name>
<filter-class>COM.FutureTense.Servlet.WebReferenceFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>WebReferenceFilter</filter-name>
<url-pattern>/Sites/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
</filter-mapping>
Restart WCS and test the redirects with parameters which should work now.

DisclaimerThe code and/or the configurations posted are not official recommendations and should be used at sole's discretion with proper testing before deploying on live WebCenter Sites systems. Cheers!!

A simple code compare functionality

One of the most important aspect of any development cycle is deployment and while deployment, it is very important to note the changes don...