0

我的学习项目有问题。我有一个带有一个 servlet 的简单项目,我需要一些具有不同范围的 CDI bean。这部分非常简单,但我必须能够将 HttpSession 注入到我的每个 CDI bean 中。为了解决这个问题,我创建了 ServletRequestListener 来获取 HttpServletRequest 对象,我将此对象存储在 ThreadLocal 对象中的应用程序范围的 bean 中,并且在此 bean 中,我有来自存储的 HttpServletRequest 的 HttpSession 对象的生产者方法。之后,我可以在任何 CDI bean 中注入 HttpSession,除了会话范围的 bean。在会话初始化之后,会话被正确注入到该 bean,但是对于同一会话中的第二个请求,我有空指针异常,因为会话 bean 在 RequestInitialized 方法之前创建(或反序列化)并且我的生产者返回空值,

这是一个会话中第二个请求的堆栈跟踪:

    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)

和来源:

听众

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);
    }
}

持有者

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);
    }
}

制片人

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();
    }
}

会话 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() + "'" +
                '}';
    }
}

和小服务程序:

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"));
        }
    }
}

为此,我使用来自 github 的 seam/servlet 源代码作为示例。

我已将我当前的代码上传到刚刚构建的Dropbox,在 JBoss 上部署为 7.1.1.Final,转到 localhost:8080/lab2,按两次 F5,您会看到问题。

4

3 回答 3

1

以下简单的事情将做你想做的事情,并且像魅力一样工作:

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();
    }

}

享受!

于 2013-10-10T11:46:21.937 回答
1

我发现问题出在会话范围 bean 中的 toString 方法中。在这种方法中,我尝试获取会话 ID,但由于某些原因(我认为是日志记录)cdi 在请求初始化之前调用了 toString 方法。当我从 toString 方法中删除对会话对象的访问时,一切正常。

于 2013-10-17T07:30:33.100 回答
0

使用 JSF,确实你可以通过 FacesContext 访问你的 httpsession ......

我们没有您的所有配置,在这里我看不到此配置的目的没有人像那样使用 servlet,您可以通过请求参数而不是注入来访问 http 会话...

于 2016-05-19T07:49:44.080 回答