Reputation: 908
I have problem with my project for studies. I have simple project with one servlet and I need to have some CDI beans with diferent scopes. This part was realy simple, but I have to be able to inject HttpSession into each of me CDI beans. To resolve this problem I made ServletRequestListener to get HttpServletRequest object, I store this object in application scoped bean in ThreadLocal object and in this bean I have producer method for HttpSession object from stored HttpServletRequest. After that I'm able to inject HttpSession in any CDI bean except session scoped beans. Session is properly injected to that bean after session initialization, but for second request in the same session I have null pointer exception, because session bean is created(or deserialized) before RequestInitialized method and my producer return null value, which is illegal accoprding to stacktrace.
Here is the stacktrace from second request in one session:
org.jboss.weld.exceptions.IllegalProductException: WELD-000052 Cannot return null from a non-dependent producer method: [method] @Produces @RequestScoped public pl.lab2.cdi.producers.SessionObjectsProducer.getSession()
org.jboss.weld.bean.AbstractProducerBean.checkReturnValue(AbstractProducerBean.java:217)
org.jboss.weld.bean.AbstractProducerBean.create(AbstractProducerBean.java:300)
org.jboss.weld.context.AbstractContext.get(AbstractContext.java:107)
org.jboss.weld.bean.proxy.ContextBeanInstance.getInstance(ContextBeanInstance.java:90)
org.jboss.weld.bean.proxy.ProxyMethodHandler.invoke(ProxyMethodHandler.java:104)
org.jboss.weld.proxies.HttpSession$776413422$Proxy$_$$_WeldClientProxy.getId(HttpSession$776413422$Proxy$_$$_WeldClientProxy.java)
pl.lab2.bean.SessionBean.toString(SessionBean.java:31)
java.lang.String.valueOf(String.java:2854)
java.lang.StringBuilder.append(StringBuilder.java:128)
org.jboss.weld.context.SerializableContextualInstanceImpl.toString(SerializableContextualInstanceImpl.java:60)
java.lang.String.valueOf(String.java:2854)
java.lang.StringBuilder.append(StringBuilder.java:128)
org.jboss.weld.context.beanstore.AttributeBeanStore.attach(AttributeBeanStore.java:109)
org.jboss.weld.context.AbstractBoundContext.activate(AbstractBoundContext.java:66)
org.jboss.weld.servlet.WeldListener.requestInitialized(WeldListener.java:141)
org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368)
org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877)
org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671)
org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930)
java.lang.Thread.run(Thread.java:724)
And sources:
Listener
package pl.lab2.servlet;
import org.apache.log4j.Logger;
import pl.lab2.cdi.BeanManagerHelper;
import pl.lab2.servlet.events.literal.*;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
public class ServletListener implements ServletRequestListener {
private static final Logger log = Logger.getLogger(ServletListener.class);
@Override
public void requestDestroyed(ServletRequestEvent sre) {
log.info("request destroyed event");
BeanManagerHelper.getBeanManagerByJNDI().fireEvent((HttpServletRequest) sre.getServletRequest(), DestroyedLiteral.INSTANCE);
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
log.info("request initialized event");
BeanManagerHelper.getBeanManagerByJNDI().fireEvent((HttpServletRequest) sre.getServletRequest(), InitializedLiteral.INSTANCE);
}
}
Holder
package pl.lab2.servlet;
import org.apache.log4j.Logger;
import pl.lab2.servlet.events.Destroyed;
import pl.lab2.servlet.events.Initialized;
import javax.enterprise.context.ApplicationScoped;
import javax.enterprise.event.Observes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
@ApplicationScoped
public class ServletObjectHolder {
private static final Logger log = Logger.getLogger(ServletObjectHolder.class);
private final ThreadLocal<HttpServletRequest> threadRequest = new ThreadLocal<HttpServletRequest>();
public HttpSession getSession() {
log.info("get session");
if (threadRequest.get() != null) {
return threadRequest.get().getSession();
}
return null;
}
public void servletRequestInitialized(@Observes @Initialized final HttpServletRequest request) {
log.info("receive request initialization");
threadRequest.set(request);
}
public void servletRequestDestroyed(@Observes @Destroyed final HttpServletRequest request) {
log.info("receive request destroyed");
threadRequest.set(null);
}
}
Producer
package pl.lab2.cdi.producers;
import org.apache.log4j.Logger;
import pl.lab2.servlet.ServletObjectHolder;
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
import java.io.Serializable;
public class SessionObjectsProducer implements Serializable {
private static final Logger log = Logger.getLogger(SessionObjectsProducer.class);
@Inject
private ServletObjectHolder servletObjectHolder;
@Produces
@RequestScoped
public HttpSession getSession() {
log.info("get session");
return servletObjectHolder.getSession();
}
}
Session bean
package pl.lab2.bean;
import javax.enterprise.context.SessionScoped;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpSession;
import java.io.Serializable;
@SessionScoped
@Named
public class SessionBean implements Serializable {
private String name;
@Inject
private HttpSession session;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "SessionBean{" +
"name='" + name + "', " +
"sessionId='" + session.getId() + "'" +
'}';
}
}
And servlet:
package pl.lab2.servlet;
import org.apache.log4j.Logger;
import org.jboss.weld.context.ConversationContext;
import org.jboss.weld.context.http.Http;
import pl.lab2.bean.ApplicationBean;
import pl.lab2.bean.ConversationBean;
import pl.lab2.bean.RequestBean;
import pl.lab2.bean.SessionBean;
import pl.lab2.cdi.producers.SessionObjectsProducer;
import javax.enterprise.context.Conversation;
import javax.inject.Inject;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
public class ServletDispatcher extends HttpServlet {
private static final Logger log = Logger.getLogger(ServletDispatcher.class);
@Inject
private ApplicationBean applicationBean;
@Inject
private SessionBean sessionBean;
@Inject
private ConversationBean conversationBean;
@Inject
private RequestBean requestBean;
@Inject
private Conversation conversation;
@Inject
@Http
private ConversationContext conversationContext;
@Inject
private SessionObjectsProducer sessionObjectsProducer;
@Override
public void init() throws ServletException {
super.init();
conversationContext.setParameterName("cId");
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
this.request(request, response);
}
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
this.request(request, response);
}
private void request(HttpServletRequest request, HttpServletResponse response) throws IOException {
log.info("request started in session " + request.getSession().getId());
String cid = request.getParameter(conversationContext.getParameterName());
if (cid != null) {
conversationContext.activate(cid);
} else {
conversationContext.activate();
}
takeActions(request);
updateData(request);
printState(response.getWriter(), request);
}
private void printState(PrintWriter writer, HttpServletRequest request) {
writer.print("<div>");
writer.print("<div>Beans:</div>");
writer.print(applicationBean.toString() + "<br />");
writer.print(sessionBean.toString() + "<br />");
writer.print(conversationBean.toString() + "<br />");
writer.print(requestBean.toString() + "<br />");
writer.print("</div>");
writer.print("<div>");
writer.print("<div>Data:</div>");
writer.print("session id: " + request.getSession().getId() + "<br />");
writer.print("conversation id: " + conversation.getId() + "<br />");
writer.print("</div>");
}
private void takeActions(HttpServletRequest request) {
if ("begin".equals(request.getParameter("conversationState"))) conversation.begin();
else if ("end".equals(request.getParameter("conversationState"))) conversation.end();
}
private void updateData(HttpServletRequest request) {
if (request.getParameter("application") != null) {
applicationBean.setName(request.getParameter("application"));
}
if (request.getParameter("session") != null) {
sessionBean.setName(request.getParameter("session"));
}
if (request.getParameter("conversation") != null) {
conversationBean.setName(request.getParameter("conversation"));
}
if (request.getParameter("request") != null) {
requestBean.setName(request.getParameter("request"));
}
}
}
To done it I use seam/servlet sources form github as example.
I've uploaded my current codes to dropbox just build, deploy on JBoss as 7.1.1.Final, go to localhost:8080/lab2, hit F5 twice and you will see the problem.
Upvotes: 0
Views: 6201
Reputation: 1005
Use JSF, indeed you will have acces to your httpsession with FacesContext...
we have not all your configuration and here i don't see the purpose of this configuration no one use servlet like that and you can acess to the http session via the request parameter not with injection...
Upvotes: 0
Reputation: 908
I figured out that the problem was in my toString method in session scoped bean. In this method I tried to get session id, but for some reasons(logging I think) cdi calls toString method before request initialization. When I removed access to session object from toString method, everything works fine.
Upvotes: 1
Reputation: 3443
Something as simple the following will do what you want and work like a charm:
import javax.enterprise.context.RequestScoped;
import javax.enterprise.inject.Produces;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.annotation.WebListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
// NOTE: this produces a request scoped session object because that's what the OP seems to want
@WebListener
public class SessionProducer implements ServletRequestListener {
private static ThreadLocal<HttpSession> SESSIONS = new ThreadLocal<>();
@Override
public void requestDestroyed(ServletRequestEvent sre) {
SESSIONS.remove();
}
@Override
public void requestInitialized(ServletRequestEvent sre) {
SESSIONS.set(HttpServletRequest.class.cast(sre.getServletRequest()).getSession());
}
@Produces @RequestScoped
protected HttpSession getSession() {
return SESSIONS.get();
}
}
Enjoy!
Upvotes: 1