【问题标题】:Environment variables not available in websocket环境变量在 websocket 中不可用
【发布时间】:2017-05-24 08:57:27
【问题描述】:

我想访问在 web.xml 中定义的环境变量,在 javax WebSocket 的实例中,使用 jetty9 运行。

我可以访问任何servlet中的环境变量,或者WebSocket的onOpen方法,但不能访问WebSocket的onMessage方法

web.xml:

<env-entry>
    <env-entry-name>entry</env-entry-name>
    <env-entry-type>java.lang.String</env-entry-type>
    <env-entry-value>env value</env-entry-value>
</env-entry>

WebSocket.java:

package com.example;

import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import java.io.IOException;

@ServerEndpoint("/ws")
public class WebSocket {

    public String getEntry(Session session) {
        try {
            InitialContext ctx = new InitialContext();
            return (String) ctx.lookup("java:comp/env/entry");
        } catch (NamingException e) {
            e.printStackTrace();
            return null;
        }
    }

    @OnOpen
    public void onOpen(Session session) throws IOException {
        String entry = getEntry(session);
        System.out.println("onOpen: entry = " + (entry != null ? entry : "Not found"));
    }

    @OnMessage
    public void onMessage(String message, Session session) throws IOException {
        String entry = getEntry(session);
        System.out.println("onMessage: entry = " + (entry != null ? entry : "Not found"));
    }

}

pom.xml:

<dependencies>
    <dependency>
        <groupId>javax.websocket</groupId>
        <artifactId>javax.websocket-api</artifactId>
        <version>1.1</version>
        <scope>provided</scope>
    </dependency>
</dependencies>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-war-plugin</artifactId>
            <version>2.3</version>
            <configuration>
                <webappDirectory>${webAppDirectory}</webappDirectory>
            </configuration>
        </plugin>
    </plugins>
</build>

输出:

onOpen: entry = env value
onMessage: entry = Not found
javax.naming.NameNotFoundException; remaining name 'env/entry'
    at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:538)
    at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:569)
    at org.eclipse.jetty.jndi.NamingContext.lookup(NamingContext.java:584)
    at org.eclipse.jetty.jndi.java.javaRootURLContext.lookup(javaRootURLContext.java:108)
    at javax.naming.InitialContext.lookup(InitialContext.java:417)
    at com.example.WebSocket.getEntry(WebSocket.java:17)
    at com.example.WebSocket.onMessage(WebSocket.java:33)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.eclipse.jetty.websocket.common.events.annotated.CallableMethod.call(CallableMethod.java:70)
    at org.eclipse.jetty.websocket.jsr356.annotations.OnMessageTextCallable.call(OnMessageTextCallable.java:60)
    at org.eclipse.jetty.websocket.jsr356.annotations.JsrEvents.callText(JsrEvents.java:189)
    at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextMessage(JsrAnnotatedEventDriver.java:377)
    at org.eclipse.jetty.websocket.common.message.SimpleTextMessage.messageComplete(SimpleTextMessage.java:69)
    at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.appendMessage(AbstractEventDriver.java:65)
    at org.eclipse.jetty.websocket.jsr356.endpoints.JsrAnnotatedEventDriver.onTextFrame(JsrAnnotatedEventDriver.java:359)
    at org.eclipse.jetty.websocket.common.events.AbstractEventDriver.incomingFrame(AbstractEventDriver.java:161)
    at org.eclipse.jetty.websocket.common.WebSocketSession.incomingFrame(WebSocketSession.java:309)
    at org.eclipse.jetty.websocket.common.extensions.ExtensionStack.incomingFrame(ExtensionStack.java:214)
    at org.eclipse.jetty.websocket.common.Parser.notifyFrame(Parser.java:220)
    at org.eclipse.jetty.websocket.common.Parser.parse(Parser.java:258)
    at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.readParse(AbstractWebSocketConnection.java:632)
    at org.eclipse.jetty.websocket.common.io.AbstractWebSocketConnection.onFillable(AbstractWebSocketConnection.java:480)
    at org.eclipse.jetty.io.AbstractConnection$2.run(AbstractConnection.java:544)
    at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:635)
    at org.eclipse.jetty.util.thread.QueuedThreadPool$3.run(QueuedThreadPool.java:555)
    at java.lang.Thread.run(Thread.java:748)

