8

Is there any way to retrieve a session from a POJO? Or ultimately to retrieve a bean from a POJO.

To clarify:

Basically I am creating a bean from a servlet and I need to access the properties of that bean from outside of the web container (from a POJO). I cannot pass the request to the pojo; and the request is needed to retrieve the session.

More specifically I have a web application that uses the Cactus framework to run JUnit tests from a web interface. However the servlet that invokes the JUnit test runner is compiled in a jar; I added extra drop down menus to change settings from which the JUnit test will read from to switch between different environments (WLI clusters), so given that the runner servlet is already compiled I cannot modify it to handle the extra parameters from the multiple environments. I have tried the persistence approach of writing to a .dat file fro which the JUnit test will read from by way of a Reader class; also I have have tried the bean approach which ultimately was not accessible from the JUnit test.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
TheWolf
  • 1,675
  • 4
  • 22
  • 34
  • Can you please elaborate a bit on your problem? – Sid Jan 21 '11 at 22:08
  • Describe your problem better. Clearly you have a web application that is manipulating data. It appears that you need to store it and then reference it external of the web application. – DwB Jan 21 '11 at 22:20
  • Okay see above edits, thanks. – TheWolf Jan 21 '11 at 22:29

4 Answers4

14

Only and only if your POJO is running in the same thread as the HttpServletRequest is running in, then you'll be able to achieve this with help of ThreadLocal<T>.

Create the following class:

public final class YourContext implements AutoCloseable {

    private static ThreadLocal<YourContext> instance = new ThreadLocal<>();

    private HttpServletRequest request;
    private HttpServletResponse response;

    private YourContext(HttpServletRequest request, HttpServletResponse response) {
        this.request = request;
        this.response = response;
    }

    public static YourContext create(HttpServletRequest request, HttpServletResponse response) {
        YourContext context = new YourContext(request, response);
        instance.set(context);
        return context;
    }

    public static YourContext getCurrentInstance() {
        return instance.get();
    }

    @Override    
    public void close() {
        instance.remove();
    }

    public HttpServletRequest getRequest() {
        return request;
    }

    public HttpSession getSession() {
        return request.getSession();
    }

    public ServletContext getServletContext() {
        return request.getServletContext();
    }

    // ... (add if necessary more methods here which return/delegate the request/response).    
}

