14

(I'm not sure exactly how to phrase the title here, and because of that I'm not really sure how to go about searching for the answer either.)

I have a Java servlet engine that handles requests. Say we have a doGet() request:

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //set up user data

    //do whatever the user requested
    SomeClass c = new SomeClass();
    c.doSomething();
}

Now in doSomething(), I want to be able to access which user made the request. Right now I'm doing it by creating a Java object within the method and passing it to wherever I need it:

public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //set up user data
    MyUserObj userObj = new MyUserObj();
    userObj.setId('123');

    //do whatever the user requested
    SomeClass c = new SomeClass(userObj);
    c.doSomething();
}

By doing this, I have access to the instance of MyUserObj, and it can be further passed along in the application as needed.

I know in ASP.NET MVC3 I can acheive this by storing items/attributes for the current thread like this: HttpContext.Current.Items.Add("myId", "123"). HttpContext is then available in other functions without explicitly having to pass around an object.

Is there a way in Java to set some variables per request (or even set the MyUserObject to be accessed later) without passing the object through as a parameter?

nabster
  • 1,561
  • 2
  • 20
  • 32
Two13
  • 197
  • 1
  • 2
  • 9
  • I took the freedom to edit your title. Please verify if it's a good one – Bozho Apr 17 '12 at 18:48
  • I'd just pass it around explicitly, implicit state and dependencies are bad for maintainability. Frameworks like Spring should help remove some of the boilerplate and clutter with doing this explicitly. – millimoose Apr 17 '12 at 18:48
  • If you understood my question then I trust your title edit is better than my original title :) – Two13 Apr 17 '12 at 18:55
  • You're comparing apples with oranges. "Plain" JSP/Servlet is absolutely not comparable to ASP.NET-MVC3. It's more comparable to Classic ASP. A better comparison material would be JSF (or any other Java EE based MVC framework like Spring MVC, Wicket, Stripes, Vaadin, etc). JSF for example has `FacesContext` and all sorts of autowiring like `@ManagedProperty`, `@Inject`, `@PersitenceContext`, etc for exactly this purpose. – BalusC Apr 17 '12 at 19:08
  • I wasn't really comparing anything. I found a useful feature in asp.net mvc3 and wondered if there was something similar in java. I like how much cleaner my mvc3 server is without having to pass around an object everywhere, so that's why I'm trying to see if it's a viable option for my java server. I realize mvc3 is more of a framework than what I'm using on the java side but that doesn't change my question, even if the answer to the question is "no, but some of these java frameworks can do this" – Two13 Apr 17 '12 at 19:15

2 Answers2

23

There isn't in the servlet API, but you can make your own pretty easily. (Some frameworks like spring-mvc, struts provide such functionality)

Just use a public static ThreadLocal to store and retrieve the object. You can even store the HttpServletRequest itself in the threadlocal and use its setAttribute()/getAttribute() methods, or you can store a threadlocal Map, to be agnostic of the servlet API. An important note is that you should clean the threadlocal after the request (with a Filter, for example).

Also note that passing the object as parameter is considered a better practice, because you usually pass it from the web layer to a service layer, which should not be dependent on web-related object, like a HttpContext.

If you decide that it is fine to store them in a thread-local, rather than passing them around:

public class RequestContext {
    private static ThreadLocal<Map<Object, Object>> attributes = new ThreadLocal<>();
    public static void initialize() {
        attributes.set(new HashMap<Map<Object, Object>>());
    }
    public static void cleanup() {
        attributes.set(null);
    }
    public static <T> T getAttribute(Object key) {
        return (T) attributes.get().get(key);
    }
    public static void setAttribute(Object key, Object value) {
        attributes.get().put(key, value);
    }
}

And a necessary filter:

@WebFilter(urlPatterns="/")
public class RequestContextFilter implements Filter {
     public void doFilter(..) {
         RequestContext.initialize();
         try {
             chain.doFilter(request, response);
         } finally {
             RequestContext.cleanup();
         }
     }
}
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
5

You can attach an object to the current request with setAttribute. This API is primarily used for internal routing, but it's safe to use for your own purposes too, as long as you use a proper namespace for your attribute names.

ataylor
  • 64,891
  • 24
  • 161
  • 189
  • wouldn't I then have to pass the ServletRequest object as a parameter? – Two13 Apr 17 '12 at 18:54
  • You would. However, if you're passing around the request to start with, you can avoid passing around other request related objects. – ataylor Apr 17 '12 at 19:02