【问题讨论】:

    标签: java jakarta-ee jetty java-websocket


    【解决方案1】:

    首先,我们需要添加一个自定义的 ServletContextListener :

    public class Config implements ServletContextListener {
        private static final String ATTRIBUTE_NAME = "config";
        private String entry;
    
        @Override
        public void contextInitialized(ServletContextEvent event) {
            ServletContext servletContext = event.getServletContext();
            String entryName = servletContext.getInitParameter("entry");
            try {
                entry = new InitialContext().lookup("java:comp/env/entry");
            } catch (NamingException e) {
                throw new RuntimeException("Config failed: entry not found", e);
            }
            servletContext.setAttribute(ATTRIBUTE_NAME, this);
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent event) {
        }
    
        public String getEntry() {
            return entry;
        }
    
        public static Config getInstance(ServletContext servletContext) {
            return (Config) servletContext.getAttribute(ATTRIBUTE_NAME);
        }
    }
    

    在接受连接时,方法 onOpen 写入一个新上下文,您可以通过将 ServletContext 添加到当前上下文的 EndpointConfig 映射中来简单地传递 ServletContext

    public class ServletSavingContext extends ServerEndpointConfig.Configurator {
    
        @Override
        public void modifyHandshake(ServerEndpointConfig config, HandshakeRequest request, HandshakeResponse response) {
            HttpSession httpSession = (HttpSession) request.getHttpSession();
            config.getUserProperties().put("context", httpSession.getServletContext());
        }
    
    }
    

    现在在你以前的课程中:

    package com.example;
    
    import javax.naming.InitialContext;
    import javax.naming.NamingException;
    import javax.websocket.OnMessage;
    import javax.websocket.OnOpen;
    import javax.websocket.Session;
    import javax.websocket.EndpointConfig;
    import javax.websocket.server.ServerEndpoint;
    import java.io.IOException;
    
    @ServerEndpoint(value = "/ws", configurator = ServletSavingContext.class)
    public class WebSocket {
    
      private EndpointConfig config;
    
      @OnOpen
      public void onOpen(Session session, EndpointConfig config) throws IOException {
        this.config = config;
        String entry = Config.getInstance(config.getUserProperties("context")).getEntry();
        System.out.println("onOpen: entry = " + (entry != null ? entry : "Not found"));
      }
    
      @OnMessage
      public void onMessage(String message, Session session) throws IOException {
        String entry = Config.getInstance(config.getUserProperties("context")).getEntry();
        System.out.println("onMessage: entry = " + (entry != null ? entry : "Not found"));
      }
    }
    

    一些文档:http://docs.oracle.com/javaee/7/api/javax/websocket/EndpointConfig.html#getUserProperties%28%29

    【讨论】:

    • 谢谢,但这不是我问的。我需要在初始上下文中有条目。
    • 编辑可能满足您的需求
    • 对不起,但这仍然不能回答我的问题。我需要能够在代码中的任何位置从InitialContext 对象本身检索变量:new InitialContext().lookup("java:comp/env/entry")
    【解决方案2】:

    注意:以下可能不是“最佳实践”,可能无法保证适用于所有容器,但适用于我在 Tomcat 上,可能对其他人有用?

    您可以将上下文分配给onOpen 中的实例变量,然后在onMessage 中使用相同的实例变量。

    @ServerEndpoint("/ws")
    public class WebSocket {
    
        private Context envContext;
    
        public String getEntry(Session session) {
            try {
                return this.envContext.lookup("entry");
            } catch (NamingException e) {
                e.printStackTrace();
                return null;
            }
        }
    
        @OnOpen
        public void onOpen(Session session) throws IOException {
            try {
                InitialContext ctx = new InitialContext();
                this.envContext = ctx.lookup("java:comp/env");
            } catch (NamingException e) {
                e.printStackTrace();
                return null;
            }
    
            String entry = getEntry(session);
            System.out.println("onOpen: entry = " + (entry != null ? entry : "Not found"));
        }
    
        @OnMessage
        public void onMessage(String message, Session session) throws IOException {
            String entry = getEntry(session);
            System.out.println("onMessage: entry = " + (entry != null ? entry : "Not found"));
        }
    
    }
    

    【讨论】:

    • @BalusC 当 websocket 接收到一个连接时,会创建一个新的 WebSocket 实例,我相信它只在单个线程中使用?所以不会有对 onOpen 或 OnMessage 的并发调用?那么 Context 对象不会被并发访问?
    【解决方案3】:

    这是一种解决方法。我只是用之前存储的变量填充上下文:

    @OnMessage
    public void onMessage(String message, Session session) throws IOException, NamingException {
        // Somewhere after onOpen, populate the context
        InitialContext ctx = new InitialContext();
        ctx.createSubcontext("java:comp/env");
        ctx.bind("java:comp/env/entry", STORED_ENTRY);  // Store the variable as an attribute of the WebSocket
    
        String entry = getEntry(session);
        System.out.println("onMessage: entry = " + (entry != null ? entry : "Not found")); 
    }
    

    【讨论】:

    • JNDI 是 JEE/HTTP/Servlet 规范的一部分。一旦您通过 HTTP 升级(在您的情况下为 WebSocket)从 HTTP 规范转换,与 HTTP(包括 Servlet 规范)相关的所有内容都不再适用。唯一安全的方法是在转换之前获取参考,例如在 ServerEndpointConfig.Configurator.modifyHandshake() 中,并在 WebSocket 端点中使用它。
    猜你喜欢
    • 1970-01-01
    • 2022-10-26
    • 1970-01-01
    • 1970-01-01
    • 2019-11-16
    • 1970-01-01
    • 2017-05-23
    • 2018-12-16
    • 1970-01-01
    相关资源
    最近更新 更多