Статьи

Java EE 7 и API WebSocket для Java (JSR 356) с AngularJS на WildFly

В этом блоге описывается API Java для протокола WebSocket (JSR 356) (который является одним из четырех новейших JSR для платформы Java EE 7 ) и предоставляется конкретное приложение, развернутое в WildFly 8 и доступное в Интернете в OpenShift .

  • [FR] Французская версия ( HTML или PDF ), посвященная уникальности с точки зрения API. Javascript sans AngularJS.

Прочитав этот пост, вы сможете понять определение Аруна Гупты о том, что можно делать с технологией WebSocket.

«WebSocket предоставляет вам двунаправленный, полнодуплексный канал связи по одному TCP».
— Арун Гупта (евангелист Java EE, Oracle) — Devoxx UK 2013

Обзор Java EE 7

Выпуск Java Platform Enterprise Edition был выпущен в Версии 7 (Java EE 7) в июне 2013 года . В соответствии с двумя предыдущими версиями (Java EE 5 и Java EE 6) Java EE 7 всегда предлагает упростить работу разработчика. Эта версия украшает предыдущие версии с 3 основными целями:

  • охватывает HTML5 (API WebSocket, API JSON-P, JAX-RS)
  • обеспечить еще большую производительность для разработчика (JMS)
  • удовлетворение потребностей предприятия (Batch API, Concurrency Utilities)
Рисунок 1. 3 цели Java EE 7

Рисунок 1. 3 цели Java EE 7

Платформа Java Enterprise Edition 7 (JSR 342) может быть подытожена следующим образом:

  • 4 новейших спецификации: Java API for WebSocket 1.0 , Java API for JSON Processing 1.0 , Batch Applications 1.0 и Concurrency Utilities for Java EE 1.0
  • 3 спецификации с серьезными обновлениями: JMS 2.0 , JAX-RS 2.0 и EL 3.0
  • и 6 спецификаций с незначительными обновлениями: JPA 2.1 , Servlet 3.1 , EJB 3.2 , CDI 1.1 , JSF 2.2 и Bean Validation 1.1

ДЕМО: AngularJS — Приложение API HTML5 / JSR-356, развернутое в WildFly 8 (OpenShift)

Если вы хотите сразу увидеть, как это выглядит, вы можете получить доступ the online application , код которого будет частично объяснен в этой статье. Это приложение, которое дает вам возможность:

  • смотреть один или несколько теннисных матчей в режиме реального времени (Quarter Final US Open 2013)
    • нажмите на каждый матч, который вы хотите получить доступ в прямом эфире
    • нажмите на кнопку выхода, чтобы отключиться от определенного соответствия
  • сделать ставку на победителя матча
    • каждый раз, когда пользователь делает ставку на один и тот же матч, счетчик увеличивается
    • в этом конце матча вы увидите результат вашей ставки

Вы скажете: «Ничего особенного!» и ты прав 🙂

