1. 程式人生 > >Java EE HTML5 WebSocket 編碼器與解碼器的例子

Java EE HTML5 WebSocket 編碼器與解碼器的例子

至少原文要保留,原味沒有了.也就沒意思了,

In this tutorial we will implement a Java EE ServerEndpoint message Encoder and Decoder in order to convert HTML5 websocket messages into Java objects (and also the reverse operation of converting Java objects into ready to be sent websocket messages).

在本教程,為了將HTML5 WebSocket訊息轉成Java物件(將Java物件轉成準備要傳送的WebSocket訊息的反向操作也一樣),我們將實現一個Java EE伺服器端點訊息編碼與解碼.

1. Introduction

1.介紹

As we have seen in previous examples a ServerEndpoint is the Java EE implementation of the server-side HTML5 websocket component:

正如我們已經在前面看到的例子,一個伺服器端點就是伺服器端的HTML5 WebSocket元件的Java EE實現:

In those previous examples the implemented ServerEndpoint received and sent messages in plain text format. What if we want to bind this textual information to complex Java objects? The Java EE WebSockets API includes a couple of components for this exact purpose: Encoders and Decoders.
在之前的那些例子,實現的伺服器端點使用純文字格式來接收和傳送訊息.要是我們想要繫結複雜的資訊到複雜的Java物件,會怎樣呢?Java EE的WebSockets API包含了一對元件來達到這樣精確的目的:編碼器和解碼器.

Encoders are used to transform a complex Java object sent by a ServerEndpoint in a format that will be understood by the client. Example: XML, JSON, Binary or some other custom format.
以一種客戶端可以理解的格式,編碼器被用來轉換一個複雜的Java物件,這個物件是由伺服器端點發送出去的.例如,XML,JSON,二進位制或一些其他自定義格式。

Decoders implement the reverse task: They transform textual or binary data sent by clients into complex Java objects ready to be passed into a given ServerEndpoint.
解碼器實現相反的任務:它們將由客戶端傳送的文字或二進位制資料轉成複雜的Java物件準備傳遞給一個給定的伺服器端點.

In this tutorial we will implement a simple Encoder - and the respective Decoder - in order to convert messages sent by clients in JSON format into a Java object representation (and vice-versa).
在本教程中,我們會實現一個簡單的編碼器和相應的解碼器,為了將客戶端傳送的訊息以JSON格式轉成一個JAVA物件表示(反過來也一樣).

This tutorial considers the following environment:
本教程考慮了以下環境:

Ubuntu 12.04
JDK 1.7.0.21
Glassfish 4.0

Note: WebSockets support was introduced in Java EE 7

.注:WebSockets的支援在Java EE 7才被引入.

2. WebSocket server endpoint

2.WebSocket伺服器端點


Following next is the ServerEndpoint used in this example:

接下來的就是這個例子使用的伺服器端點:

package com.byteslounge.websockets;

import java.io.IOException;

import javax.websocket.EncodeException;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint(
  value = "/websocket", 
  encoders = { MessageEncoder.class }, 
  decoders = { MessageDecoder.class }
)
public class WebSocketTest {

  @OnMessage
  public void onMessage(Message message, Session session) 
      throws IOException, EncodeException {

    // Echo the received message back to the client
    Message response = new Message();
    response.setSubject("Response to " + message.getSubject());
    response.setContent("echo " + message.getContent());
    session.getBasicRemote().sendObject(response);

  }

  @OnOpen
  public void onOpen() {
    System.out.println("Client connected");
  }

  @OnClose
  public void onClose() {
    System.out.println("Connection closed");
  }

}

We have introduced some changes into this server endpoint when compared to the ones we defined in the previous tutorials.
和我們之前的教程定義過的服務端點相比,這個服務端點,我們已經介紹過了一些變化.

