使用Java WebSockets,JSR 356和JSON映射到POJO的

因此,我一直在研究Tyrus (JSR 356 WebSocket for Java规范的参考实现)。 因为我一直在寻找测试工具,所以我对在Java中同时运行客户端和服务器端感兴趣。 因此,恐怕此博客文章中没有HTML5。

在此示例中,我们想来回发送JSON,因为我像这样老式,所以我希望能够绑定到POJO对象。 我将为此使用Jackson,因此我的Maven文件如下所示:

<dependencies>
    <dependency>
        <groupId>javax.websocket</groupId>
        <artifactId>javax.websocket-api</artifactId>
        <version>1.0-rc3</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.tyrus</groupId>
        <artifactId>tyrus-client</artifactId>
        <version>1.0-rc3</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.tyrus</groupId>
        <artifactId>tyrus-server</artifactId>
        <version>1.0-rc3</version>
    </dependency>

    <dependency>
        <groupId>org.glassfish.tyrus</groupId>
        <artifactId>tyrus-container-grizzly</artifactId>
        <version>1.0-rc3</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.2.0</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.2.0</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.2.0</version>
    </dependency>

  </dependencies>

因此,我们需要做的第一件事就是定义Encode / Decoder接口的实现来为我们完成这项工作。 这将对bean类是什么进行一些简单的反映。 像使用JAX-WS一样,将它们放在同一个类中会更容易。 请注意,我们使用接口的流版本,并且仅处理文本内容。 (暂时忽略发送二进制数据的能力)

package websocket;

import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.IOException;
import java.io.Reader;
import java.io.Writer;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

public abstract class JSONCoder<T>
  implements Encoder.TextStream<T>, Decoder.TextStream<T>{

    private Class<T> _type;

    // When configured my read in that ObjectMapper is not thread safe
    //
    private ThreadLocal<ObjectMapper> _mapper = new ThreadLocal<ObjectMapper>() {

        @Override
        protected ObjectMapper initialValue() {
            return new ObjectMapper();
        }
    };

    @Override
    public void init(EndpointConfig endpointConfig) {

        ParameterizedType $thisClass = (ParameterizedType) this.getClass().getGenericSuperclass();
        Type $T = $thisClass.getActualTypeArguments()[0];
        if ($T instanceof Class) {
            _type = (Class<T>)$T;
        }
        else if ($T instanceof ParameterizedType) {
            _type = (Class<T>)((ParameterizedType)$T).getRawType();
        }
    }

    @Override
    public void encode(T object, Writer writer) throws EncodeException, IOException {
        _mapper.get().writeValue(writer, object);
    }

    @Override
    public T decode(Reader reader) throws DecodeException, IOException {
        return _mapper.get().readValue(reader, _type);
    }

    @Override
    public void destroy() {

    }

}

Bean类非常简单,带有Coder的静态子类,我们以后可以使用它。

package websocket;

public class EchoBean {

    public static class EchoBeanCode extends
       JSONCoder<EchoBean> {

    }

    private String _message;
    private String _reply;

    public EchoBean() {

    }

    public EchoBean(String _message) {
        super();
        this._message = _message;
    }

    public void setMessage(String _message) {
        this._message = _message;
    }

    public String getMessage() {
        return _message;
    }

    public void setReply(String _reply) {
        this._reply = _reply;
    }

    public String getReply() {
        return _reply;
    }

}

因此,我们需要实现服务器端点,因此可以采用两种方式之一,即注释POJO或扩展端点。 我要为服务器使用第一个,为客户端使用第二个。 实际上,此服务所做的全部工作就是将消息发布回客户端。 注意编码和解码器的注册。 在这种情况下是相同的类。

package websocket;

import java.io.IOException;

import javax.websocket.EncodeException;
import javax.websocket.EndpointConfig;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import static java.lang.System.out;

@ServerEndpoint(value="/echo",
                encoders = {EchoBean.EchoBeanCode.class},
                decoders = {EchoBean.EchoBeanCode.class})
public class EchoBeanService
{

    @OnMessage
    public void echo (EchoBean bean, Session peer) throws IOException, EncodeException {
        //
        bean.setReply("Server says " + bean.getMessage());
        out.println("Sending message to client");
        peer.getBasicRemote().sendObject(bean);
    }

    @OnOpen
    public void onOpen(final Session session, EndpointConfig endpointConfig) {
        out.println("Server connected "  + session + " " + endpointConfig);
    }
}