На первый взгляд, это звучит как то, что уже было замечено во многих современных приложениях, но именно эта технология имеет значение, потому что, как вы увидите ниже, все основано на стандарте нового протокола WebSocket (ws: // ou wss: //), а не «HTTP-взлом».

Технологии, используемые для разработки этого приложения:

  • Интерфейс: HTML5 , CSS , Javascript (WebSocket API) с загрузочным CSS и AngularJS
  • Серверная часть: Java API for WebSocket , EJB , JSON-P , JAX-RS
Рисунок 2. Открытое приложение США - реализация WebSocket (Java API et Javascript API)

Рисунок 2. Открытое приложение США — реализация WebSocket (Java API et Javascript API)

Нет! Эта демонстрация не является приложением чата 🙂 Очевидно, что «демо чата» — это то, что сначала приходит на ум, чтобы проиллюстрировать использование технологии WebSocket. Однако есть много других вариантов использования, таких как совместная работа над текстовым документом в Интернете или онлайн-игры, такие как шахматы, представленные в программной заметке JavaOne 2013 .

Это приложение доступно в облаке благодаря OpenShift , продукту PaaS для облачных вычислений от RedHat. Он развернут на WildFly 8.0.0-Beta1 (стандартно сертифицированный Java EE 7 до конца 2013 года). Чтобы настроить сервер приложений, такой как WildFly, на OpenShit, вам просто нужно прочитать это сообщение в блоге Шекхара Гулати

WebSocket (WS): новый протокол, отличный от HTTP

HTTP является стандартным протоколом для Интернета, он очень эффективен для многих случаев использования, но, тем не менее, имеет некоторые недостатки в случае интерактивных веб-приложений :

  • полудуплекс : на основе шаблона запроса / ответа клиент отправляет запрос, а сервер выполняет обработку перед отправкой ответа, клиент вынужден ждать ответа сервера
  • подробный : много информации отправляется в заголовках HTTP, связанных с сообщением, как в запросе HTTP, так и в ответе HTTP
  • для добавления режима push- уведомлений на сервере необходимо использовать обходной путь (опрос, длинный опрос, Comet / Ajax), так как не существует стандартного

Этот протокол не оптимизирован для масштабирования в больших приложениях, которые имеют значительные потребности двунаправленной связи в реальном времени. Вот почему новый протокол WebSocket предлагает более продвинутые функции, чем HTTP, потому что он:

  • основано на 1 unique TCP connection between 2 peers (тогда как для каждого HTTP-запроса / ответа требуется новое TCP-соединение)
  • bidirectionnal : клиент может отправить сообщение на сервер, а сервер также может отправить сообщение клиенту
  • full-duplex : клиент может отправлять несколько сообщений на сервер, а также с сервера на клиент, не дожидаясь ответа друг от друга

Термин « клиент» используется только для определения того, кто инициирует соединение. Как только соединение установлено, клиент и сервер становятся равноправными узлами с одинаковой пропускной способностью.

Протокол WebSocket изначально предназначался для включения в спецификацию HTML5, но, поскольку HTML5 будет официально выпущен в 2014 году, протокол WebSocket, наконец, также будет установлен, как и протокол HTTP, согласно спецификации IETF, с RFC 6455 .

Как показано на диаграмме ниже, протокол WebSocket работает в двух фазах :

  1. handshake (открыть и закрыть)
  2. data transfer
Рисунок 3. Как работает протокол WebSocket

Рисунок 3. Как работает протокол WebSocket

Открывающее рукопожатие

Начальная фаза рукопожатия — это уникальный HTTP-запрос / ответ между тем, кто инициирует соединение (равноправный клиент) и равноправным сервером. Этот HTTP-обмен является специфическим, потому что он использует концепцию обновления, определенную в спецификации HTTP . Принцип прост: обновление HTTP позволяет клиенту просить сервер изменить протокол связи и тем самым гарантировать, что клиент и сервер могут обсуждать использование протокола, отличного от HTTP.

Пример 1. Пример запроса HTTP Handshake

1
2
3
4
5
6
7
GET /usopen/matches/1234 HTTP/1.1    
Host: wildfly-mgreau.rhcloud.com:8000 
Upgrade: websocket 
Connection: Upgrade
Origin: http://wildfly-mgreau.rhcloud.com
Sec-WebSocket-Key:0EK7XmpTZL341oOh7x1cDw==
Sec-WebSocket-Version:13

Строка 1: требуется метод HTTP GET и версия HTTP 1.1
Строка 2: Хост, используемый для соединения WebSocket
Строка 3: Запрос на обновление до протокола WebSocket
Строка 4: Запрос на обновление с HTTP на другой протокол

Пример 2. Пример ответа HTTP Handshake

1
2
3
4
HTTP/1.1 101 Switching Protocols
Connection:Upgrade
Sec-WebSocket-Accept:SuQ5/hh0kStSr6oIzDG6gRfTx2I=
Upgrade:websocket

Строка 1: HTTP-код ответа 101: сервер совместим и принимает отправку сообщений по другому протоколу
Строка 4: обновление до протокола WebSocket принято

Когда запрос на обновление с HTTP до протокола WebSocket одобрен сервером конечной точки, использование HTTP-связи больше невозможно, все обмены должны выполняться через протокол WebSocket.

Передача данных

Как только рукопожатие одобрено, устанавливается использование протокола WebSocket. Существует открытое соединение на стороне равноправного сервера, а также на стороне равноправного клиента , для инициирования связи вызываются обработчики обратного вызова.

Передача данных теперь может начаться, поэтому 2 партнера могут обмениваться сообщениями при двунаправленной и дуплексной связи.

Как показано на рисунке 3 , peer server может отправлять несколько сообщений ( в этом примере: 1 сообщение для каждого набранного очка, 1 сообщение каждый раз, когда любой пользователь делает ставку на эту игру, и 1 сообщение в конце матча ) без каких-либо Ответ peer client и одноранговый клиент также могут отправлять сообщения в любое время ( в этом примере: ставка на победителя матча ). Каждый узел может отправить определенное сообщение, чтобы закрыть соединение.

В платформе Java EE7 код на peer server side пишется на Java, а код на peer client side — на Java или Javascript .

Заключительное рукопожатие

Эта фаза может быть инициирована обоими узлами . Узел, который хочет закрыть связь, должен отправить кадр управления закрытия, и он также получит кадр управления закрытия в качестве ответа.

WebSocket Javascript API (клиент)

Для связи из веб-приложения с сервером, использующим протокол WebSocket, необходимо использовать клиентский API Javascript . Это роль W3C, чтобы определить этот API. Спецификация W3C для JavaScript WebSocket API находится в стадии завершения. Интерфейс WebSocket обеспечивает, среди прочего, следующее:

  • атрибут для определения URL соединения с конечной точкой сервера ( url )
  • атрибут для определения состояния соединения ( readyState : CONNECTING, OPEN, CLOSING, CLOSED)
  • некоторый обработчик событий, связанный с жизненным циклом WebSocket, например:
    • Обработчик события onopen вызывается при открытии нового соединения
    • onerror обработчика onerror вызывается, когда во время связи произошла ошибка
    • onmessage обработчика onmessage вызывается при поступлении сообщения с сервера
  • методы ( send(DOMString data) , send(Blob data) send(DOMString data) send(Blob data) ), с помощью которых можно отправлять различные типы потоков (текстовые, двоичные) на сервер Endpoint

Пример 3. Пример исходного кода Javascript с http://websocket.org

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
var wsUri = "ws://echo.websocket.org/";
 
function testWebSocket() {
 
        websocket = new WebSocket(wsUri);
        websocket.onopen = function(evt) { onOpen(evt) };
        websocket.onclose = function(evt) { onClose(evt) };
        websocket.onmessage = function(evt) { onMessage(evt) };
        websocket.onerror = function(evt) { onError(evt) }; }
}
 
