javaspringhibernateopen-session-in-view

Access HttpServletRequest anywhere


I used to have an Open Session In Conversation Filter based on cookies for a JSF 2 app. Now I want to build the same mechanism but technology-agnostic. Reusing some code, I have written this in a class that extends OncePerRequestFilter:

@Override
protected void doFilterInternal(HttpServletRequest request,
        HttpServletResponse response, FilterChain filterChain)
        throws ServletException, IOException {

    UUID conversationId = lookupConversationOrCreateIfNecessary(request,
            response);

    log.debug("Binding conversation '{}' to request '{}'", conversationId,
            request);
    bindConversation(conversationId, request);

    try {
        filterChain.doFilter(request, response);
    } finally {
        log.debug("Unbinding conversation '{}' from request '{}'",
                conversationId, request);
        unbindConversation(conversationId, request);
    }

}

Now, when I reach bindConversation(conversationId, request) I just add a request attribute which points to the conversationId which is mapped to a Hibernate Session.

Anyways, in JSF I can access the current request by using FacesContext.getCurrentInstance().getExternalContext().getRequest() and implemented a CurrentSessionContext using this. But in plain servlets how can I access the current request programmatically?

Note: I have been reading the OncePerRequestFilter javadocs and I found this:

As of Servlet 3.0, a filter may be invoked as part of a REQUEST or ASYNC dispatches that occur in separate threads. A filter can be configured in web.xml whether it should be involved in async dispatches. However, in some cases servlet containers assume different default configuration. Therefore sub-classes can override the method shouldNotFilterAsyncDispatch() to declare statically if they [sic] shouuld indeed be invoked, once, during both types of dispatches in order to provide thread initialization, logging, security, and so on. This mechanism complements and does not replace the need to configure a filter in web.xml with dispatcher types.

So, would it be dangerous to use a ThreadLocal to achieve what I want?


Solution

  • As you mention in your question: using a ThreadLocal seems a good option. I don't see why it would be unsafe as soon as you use your filter for both REQUEST and ASYNC.

    EDIT

    @Override
    protected void doFilterInternal(HttpServletRequest request,
                                    HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
    
    
        UUID conversationId = lookupConversationOrCreateIfNecessary(request,
                response);
    
        log.debug("Binding conversation '{}' to request '{}'", conversationId,
                request);
    
        ConversationHolder.setId(conversationId);
    
        bindConversation(conversationId, request);
    
        try {
            filterChain.doFilter(request, response);
        } finally {
            log.debug("Unbinding conversation '{}' from request '{}'",
                    conversationId, request);
            ConversationHolder.clear();
            unbindConversation(conversationId, request);
        }
    
    }
    
    @Override
    protected boolean shouldNotFilter(HttpServletRequest request) throws ServletException {
        return false; //to be sure both REQUEST and ASYNC are filtered
    }
    

    And the ConversationHolder

    public class ConversationHolder extends ThreadLocal<UUID>{
    
        private static ConversationHolder INSTANCE = new ConversationHolder();
    
        public static void setId(UUID conversationId){
              INSTANCE.set(conversationId);
        }
    
        public static UUID getId(){
            return INSTANCE.get();
        }
    
        public static void clear(){
            INSTANCE.remove();
        }
    
    }
    

    Since conversationId is a local variable it won't be shared between request.

    Since ConversationHolder is a ThreadLocal, the value you get from it during doFilter(...) will be correct. (except if you create new Thread by hand during your request processing, but it is not a recommended design)