让我们看一下客户端bean,这次扩展了标准Endpoint类并为消息添加了特定的侦听器。 在这种情况下,当收到消息时,只需关闭连接即可简化我们的测试案例。 在现实世界中,管理这种连接显然会更加复杂。

package websocket;

import java.io.IOException;

import javax.websocket.ClientEndpoint;
import javax.websocket.CloseReason;
import javax.websocket.EncodeException;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;

import static java.lang.System.out;

@ClientEndpoint(encoders = {EchoBean.EchoBeanCode.class},
                decoders = {EchoBean.EchoBeanCode.class})
public class EchoBeanClient 
  extends Endpoint
{
    public void onOpen(final Session session, EndpointConfig endpointConfig) {

        out.println("Client Connection open "  + session + " " + endpointConfig);

        // Add a listener to capture the returning event
        //

        session.addMessageHandler(new MessageHandler.Whole() {

            @Override
            public void onMessage(EchoBean bean) {
                out.println("Message from server : " + bean.getReply());

                out.println("Closing connection");
                try {
                    session.close(new CloseReason(CloseReason.CloseCodes.NORMAL_CLOSURE, "All fine"));
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });

        // Once we are connected we can now safely send out initial message to the server
        //

        out.println("Sending message to server");
        try {
            EchoBean bean = new EchoBean("Hello");
            session.getBasicRemote().sendObject(bean);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (EncodeException e) {
            e.printStackTrace();
        }

    }
}

现在,使用Tyrus来独立运行WebSocket确实非常简单,您只需实例化服务器并启动它。 请注意,这会启动守护程序线程,因此您需要确保它是否在main方法中,并且您需要执行一些操作来保持JVM的生命。

import org.glassfish.tyrus.server.Server;

Server server = new Server("localhost", 8025, "/", EchoBeanService.class);
server.start();

因此客户相对简单; 但是在执行声明式方法时,我们需要在注册客户端类时显式注册编码器和解码器。

import javax.websocket.ClientEndpointConfig;
import javax.websocket.Decoder;
import javax.websocket.Encoder;
import javax.websocket.Session;

import org.glassfish.tyrus.client.ClientManager;

// Right now we have to create a client, which will send a message then close
// when it has received a reply
//

ClientManager client = ClientManager.createClient();
EchoBeanClient beanClient = new EchoBeanClient();

Session session = client.connectToServer(
        beanClient, 
        ClientEndpointConfig.Builder.create()
         .encoders(Arrays.<Class<? extends Encoder>>asList(EchoBean.EchoBeanCode.class))
         .decoders(Arrays.<Class<? extends Decoder>>asList(EchoBean.EchoBeanCode.class))
         .build(),
        URI.create("ws://localhost:8025/echo"));

// Wait until things are closed down

while (session.isOpen()) {
    out.println("Waiting");
    TimeUnit.MILLISECONDS.sleep(10);
}

现在,其输出如下所示:

Server connected SessionImpl{uri=/echo, id='e7739cc8-1ce5-4c26-ad5f-88a24c688799', endpoint=EndpointWrapper{endpointClass=null, endpoint=org.glassfish.tyrus.core.AnnotatedEndpoint@1ce5bc9, uri='/echo', contextPath='/'}} javax.websocket.server.DefaultServerEndpointConfig@ec120d
Waiting
Client Connection open SessionImpl{uri=ws://localhost:8025/echo, id='7428be2b-6f8a-4c40-a0c4-b1c8b22e1338', endpoint=EndpointWrapper{endpointClass=null, endpoint=websocket.EchoBeanClient@404c85, uri='ws://localhost:8025/echo', contextPath='ws://localhost:8025/echo'}} javax.websocket.DefaultClientEndpointConfig@15fdf14
Sending message to server
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Waiting
Sending message to client
Message from server : Server says Hello
Closing connection
Waiting

有趣的是,这是第一次运行,会有一个暂停,我怀疑这是由于杰克逊进行了设置,但我没有时间进行分析。 我确实发现,这种漫长的延迟发生在第一篇文章中-尽管显然,这比一般地传递​​纯文本消息要慢。 差异是否对您很重要取决于您的应用程序。

将纯文本的性能与JSON流API(例如由新JSR提供的JSON流API)以及将这些值绑定到JSON POJO的版本进行比较会很有趣。 也许是另一天的事情。


翻译自: https://www.javacodegeeks.com/2013/05/using-java-websockets-jsr-356-and-json-mapped-to-pojos.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值