function onOpen(evt) {
        writeToScreen("CONNECTED");
        doSend("WebSocket rocks");
}
function onClose(evt) {
        writeToScreen("DISCONNECTED");
}
function onMessage(evt) {
        writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data+'</span>');
        websocket.close();
}
 
function onError(evt) {
        writeToScreen('<span style="color: red;">ERROR:</span> ' + evt.data);
}
function doSend(message) {
        writeToScreen("SENT: " + message);
        websocket.send(message);
}

JSR 386: Java API для протокола WebSocket

Поскольку W3C определяет, как использовать WebSocket в Javascript, Java Communitee Process (JCP) делает то же самое для мира Java через JSR 386. JSR 356 определяет Java API для протокола WebSocket, который является частью веб-профиля Java EE и дает способность к :

  • создайте WebSocket Endpoint (сервер или клиент), имя, данное компоненту Java, который может взаимодействовать через протокол WebSocket
  • выбор аннотации или программный подход
  • отправлять и потреблять сообщения управления, текстовые или двоичные через этот протокол
    • управлять сообщением как полным сообщением или последовательностью частичных сообщений
    • отправлять или получать сообщения как объекты Java (концепция кодеров / декодеров )
    • отправлять сообщения синхронно или асинхронно
  • настраивать и управлять сеансом WebSocket (тайм-аут, файлы cookie…)