Implement javax.servlet.Filter which does the following in doFilter() method and is mapped on an url-pattern of interest, e.g. /* or on the servlet-name of your front controller servlet.

@Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws ServletException, IOException {
    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;

    try (YourContext context = YourContext.create(request, response)) {
        chain.doFilter(request, response);
    }
}

Note the importance of try-with-resources statement. It guarantees that the YourContext#close() will be called after the filter has done its job and the ThreadLocal resource will be cleared. Otherwise the thread will still contain it when recycled for another HTTP request.

And here's how you could use it in the POJO:

YourContext context = YourContext.getCurrentInstance();
HttpSession session = context.getSession();

This all is basically also how the Context objects of the average MVC framework works, like JSF's FacesContext and the one in Wicket.

Said that, have you looked at CDI? Perhaps it's easier to make the artifacts CDI-managed so you can just @Inject them in each other.

BalusC
  • 1,082,665
  • 372
  • 3,610
  • 3,555
  • Thanks, this sounds great. Which package does the templated ThreadLocal belong to? The JDK native ThreadLocal does not accept templates. – TheWolf Jan 21 '11 at 23:43
  • Ok I found it in JDK 6, I am using WLI 8 so I am limited to JDK 1.4, will the non templated object work fine? – TheWolf Jan 21 '11 at 23:44
  • 1
    The `ThreadLocal` link in my answer points to `java.lang.ThreadLocal` javadoc. I'm not sure what you mean with "templates", but if you're actually referring to the generic type parameter `` (and taking your consistent use of the old fashioned "POJO" term into account) I suspect that you aren't on Java 1.5 yet. In that case, just omit the `` in the code example and apply casts `(Context)` where applicable. As the javadoc hints, the `ThreadLocal` is been available since 1.2. – BalusC Jan 21 '11 at 23:44
  • I am assuming this chain object is of type FilterChain, where will this object come from? – TheWolf Jan 22 '11 at 00:53
  • 1
    That's available as the last argument of `doFilter()` method. See also the javadoc. Are you familiar with filters as well? Get yourself introduced in [our wiki page](http://stackoverflow.com/tags/servlet-filters/info). – BalusC Jan 22 '11 at 00:56
3

Yes, there is.

If you're using a web framework, for example Wicket, there is often a way to get the current HttpSession. From there, you can get the Spring ApplicationContext and if you have that, you can get Spring Beans from it. This works in any location, as we're using static utility methods only.

Example code:

import org.apache.wicket.protocol.http.WebApplication;
import org.springframework.web.context.support.WebApplicationContextUtils;
import org.springframework.web.context.WebApplicationContext;

ServletContext sc = WebApplication.getServletContext();
WebApplicationContext wac = WebApplicationContextUtils.getWebApplicationContext(sc);

Object bean = wac.getBean("myBeanId");

Note however that the Spring Filter and the Wicket Filter must be in place and handle the current request, otherwise the utility methods won't work.

If you didn't mean Spring Beans, than you would have to store them in the HTTP Session yourself. If you do not have a web framework, you may want to do what rfeak is suggesting and implement your own ThreadLocal.

mhaller
  • 14,122
  • 1
  • 42
  • 61
2

A Pojo is a Plain Old Java Object. POJOS have nothing to do with sessions.

The https session is available on the request object.

Check out this

http://download.oracle.com/docs/cd/E17802_01/webservices/webservices/docs/1.6/api/index.html

specifically the HttpServletRequest interface, and the getSession() method.

For the 'bean' part of your question. A bean is a java class that conforms to 3 standards.

  1. No arg constructor
  2. setters and getters to access private fields
  3. Implements serializable.

A POJO is a bean if it follows those conventions.

hvgotcodes
  • 118,147
  • 33
  • 203
  • 236
  • I mean retrieving a web session generated from a web session (ie. from some sort of request) from a POJO outside of the web container. (Session not coming from the POJO of course). – TheWolf Jan 21 '11 at 22:09
  • @Vapen `a POJO outside of the web container` no way, you need to persist data some where or anyhow you need to transfer data – jmj Jan 21 '11 at 22:11
  • I have tried the persistence approach via writing the parameters to a .dat file and I ran into a race condition given concurrent requests accessing the .dat file. – TheWolf Jan 21 '11 at 22:15
  • @vapen, you can set up a simple REST Webservice that returns an xml/json/whatever you need to ge the data you need – hvgotcodes Jan 21 '11 at 22:16
2

Assuming that you are referring to Servlet programming ....

There's no direct way to get from a POJO to the Session. You need to get the Session from the HttpServletRequest object.

There are 2 popular solutions that I've seen for dealing with this.

First option is to create a context object of some sort that contains the Session. This context is then passed along down into your business layer so that your POJOs can get this information if they need it.

Second option is to leverage ThreadLocal storage. Often the session is placed on ThreadLocal storage by a filter or interceptor. Then any object in your system can fetch it from the thread. This pattern shows up in a lot of the web frameworks like Spring and Struts.

rfeak
  • 8,124
  • 29
  • 28
  • I need the context object to be stateful and maintain its state throughout the entire life of the session and also be mutable, is this possible? – TheWolf Jan 21 '11 at 22:44
  • The solutions I offered would only have access during the life of the request. It would be possible to snag a Session and store a reference to it outside of the standard request life cycle, but I really wouldn't recommend that. At that point you become beholden to how your servlet container handles sessions. Not really where you want to be. Could you possibly reformulate your question to show what you want to do at a higher level? Maybe getting a reference to a Session isn't the best solution. – rfeak Jan 21 '11 at 22:50
  • Thanks, I have recently edited my question can you please check the new edits. I can further explain the requirements if needed :) – TheWolf Jan 21 '11 at 22:55
  • The session itself is stateful and you can freely put anything in there you need. Use it. Don't build a second parallel session, you'll end up regretting it. – Stijn de Witt Mar 12 '13 at 21:25