Seregwethrin
Seregwethrin

Reputation: 1389

Accessing Java Web's HttpServletRequest/Response object in custom class files

I really got tired of this problem and can't find a best practice for doing it. Let me clear the problem with an example:

I have a static method like :

public class AppError {
   public static void systemError(string errorMsg) { //Like "Can't connect to DB"
      HttpServletRequest request = ?????????? "HOW TO ACCESS?";
      request.setAttribute("Error", errorMsg);
      request.getRequestDispatcher("/error.jsp").forward(request, response(????????));
   }
}

As you can see I need to access request and response objects and I DON'T WANT to pass HttpServletRequest/Response by parameters because that means passing it where AppError.systemError called, and that is so many places: reading/writing files, cookie and user operations, database operations etc...

I thought I found a solution, ThreadLocal, I used it in a Filter and saved request and response objects but then Filter.Destroy() method where I free ThreadLocal variables by ThreadLocal.remove() method is not invoked when I tested it in Debug mode. That means the thread may never be dealloced.

The ServletContextListerner is NOT A SOLUTION too because I couldn't find a way to access request and response objects within the ServletContext object. I expected a method like ServletContext.getRequest() but there's none.

I'm also using JSF, but the problem with the JSF is FacesContext is not accessible in custom classess too. Also JSF is being loaded later than listeners, so I can't use my error system in the listeners if I used FacesContext to get request and response objects.

So what is the best practice here? I mean this is a clear problem and I can't move on without solving this.

Btw, the error method is an example, there are other places that I need to access request and response objects.

Upvotes: 2

Views: 2852

Answers (1)

Arjan Tijms
Arjan Tijms

Reputation: 38163

In principal there's no reason why a correctly setup Filter would not work in combination with thread local storage (TLS).

The key insight is not to clear the TLS in the Filter#Destroy() method, but in a finally clause after the main filtering chain:

@WebFilter(filterName="requestTLSFilter")
public class RequestTLSFilter extends Filter {

    private static final ThreadLocal<ServletRequest> THREAD_LOCAL = new ThreadLocal<ServletRequest>();

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        try {
            THREAD_LOCAL.put(request);
            chain.doFilter(request, response);
        } finally {
            THREAD_LOCAL.remove();
        }
    }

    // Other methods    
}

With respect to JSF, FacesContext.getCurrentInstance() is available everywhere. JSF has no implicit knowledge of who's calling that, BUT it's only available to code in the call chain after the Facelets servlet is invoked.

There's one other alternative. If your container has Java Authorization Contract for Containers (JACC) support, then the following gets you the current request as well:

HttpServletRequest request = (HttpServletRequest) PolicyContext.getContext("javax.servlet.http.HttpServletRequest");

Upvotes: 4

Related Questions