访问ServletContextListener中的会话变量

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了访问ServletContextListener中的会话变量相关的知识,希望对你有一定的参考价值。

我正在使用Grails实现上传功能,基本上用户可以上传文本文件,然后系统会将该文本文件的每一行保留为数据库记录。虽然上传工作正常,但较大的文件需要时间来处理,因此他们要求有一个进度条,以便用户可以确定他们的上传是否仍在处理或是否发生了实际错误。

为此,我所做的是创建两个URL:

  • /upload,它是接收上传文本文件的实际URL。
  • /upload/status?uploadToken=根据uploadToken返回某个上传的状态。

我所做的是在处理每一行之后,服务将更新会话级计数器变量:

// import ...

class UploadService {
    Map upload(CommonsMultipartFile record, GrailsParameterMap params) {
        Map response = [success: true]

        try {
            File file = new File(record.getOriginalFilename())
            FileUtils.writeByteArrayToFile(file, record.getBytes())
            HttpSession session = WebUtils.retrieveGrailsWebRequest().session
            List<String> lines = FileUtils.readLines(file, "UTF-8"), errors = []
            String uploadToken = params.uploadToken

            session.status.put(uploadToken,
                [message: "Checking content of the file of errors.",
                    size: lines.size(),
                    done: 0])

            lines.eachWithIndex { l, li ->
                // ... regex checking per line and appending any error to the errors List

                session.status.get(uploadToken).done++
            }

            if(errors.size() == 0) {
                session.status.put(uploadToken,
                    [message: "Persisting record to the database.",
                        size: lines.size(),
                        done: 0])

                lines.eachWithIndex { l, li ->
                    // ... Performs GORM manipulation here

                    session.status.get(uploadToken).done++
                }           
            }
            else {
                response.success = false
            }
        }
        catch(Exception e) {
            response.success = false
        }

        response << [errors: errors]

        return response
    }
}

然后创建一个连接到simple WebSocket implementation URL的/upload/status?uploadToken=。问题是我无法访问POGO上的会话变量。我甚至将POGO改为Grails服务,因为我认为这是问题的原因,但我仍然无法访问会话变量。

// import ...

@ServerEndpoint("/upload/status")
@WebListener
class UploadEndpointService implements ServletContextListener {    
    @OnOpen
    public void onOpen(Session userSession) { /* ... */ }

    @OnClose
    public void onClose(Session userSession, CloseReason closeReason) { /* ... */ }

    @OnError
    public void onError(Throwable t) { /* ... */ }

    @OnMessage
    public void onMessage(String token, Session userSession) {
        // Both of these cause IllegalStateException
        def session = WebUtils.retrieveGrailsWebRequest().session
        def session = RequestContextHolder.currentRequestAttributes().getSession()

        // This returns the session id but I don't know what to do with that information.
        String sessionId = userSession.getHttpSessionId() 

        // Sends the upload status through this line    
        sendMessage((session.get(token) as JSON).toString(), userSession)
    }

    private void sendMessage(String message, Session userSession = null) {
        Iterator<Session> iterator = users.iterator()
        while(iterator.hasNext()) {
            iterator.next().basicRemote.sendText(message)
        }
    }
}

相反,给我一个错误:

由IllegalStateException引起:未找到线程绑定请求:您是指在实际Web请求之外的请求属性,还是在最初接收线程之外处理请求?如果您实际在Web请求中操作并仍然收到此消息,则您的代码可能在DispatcherServlet / DispatcherPortlet之外运行:在这种情况下,请使用RequestContextListener或RequestContextFilter来公开当前请求。

我已经通过发送静态String内容验证了Web套接字正在工作。但我想要的是能够获得该计数器并将其设置为发送消息。我正在使用Grails 2.4.4和Grails Spring Websocket插件,虽然看起来很有前途,但只能从Grails 3开始使用。有没有办法实现这个目标,或者如果没有,我应该采用什么方法?

答案

非常感谢the answer to this question帮助我解决了我的问题。

我刚刚修改了我的UploadEndpointService和那个答案上的POGO,而不是把它作为服务类,我把它还原成一个@Serverendpoint。我还配置了它的configurator注释并添加了onOpen()值。我还在import grails.converters.JSON import grails.util.Environment import javax.servlet.annotation.WebListener import javax.servlet.http.HttpSession import javax.servlet.ServletContext import javax.servlet.ServletContextEvent import javax.servlet.ServletContextListener import javax.websocket.CloseReason import javax.websocket.EndpointConfig import javax.websocket.OnClose import javax.websocket.OnError import javax.websocket.OnMessage import javax.websocket.OnOpen import javax.websocket.server.ServerContainer import javax.websocket.server.ServerEndpoint import javax.websocket.Session import org.apache.log4j.Logger import org.codehaus.groovy.grails.commons.GrailsApplication import org.codehaus.groovy.grails.web.json.JSONObject import org.codehaus.groovy.grails.web.servlet.GrailsApplicationAttributes import org.springframework.context.ApplicationContext @ServerEndpoint(value="/ep/maintenance/attendance-monitoring/upload/status", configurator=GetHttpSessionConfigurator.class) @WebListener class UploadEndpoint implements ServletContextListener { private static final Logger log = Logger.getLogger(UploadEndpoint.class) private Session wsSession private HttpSession httpSession @Override void contextInitialized(ServletContextEvent servletContextEvent) { ServletContext servletContext = servletContextEvent.servletContext ServerContainer serverContainer = servletContext.getAttribute("javax.websocket.server.ServerContainer") try { if (Environment.current == Environment.DEVELOPMENT) { serverContainer.addEndpoint(UploadEndpoint) } ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute(GrailsApplicationAttributes.APPLICATION_CONTEXT) GrailsApplication grailsApplication = ctx.grailsApplication serverContainer.defaultMaxSessionIdleTimeout = grailsApplication.config.servlet.defaultMaxSessionIdleTimeout ?: 0 } catch (IOException e) { log.error(e.message, e) } } @Override void contextDestroyed(ServletContextEvent servletContextEvent) { } @OnOpen public void onOpen(Session userSession, EndpointConfig config) { this.wsSession = userSession this.httpSession = (HttpSession) config.getUserProperties().get(HttpSession.class.getName()) } @OnMessage public void onMessage(String message, Session userSession) { try { Map params = new JSONObject(message) if(httpSession.status == null) { params = [message: "Initializing file upload.", size: 0, token: 0] sendMessage((params as JSON).toString()) } else { sendMessage((httpSession.status.get(params.token) as JSON).toString()) } } catch(IllegalStateException e) { } } @OnClose public void onClose(Session userSession, CloseReason closeReason) { try { userSession.close() } catch(IllegalStateException e) { } } @OnError public void onError(Throwable t) { log.error(t.message, t) } private void sendMessage(String message, Session userSession=null) { wsSession.basicRemote.sendText(message) } } 方法中添加了第二个参数。这是编辑过的课程:

onOpen()

真正的魔法发生在qazxswpoi方法中。可以访问会话变量。

以上是关于访问ServletContextListener中的会话变量的主要内容,如果未能解决你的问题,请参考以下文章

一个简单的ServletContextListener示例

ServletContextListener 监听器

ServletContextListener

ServletContextListener接口用法

如何从 ServletContextListener 中查询 servlet 容器以获取有效 URL?

ServletContextListener 和静态块