С открытым исходным кодом JSR-356 RI (эталонная реализация) является проект Tyrus

Конечная точка сервера WebSocket

Преобразование простого старого объекта Java (POJO) в конечную точку сервера WebSocket (а именно, способную обрабатывать запросы от разных клиентов по одному и тому же URI) очень простое, поскольку вам нужно только аннотировать класс Java с помощью @ServerEndpoint и один метод с помощью @ OnMessage :

01
02
03
04
05
06
07
08
09
10
11
12
import javax.websocket.OnMessage;
import javax.websocket.ServerEndpoint;
 
@ServerEndpoint("/echo")
public class EchoServer {
 
        @OnMessage
        public String handleMessage(String message){
                return "Thanks for the message: " + message;
        }
 
}

Строка 04: @ServerEndpoint преобразует этот POJO в конечную точку WebSocket, атрибут value является обязательным для установки URI доступа к этой конечной точке
Строка 07: метод handleMessage будет вызываться для каждого полученного сообщения

Аннотации

Этот Java API предоставляет несколько типов аннотаций для полной совместимости с протоколом WebSocket:

аннотирование Роль
@ServerEndpoint Объявите конечную точку сервера
@ClientEndpoint Объявите клиентскую конечную точку
@OnOpen Объявите, что этот метод обрабатывает открытые события
@OnMessage Объявите, что этот метод обрабатывает сообщения Websocket
@OnError Объявите, что этот метод обрабатывает ошибку
@OnClose Объявите, что этот метод обрабатывает события закрытия WebSocket

