Does CDI Injection Of SessionScoped Beans Into A Servlet Work?
CDI managed beans are injectable intro Servlets. But: are SessionScoped instances correctly associated with requests belonging to a given session?
The following servlet injects a @SessionScoped SessionStore
managed bean and stores the recent URI as payload. The content of the HTTP header, as well as, the number of SessionStore instances are printed as well:
@WebServlet(name = "FrontController", urlPatterns = {"/FrontController/*"})
public class FrontController extends HttpServlet {
@Inject
SessionStore store;
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String payload = store.getPayload();
final PrintWriter out = response.getWriter();
Enumeration headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String name = headerNames.nextElement();
String value = request.getHeader(name);
out.println(name + " : " + value);
}
String uri = request.getRequestURI();
out.println("Payload: " + payload);
out.println("# of sessions : " + SessionStore.INSTANCE_COUNT.get());
store.setPayload(uri);
}
}
The SessionStore
managed bean increments on each new session the instance counter. Accessing the servlet in a new browser should increment the counter:
@SessionScoped
public class SessionStore implements Serializable{
public static AtomicLong INSTANCE_COUNT = new AtomicLong(0);
private String payload;
@PostConstruct
public void onNewSession(){
INSTANCE_COUNT.incrementAndGet();
}
public String getPayload() {
return payload;
}
public void setPayload(String payload) {
this.payload = payload;
}
@PreDestroy
public void onSessionDestruction(){
INSTANCE_COUNT.decrementAndGet();
}
}
Each new browser window (not a tab) creates a new session, what causes the creation of a new SessionStore
instance. DI of @SessionScoped
CDI beans into plain servlets works as expected. Opening three browsers generates the following output:
host : localhost:8080
user-agent : Mozilla/5.0 (Macintosh; Intel Mac OS X 10.8; rv:13.0) Gecko/20100101 Firefox/13.0.1
accept : text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
accept-language : en-us,en;q=0.5
accept-encoding : gzip, deflate
connection : keep-alive
cookie : JSESSIONID=0d84f4c63e6a10054c92dbdf4584
cache-control : max-age=0
Payload: /servlets-and-cdi/FrontController
# of sessions : 3
The Maven 3 project servlets-and-cdi was pushed into the repo: http://kenai.com/projects/javaee-patterns/sources/hg/show/servlets-and-cdi?rev=428
Small remark: the Enumeration headerNames needs to be an Enumeration<String> to get the code to compile (it is correct at the Kenai repo BTW)
Posted by Christiaan Ypma on August 11, 2012 at 07:58 PM CEST #
Can you please explain how the servlet instances are managed? Because apparently your example shouldn't work. I mean we have a singleton (FrontController) in which we inject a narrower scope instance (SesstionStore). Shouldn't we end up with a instance of FrontController configured with a SessionStore object that is particular to some user? The SessionStore available for that user will be injected into a servlet accessed by everybody else. Can you please explain a little bit how the container avoid this situation? Thank you.
Posted by Andrei Rugina on August 15, 2012 at 09:07 PM CEST #
Hi,
This is a very cool feature, quite amazing. I am using spring injection now, but this sounds way too good. i'm just wondering how cdi works: i mean injection is done on each http request ? Thank you
Posted by violeta marinescu on August 17, 2012 at 11:57 PM CEST #
Really interesting test as I noticed in a lot of pages people testing instances using System.identityHashCode. I have a question for you, in your example it would make a difference changing: @Inject
SessionStore store; to @Inject
Instance<SessionStore> store? and get the store instance using store.get()?
Thanks.
Posted by NauticalMike on February 17, 2013 at 12:32 AM CET #
Thanks, nice post
Posted by Binh Nguyen on January 28, 2016 at 12:35 PM CET #