Method onMessage is now receiving a parameter of type Message instead of receiving a String (we will see the Message class in a few moments). This same method now writes an object - also of type Message - directly to the connected client.
onMessage方法現在是接收一個Message型別的引數,而不是接受一個String(我們將過一會就看到Message類。現在這個同樣的方法寫一個物件 - 也是Message型別 - 直接連線的客戶端。

We are now declaring encoders and decoders in the type level @ServerEndpoint annotation. They will be used to convert messages received from the clients in a specific text format into instances of type Message (and vice-versa).

現在我們在類級別上的@ServerEndpoint註解宣告編碼器和解碼器. 它們將用於轉換接收到來自於客戶端的使用一種指定的文字格式訊息,變成為Message型別的例項(反之亦然)

3. The Message class
3.Message類
Now we define the Message class we used in the previously defined ServerEndpoint:
現在我們定義我們先前在伺服器端點使用過的Message類.
package com.byteslounge.websockets;

public class Message {

  private String subject;
  private String content;

  public String getSubject() {
    return subject;
  }

  public void setSubject(String subject) {
    this.subject = subject;
  }

  public String getContent() {
    return content;
  }

  public void setContent(String content) {
    this.content = content;
  }

}

This class is just a simple POJO that represents a message received by a connected client. It contains a subject and the message content itself. It will be also used to represent server responses sent to clients.
這個類只是一個簡單的POJO,它代表一條連線客戶端的接收訊息.它包含了一個主題和它自己本身的內容.它也將用於表示伺服器傳送到客戶端的響應.
4. The Encoder 4.編碼器.
In this example the connected clients will be expecting messages sent by the server to be in JSON format. Since our ServerEndpoint is directly sending instances of type Message we must define an Encoder to convert them into a JSON String:
在這個例子中,連線的客戶端期待由伺服器使用JSON格式傳送的訊息。由於我們ServerEndpoint是直接傳送Message型別的例項,我們必須定義一個編碼器,將它們轉換成JSON字串:
package com.byteslounge.websockets;

import javax.json.Json;
import javax.json.JsonObject;
import javax.websocket.EncodeException;
import javax.websocket.Encoder;
import javax.websocket.EndpointConfig;

public class MessageEncoder implements Encoder.Text<Message> {

  @Override
  public String encode(Message message) throws EncodeException {

    JsonObject jsonObject = Json.createObjectBuilder()
        .add("subject", message.getSubject())
        .add("content", message.getContent()).build();
    return jsonObject.toString();

  }

  @Override
  public void init(EndpointConfig ec) {
    System.out.println("MessageEncoder - init method called");
  }

  @Override
  public void destroy() {
    System.out.println("MessageEncoder - destroy method called");
  }

}


Since we defined this encoder to be used in our ServerEndpoint all messages sent by the endpoint will go through the encoder. The encoder takes the Message instance sent by the endpoint and converts it into JSON format.
既然我們定義這個編碼器在我們的伺服器端點使用,所有由這個端點發送的訊息就會通過這個編碼器.此編碼器拿到了由這個端點發送的Message例項,並將它轉成JSON格式.

The encoder must implement the javax.websocket.Encoder interface. The implemented interface methods are kind of self-explanatory.

編碼器必須實現的javax.websocket.Encoder介面。實現的介面方法都是不言自明的。

5. The Decoder

5.解碼器.


Now we need a decoder, ie. the component that will receive JSON messages from connected clients and convert them into Message instances:

現在我們還需要一個解碼器,這個元件將會接收來自於連線到的客戶端的訊息,並將它們轉成Message例項.

package com.byteslounge.websockets;

import java.io.StringReader;

import javax.json.Json;
import javax.json.JsonObject;
import javax.websocket.DecodeException;
import javax.websocket.Decoder;
import javax.websocket.EndpointConfig;

public class MessageDecoder implements Decoder.Text<Message> {

  @Override
  public Message decode(String jsonMessage) throws DecodeException {

    JsonObject jsonObject = Json
        .createReader(new StringReader(jsonMessage)).readObject();
    Message message = new Message();
    message.setSubject(jsonObject.getString("subject"));
    message.setContent(jsonObject.getString("content"));
    return message;

  }

  @Override
  public boolean willDecode(String jsonMessage) {
    try {
      // Check if incoming message is valid JSON
      Json.createReader(new StringReader(jsonMessage)).readObject();
      return true;
    } catch (Exception e) {
      return false;
    }
  }

  @Override
  public void init(EndpointConfig ec) {
    System.out.println("MessageDecoder -init method called");
  }

  @Override
  public void destroy() {
    System.out.println("MessageDecoder - destroy method called");
  }

}

As we can see the decoder converts a JSON textual message into an instance of type Message. After this step the message will finally be delivered to the ServerEndpoint in the expected format.
正如我們可以看到的解碼器將一個JSON文字訊息轉成一個Message型別例項.緊跟著下一步,這條訊息最終使用期望的格式傳送到伺服器端點.

The decoder must implement the javax.websocket.Decoder interface.
這個解碼器必須實現javax.websocket.Decoder介面

The method willDecode is called previously than decode method and is used to determine if the message should really be decoded. In this example we are just checking if the message sent by the client is valid JSON.
比decode方法要早呼叫的willDecode方法,並且它用來決定這條訊息是否應該真正要被解碼.在這個例子,我們只是檢查由客戶端傳送的訊息是不是有效的JSON.

All other methods are kind of self-explanatory.
所有其它的方法都是不言自明的.


6. Client side

6.客戶端


Now we need to write the client-side of our test application:

現在我們需要編寫我們測試應用的客戶端.

<!DOCTYPE html>
<html>
<head>
<title>Testing websockets</title>
</head>
<body>
  <div>
    <span>Subject:</span>
    <input id="subject" type="text" />
    <br />
    <span>Content:</span>
    <input id="content" type="text" />
  </div>
  <div>
    <input type="submit" value="Send message" onclick="send()" />
  </div>
  <div id="messages"></div>
  <script type="text/javascript">
    var webSocket = 
      new WebSocket('ws://localhost:8080/byteslounge/websocket');

    webSocket.onerror = function(event) {
      onError(event);
    };

    webSocket.onopen = function(event) {
      onOpen(event);
    };

    webSocket.onmessage = function(event) {
      onMessage(event);
    };

    function onMessage(event) {
      var json = JSON.parse(event.data);
      document.getElementById('messages').innerHTML 
        = '<br />Received server response!'
        + '<br />Subject: ' + json.subject
        + '<br />Content: ' + json.content;
    }

    function onOpen(event) {
      alert('Connection established');
    }

    function onError(event) {
      alert('Error');
    }

    function send() {
      var subject = document.getElementById('subject').value;
      var content = document.getElementById('content').value;
      var json = { 
        'subject' : subject,
        'content' : content
      };
      webSocket.send(JSON.stringify(json));
      return false;
    }
  </script>
</body>
</html>

Similarly to what we have done in the previous tutorials - as stated in this article Introduction section - we will use this sample page in order to interact with our ServerEndpoint.
和我們之前完成的教程一樣,在這篇文章中介紹部分的描述,我們將會使用這個示例頁與我們的伺服器端點進行互動.

There are two input fields, one for inserting the message subject and the other for inserting the message content.
有兩個輸入欄位,一個是插入訊息的主題和另一個插入訊息的內容.

When the user presses the "Send message" button the input fields content will be converted into a textual JSON message and sent through the websocket. The ServerEndpoint together with the Encoder and the Decoder will produce a message response and send it back to the client.
當用戶按下"Send Message"按鈕,輸入欄位內容就會轉成文字JSON訊息,並通過websocket傳送.伺服器端點和編碼器,解碼器一起產生一個訊息響應,併發送回到客戶端.

Finally the message received from the server in JSON format will be processed by the client and presented to the user.
最後接收了來自於伺服器使用了JSON格式的訊息會由客戶端處理,並呈現給使用者.

7. Testing

7.測試


Now we deploy the application and access the following URL:
現在我們部署此應用並訪問以下URL:


http://localhost:8080/byteslounge/page.html

The following page will be presented:

將呈現以下頁面


Now we fill the details and press the Send button:

現在,我們填寫的詳細資訊,然後按“傳送”按鈕:


We will receive the response from the server:

我們將收到來自於伺服器的響應:


8. Conclusion

8.概括.


As we have seen Encoders and Decoders are very convenient to convert messages in some format that the client understands into Java objects of arbitrary complexity. The message exchanging format may be whatever format that suits your needs.
正如我們已經看到的編碼器和解碼器都非常方便將一些客戶端能理解的格式的訊息轉成任意複雜的Java物件.訊息交換格式可以是任何適合你需要的格式.

In this concrete example we used JSON but you may use any other format like XML or even a completely custom format. Even pure binary messages may be used but that will be covered in another tutorial.
在這個具體的例子中,我們使用JSON,但是你可以使用任何其它格式.如XML或者甚至是一種完全自定義的格式.即使純二進位制訊息可以使用,但那將會在另一個教程提到.

9. Downloadable sample

9.下載的示例.


The example source code is available for download at the end of this page. The test was executed in Glassfish 4 (you will need a Java EE 7 compliant application server).
這個例子原始碼在本頁的末尾可供下載.這個測試是執行在Glassfish4(你將需要一個JavaEE 7相容應用伺服器).

Download source code from this tutorial