Thursday, March 17, 2011

Removing context path for seam based applications

As stated earlier, a java web application behind any proxy is not aware that its context path is not required and tries to propagate it for redirects and links.

The solution I provided before by simply ignoring the context path when proxying to the application works, but only fights the symptoms. After spending some time with google and the seam and jsf documentation I found a way to get to the cause of the problem. It still does not cover all cases but works very well for seam based applications. For example, <s:link> now omits the context path while <h:outputLink> still preserves it. So the first workaround is still necessary but it doesn't hurt anyways.

The main idea is based on this post - use a Filter and a HttpServletResponseWrapper to modify the URL passed to all servlet responses by simply removing the context path. This magically applies also to all urls created by seam and leads to a more context-free url for your virtual host.

One thing you have to take care of is to set your cookiepath manually to keep cookies accessible whether your context path is active or not. For glassfish, you can do this in sun-web.xml:


    ...
    
        
            
        
    
    ...


Now without further comment the two required source files and the changes required for web.xml:

package eu.rbecker.util.servlet;

import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

/**
 *
 * @author Roben
 */
public class FakeContext extends HttpServletResponseWrapper {

    private String originalContext;

    public FakeContext(HttpServletResponse response, String originalContext) {
        super(response);
        this.originalContext = originalContext;
    }

    @Override
    public String encodeURL(String url) {
        // preserve original URL encoding
        String out = super.encodeURL(url);
        // remove original context path from url
        if (originalContext != null && !originalContext.isEmpty() && out.startsWith(originalContext)) {
            out = out.substring(originalContext.length());
        }
        return out;
    }
}

package eu.rbecker.util.filter;

import eu.rbecker.util.servlet.FakeContext;
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;

/**
 *
 * @author Roben
 */
public class MyResponseFilter implements Filter {

    private boolean omitContextPath = false;

    @Override
    public void init(FilterConfig fc) throws ServletException {
    }

    @Override
    public void doFilter(ServletRequest sr, ServletResponse sr1, FilterChain fc) throws IOException, ServletException {
        if (sr1 instanceof HttpServletResponse) {
            // create new wrapped response with fake context path
            FakeContext fake = new FakeContext((HttpServletResponse) sr1, ((HttpServletRequest) sr).getContextPath());
            // apply filter
            fc.doFilter(sr, fake);
        }
    }

    @Override
    public void destroy() {
    }
}


    myResponseFilter
    eu.rbecker.util.filter.MyResponseFilter


    myResponseFilter
    /*
    REQUEST
    FORWARD
    INCLUDE

1 comment:

  1. I wish I understood more of this! Maybe I will eventually :)

    ReplyDelete