@ServerEndpoint перечислены ниже:

  • значение
    • относительный URI или шаблон URI (например: «/ echo», «/ match / {match-id}»)
  • декодеры
    • список имен классов декодера сообщений
  • кодеры
    • список имен классов кодировщика сообщений
  • субпротоколы
    • список имен поддерживаемых подпротоколов (например: http://wamp.ws)

Кодеры и декодеры

Как описано ранее в этой статье, сервер Endpoint может получать различные типы содержимого в сообщениях: данные в текстовом формате (JSON, XML …) или в двоичном формате.

Чтобы эффективно управлять сообщениями от однорангового клиента или с ними в бизнес-коде приложения, можно создавать классы Java Encoders и Decoders .

Каким бы ни был алгоритм преобразования, тогда будет возможно преобразовать:

  • бизнес POJO для передачи в желаемом формате для общения (JSON, XML, Binary …)
  • приток в конкретном формате (JSON, XML ..) в бизнес POJO

Таким образом, код приложения структурирован таким образом, что на бизнес-логику не влияют тип и формат сообщений, которыми обмениваются потоки между равноправным сервером и равноправными клиентами .

Конкретный пример представлен позже в статье.

Конечная точка клиента WebSocket

Этот Java API также предлагает поддержку для создания конечных точек Java на стороне клиента.

Пример 4. Пример конечной точки клиента Java

01
02
03
04
05
06
07
08
09
10
11
@ClientEndpoint
public class HelloClient {
 
        @OnMessage
        public String message(String message){
                // code
        }
}
 
WebSocketContainer c = ContainerProvider.getWebSocketContainer();
c.connectToServer(HelloClient.class, "hello");

US OPEN Заявка

Образец приложения разворачивается как результат WAR сборки с Apache Maven. В дополнение к традиционному жизненному циклу управления WebSocket рабочий процесс отправки сообщений выглядит следующим образом:

  • каждый равноправный клиент может подключиться к 1 или 4 матчам
  • каждый равноправный клиент может отключиться от матча
  • в каждой точке матча клиенты, подключенные к этому матчу, будут получать данные (оценка, сервис…)
  • равноправный клиент может отправить сообщение, чтобы сделать ставку на победителя матча
  • каждый раз, когда один равноправный клиент делает ставку на матч, все остальные равноправные клиенты, которые сделали ставку на один и тот же матч, получат сообщение с общим количеством игроков
  • в конце матча клиент равных получает сообщение, содержащее имя победителя и конкретное сообщение, если они делают ставку на этот матч

Все сообщения обмениваются в формате JSON

План проекта выглядит следующим образом:

Пример 5. Структура проекта Maven

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
+ src/main/java
   |+ com.mgreau.wildfly.websocket
      |+ decoders
         |- MessageDecoder.java  
      |+ encoders      
         |- BetMessageEncoder.java
         |- MatchMessageEncoder.java
      |+ messages      
         |- BetMessage.java
         |- MatchMessage.java
         |- Message.java
      |+ rest      
         |- RestApplication.java
         |- TournamentREST.java
      |- MatchEndpoint.java   
      |- StarterService.java  
      |- TennisMatch.java     
+ src/main/resources
+ scr/main/webapp
   |+ css
   |+ images
   |+ js
      |+ live   
         |- app.js
         |- controllers.js
         |- directives.js
         |- services.js
      |- websocket.js 
   |+ templates
      |- bet.html
      |- match.html
      |- msg.html
   |- index.html
   |- live.html
pom.xml

Строка 04: декодировать сообщения JSON, отправленные от равноправного клиента (о ставке на победителя), в POJO ( BetMessage )
Строка 05: кодирование в формате JSON (через JSON-P), все сообщения о победителе и сведения о матче для пировых клиентов.
Строка 08: POJO для обработки сообщений, отправляемых между узлами
Строка 12: конечная точка REST для отображения всех матчей турнира
Строка 15: приложение WebSocket Server Endpoint ( одноранговый сервер )
Строка 16: EJB @Startup для инициализации этого приложения во время развертывания.
Строка 17: POJO для обработки информации о матче
Строка 23: файлы AngularJS для обработки нескольких совпадений (live.html) с вызовами REST и WebSocket
Строка 28: файл, содержащий реализацию API Javascript для протокола WebSocket для обработки клиентской части сообщения для простого случая (index.html)

Maven-зависимости для Java EE 7 API

Пример 6. pom.xml с зависимостями Java EE 7

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
<project>
...
<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <!-- Java EE 7 -->
        <javaee.api.version>7.0</javaee.api.version>
</properties
 
<dependencies>
        <dependency>
                <groupId>javax</groupId>
                <artifactId>javaee-api</artifactId>
                <version>${javaee.api.version}</version>
                <scope>provided</scope>
        </dependency>
</dependencies>
...
</project>

Строка 11: важно использовать зависимости Java EE 7, чтобы иметь возможность развертывать одно и то же приложение на нескольких серверах приложений Java EE (WildFly, Glassfish …) без изменения кода .

Добавить конечную точку сервера

Эта конечная точка может получать сообщения о ставках на победителя матча (идентифицируется по идентификатору матча), а также может отправлять равноправному клиенту всю информацию о ходе матча и ставках.

Пример 7. Конечная точка сервера: MatchEndpoint.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
@ServerEndpoint(
                value = "/matches/{match-id}"
                        decoders = { MessageDecoder.class },
                        encoders = { MatchMessageEncoder.class, BetMessageEncoder.class }
                )
public class MatchEndpoint {
 
        private static final Logger logger = Logger.getLogger("MatchEndpoint");
 
         /** All open WebSocket sessions */
        static Set<Session> peers = Collections.synchronizedSet(new HashSet<Session>());
 
        /** Handle number of bets by match */
        static Map<String, AtomicInteger> nbBetsByMatch = new ConcurrentHashMap<>();
 
        @Inject StarterService ejbService;
 
        @OnOpen
        public void openConnection(Session session,
                                        @PathParam("match-id") String matchId) {
            session.getUserProperties().put(matchId, true);
            peers.add(session);
 
            //Send live result for this match
            send(new MatchMessage(ejbService.getMatches().get(matchId)), matchId);
        }
 
        public static void send(MatchMessage msg, String matchId) {
          try {
            /* Send updates to all open WebSocket sessions for this match */
            for (Session session : queue) {
              if (Boolean.TRUE.equals(session.getUserProperties().get(matchId))){
                if (session.isOpen()){
                      session.getBasicRemote().sendObject(msg);       
                }
              }
            }
          } catch (IOException | EncodeException e) {
            logger.log(Level.INFO, e.toString());
          }
        }
 
        public static void sendBetMessage(Session session, BetMessage betMsg, String matchId)
        {
          try {
              betMsg.setNbBets(nbBetsByMatch.get(matchId).get());
              session.getBasicRemote().sendObject(betMsg);
                  logger.log(Level.INFO, "BetMsg Sent: {0}", betMsg.toString());
          } catch (IOException | EncodeException e) {
                  logger.log(Level.SEVERE, e.toString());
          }
        }
 
        @OnMessage
        public void message(final Session session, BetMessage msg,   
                                        @PathParam("match-id") String matchId) {
          session.getUserProperties().put("bet", msg.getWinner());
 
          //Send betMsg with bet count
          if (!nbBetsByMatch.containsKey(matchId)){
             nbBetsByMatch.put(matchId, new AtomicInteger());
          }
          nbBetsByMatch.get(matchId).incrementAndGet();
          sendBetMessages(null, matchId, false);
        }
 
        @OnClose
        public void closedConnection(Session session,
                            @PathParam("match-id") String matchId) {
          if (session.getUserProperties().containsKey("bet")){
                  nbBetsByMatch.get(matchId).decrementAndGet();
                  sendBetMessages(null, matchId, false);
          }
          /* Remove this connection from the queue */
          peers.remove(session);
        }
...
}

Строка 02: доступ к URI к этой конечной точке , поскольку корень контекста приложения — / usopen , окончательный URL-адрес выглядит следующим образом: ws://<host>:<port>/usopen/matches/1234
Строка 03: MessageDecoder преобразует входящий поток JSON (о ставке на победителя) в сообщение POJO BetMessage
Строка 04: эти 2 кодера добавляют возможность преобразования из MatchMessage POJO и BetMessage POJO в сообщения в формате JSON
Строка 20: аннотация @PathParam позволяет извлечь часть запроса WebSocket и передать значение (идентификатор соответствия) в качестве параметра метода, можно управлять несколькими совпадениями с несколькими клиентами для каждого совпадения.
Строка 34: Отправьте подключенным партнерам сообщения о ходе матча. Благодаря объекту MatchMessageEncoder просто передайте объект MatchMessage .
Строка 55: обработка полученных сообщений о ставке на победителя, благодаря объекту MessageDecoder , одним из параметров этого метода является объект BetMessage .

Кодирует и декодирует сообщения

Чтобы кодировать или декодировать сообщения, которыми обмениваются одноранговые узлы, просто реализуйте соответствующий интерфейс в соответствии с типом сообщения (текстовое, двоичное) и направлением обработки (кодирование, декодирование), а затем переопределите соответствующий метод.

В приведенном ниже примере это кодировщик для MatchMessage POJO в формате JSON. API, используемый для выполнения этой обработки, также является новым API, выпущенным с Java EE 7: Java API для JSON Processiong (JSON-P)

Пример 8. Текстовый кодировщик: MatchMessageEncoder.java

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
public class MatchMessageEncoder implements Encoder.Text<MatchMessage> {
 
        @Override
        public String encode(MatchMessage m) throws EncodeException {
                StringWriter swriter = new StringWriter();
                try (JsonWriter jsonWrite = Json.createWriter(swriter)) {
                        JsonObjectBuilder builder = Json.createObjectBuilder();
                        builder.add(
                                "match",
                                Json.createObjectBuilder()
                                        .add("serve", m.getMatch().getServe())
                                        .add("title", m.getMatch().getTitle())
                                        ...
                        }
 
                        jsonWrite.writeObject(builder.build());
                }
                return swriter.toString();
        }
}

Веб-клиент HTML5 (одно совпадение — index.html)

Страница index.html этого приложения загружает файл websocket.js для реализации API Javascript WebSocket и, таким образом, взаимодействует с конечной точкой сервера Java. Эта страница обрабатывает только одно совпадение.

Пример 9. API Javascript, реализованный в websocket.js

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
var wsUrl;
if (window.location.protocol == 'https:') { 
        wsUrl = 'wss://' + window.location.host + ':8443/usopen/matches/1234';
} else {
        wsUrl = 'ws://' + window.location.host + ':8000/usopen/matches/1234';
}
 
function createWebSocket(host) {
        if (!window.WebSocket) {   
        ...
        } else {
                socket = new WebSocket(host);  
                socket.onopen = function() {
                        document.getElementById("m1-status").innerHTML = 'CONNECTED...';
                };
                socket.onclose = function() {
                        document.getElementById("m1-status").innerHTML = 'FINISHED';
                };
                ...
                socket.onmessage = function(msg) {
                        try {
                                console.log(data);
                                var obj = JSON.parse(msg.data);    
                                if (obj.hasOwnProperty("match")){  
                                        //title
                                        m1title.innerHTML = obj.match.title;
                                        // comments
                                        m1comments.value = obj.match.comments;
                                        // serve
                                        if (obj.match.serve === "player1") {
                                                m1p1serve.innerHTML = "S";
                                                m1p2serve.innerHTML = "";
                                        } else {
                                                m1p1serve.innerHTML = "";
                                                m1p2serve.innerHTML = "S";
                                        }
                                        ..
                                }
                                ...
                        } catch (exception) {
                                data = msg.data;
                                console.log(data);
                        }
                }
        }
}

Строка 02: выберите соответствующий протокол WebSocket в соответствии с используемым протоколом HTTP (безопасный или нет).
Строка 09: проверьте, поддерживает ли браузер API WebSocket
Строка 12: создание объекта WebSocket
Строка 23. Попытайтесь проанализировать сообщение JSON, отправленное одноранговым сервером , в функцию, вызываемую onmessage события onmessage
Строка 24: проверьте полученный тип объекта (MatchMessage или BetMessage), чтобы добиться адекватного лечения с помощью DOM

Чтобы узнать, какие браузеры совместимы с WebSocket API, посетите веб-сайт caniuse.com . На сегодняшний день последние версии браузеров совместимы, за исключением Android и Opera Mini Browser, которые вместе представляют только 3% веб-трафика.

Клиент AngularJS (несколько совпадений — live.html)

Как мы видели в начале этого поста, версия для обработки нескольких совпадений разработана с AngularJS на стороне клиента. Итак, есть 4 файла JS, которые содержат:

  • app.js : просто определите угловое приложение tennisApp
  • controllers.js : контроллер TournamentCtrl
  • directives.js :
    • директива пари и шаблон bet.html для отображения количества игроков и текущей ставки
    • директива match и шаблон match.html для обработки оперативной информации
    • директива msg и шаблон msg.html для показа сообщения в конце матча с результатом ставки, если это необходимо
  • services.js :
    • WebSocketService: для обработки жизненного цикла WebSocket с обратным вызовом
    • TournamentRESTService: получить все совпадения с сервера REST Endpoint
    • BetsService и MatchesServices

Этот пост не является учебником по AngularJS, так как это мое первое приложение с этим фреймворком. Но это было быстрое решение для обработки нескольких матчей на стороне клиента. Вы можете прочитать 3 следующих поста, чтобы лучше понять код JS: директивы на французском языке и рефакторинг к директивам AngularJS и пример AngularJS WebSocket Service

Исходный код на Github

Вы можете раскошелиться на Github по адресу https://github.com/mgreau/javaee7-websocket

Этот пример приложения является базовым, может быть много улучшений, таких как ставки по другим критериям …

Особенностью, которая технически может быть интересной, будет создание нового типа ставки на основе координат каждой выигрышной точки . Просто нарисуйте основание с помощью HTML5 Canvas API и управляйте координатами, выбранными пользователем (например, выигрышной точкой), а затем сравните с фактическими координатами победителя точки.

Построить и развернуть войну

Предварительное условие:

  • JDK 7
  • Apache Maven 3.0.4+
  • Сервер приложений Java EE 7 (WildFly 8)

Чтобы построить WAR, вам нужно просто выполнить команду Maven ниже;

1
mvn clean package

Если вашим сервером приложений является WildFly, вы можете быстро развернуть WAR с помощью следующей команды (WildFly должен быть запущен):

1
mvn jboss-as:deploy

Приложение usopen доступно по адресу:

WildFly 8 использует свой новый Web Server called Undertow который заменяет Tomcat.

Я не тестировал это приложение на Glassfish 4, но поскольку я использую только зависимости Java EE 7 API, оно может без проблем работать с тем же кодом. Дайте мне знать, если у вас есть проблемы с GlassFish.

Тест: WebSocket VS REST

Чтобы получить некоторые показатели производительности нового протокола, Арун Гупта разработал приложение, которое позволяет сравнивать время выполнения одной и той же обработки, выполняемой кодом WebSocket и кодом REST.

Каждая конечная точка (конечная точка REST и конечная точка WebSocket) просто выполняет «эхо», поэтому они возвращают только потоки, которые они получают. Веб-интерфейс приложения позволяет вам определять размер сообщения и количество раз, которое сообщение должно быть отправлено до окончания теста.

Результаты тестов, показанные ниже, довольно красноречивы:

Запрос Общее время выполнения
Конечная точка REST
Общее время выполнения
Конечная точка WebSocket
Отправка 10 сообщений по 1 байту 220 мс (63 мс) * 7 мс (29 мс) *
Отправка 100 сообщений по 10 байт 986 мс (587 мс) * 57 мс (74 мс) *
Отправка 1000 сообщений по 100 байт 10 210 мс (4 636 мс) * 179 мс (288 мс) *
Отправка 5000 сообщений по 1000 байт 54 449 мс (18 049 мс) * 1202 мс (2862 мс) *

Значения между () * представляют те же тесты на моем ноутбуке (MacBook Pro 2013 i5 — 8Go — 128SSD) с WildFly 8.0.0-beta1 с конфигурацией по умолчанию.

Отзывы о WebSocket

Я особенно рекомендую конференции Аруна Гупты , которые позволят вам менее чем за 1 час открыть и понять технологию WebSocket в целом и Java API для WebSocket.

Для получения более подробной информации идеально подходят спецификации IETF, W3C и Java.

Эта статья была построена на основе конференции Devoxx в Великобритании 2013 года.

Вывод

В этой статье на конкретном примере представлены протокол WebSocket, API-интерфейс HTML5 WebSocket и API Java для WebSocket, выпущенные с Java EE 7 . Уже было возможно использовать WebSocket с Java-фреймворками, такими как Atmosphere, но не было стандарта.

Сегодня все стандарты завершены или должны быть выполнены , эта новая технология отвечает конкретным потребностям и является многообещающей с точки зрения производительности. Для интенсивного использования этот протокол должен быть разрешен в компаниях, где часто разрешен только протокол HTTP.