Статьи

API клиента WebSocket в Java EE 7

В этой статье мы рассмотрим менее обсуждаемый API-интерфейс Web Socket Client и способы его использования в самом контейнере Java EE 7.

Правила API Web Socket Server

Серверный API JSR 356 (Web Socket API для Java) чаще всего используется для построения реализаций конечных точек Web Socket. Чаще всего, с точки зрения клиента, стандартный JavaScript Web Socket API используется клиентами на основе HTML5 (браузер), которые подключаются к конечным точкам сервера веб-сокетов и пользуются двунаправленной и дуплексной связью. Вы бы видели распространенные примеры таких приложений, как живые карты, биржевые тикеры, игры, совместное использование экрана и т. Д. — все эти варианты использования идеально подходят для веб-сокетов, а Java EE 7 — идеальная платформа для создания масштабируемой серверной части, управляемой веб-сокетами.

А как насчет клиентского API Web Socket?

Спецификация Web Socket также включает в себя API на стороне клиента и обязательна для всех реализаций JSR 356 (например, Tyrus , Undertow и т. Д.) Для его предоставления. Существует довольно много случаев, когда клиент на основе веб-сокета / клиент, ориентированный на браузер, может не потребоваться

пример

Рассмотрим сценарий, в котором вы хотите подключиться к конечной точке веб-сокета стороннего производителя, использовать ее информацию и сохранить ее для дальнейшего использования? Может быть для дальнейшего анализа? В таких случаях полезно использовать клиентский API в самом контейнере Java EE.

Давайте рассмотрим это на простом примере.

(аннотированный) Web Socket Client

Примечание: логика для @OnMessage была исключена специально и была реализована другим способом (пояснено позже)

package blog.abhirockzz.wordpress.com;

import javax.websocket.ClientEndpoint;
import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.Session;

@ClientEndpoint
public class StockTickerClient {

    @OnClose
    public void closed(Session session) {
        System.out.println("Session " + session + " closed");

    }

    @OnError
    public void error(Throwable error) {
        System.out.println("Error: " + error.getMessage());

    }

}

Фондовый тикер (информация) JPA

package blog.abhirockzz.wordpress.com;

import java.io.Serializable;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "STOCK_TICK")
public class StockTick implements Serializable {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Long id;

    private String name;
    private String price;

    public StockTick(String name, String price) {
        this.name = name;
        this.price = price;
    }

    public StockTick() {
        //for JPA
    }

    //getters and setters omitted ...
}

Бин без гражданства

  • Обрабатывает постоянство информации тикера
  • Выполняет свои операции с источником данных JDBC по умолчанию, предоставленным контейнером Java EE 7 ( соглашение о конфигурации  в действии!)
package blog.abhirockzz.wordpress.com;

import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

@Stateless
public class StockInfoPersistenceService {

    @PersistenceContext
    EntityManager em;

    public void save(String name, String price){
        em.persist(new StockTick(name, price));
    }
}

Синглтон EJB

  • Использует API веб-сокета ContainerProvider
  • Инициирует соединение с сервером веб-сокетов
  • Впрыскивает StockInfoPersistenceService фасоли и использует его в рамках addMessageHandler реализации

Как и в предыдущем примечании, логика (постоянства), которая могла быть встроена в аннотированный метод @OnMessage в классе StockTickerClient , была включена сюда. Это связано с тем, что произошла ошибка при внедрении компонента StockInfoPersistenceService (без сохранения состояния), а сам экземпляр был преобразован в ноль .

package blog.abhirockzz.wordpress.com;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.Singleton;
import javax.ejb.Startup;
import javax.inject.Inject;
import javax.websocket.ContainerProvider;
import javax.websocket.DeploymentException;
import javax.websocket.MessageHandler;
import javax.websocket.Session;
import javax.websocket.WebSocketContainer;

@Singleton
@Startup
public class StockServiceBootstrapBean {

    private final String WS_SERVER_URL = "ws://api.stocks/ticker"; //fictitious
    private Session session = null;

    @Inject
    StockInfoPersistenceService tickRepo;

    @PostConstruct
    public void bootstrap() {
        WebSocketContainer webSocketContainer = null;
        try {
            webSocketContainer = ContainerProvider.getWebSocketContainer();
            session = webSocketContainer.connectToServer(StockTickerClient.class, new URI(WS_SERVER_URL));

            System.out.println("Connected to WS endpoint " + WS_SERVER_URL);
            session.addMessageHandler(new MessageHandler.Whole<String>() {

                @Override
                public void onMessage(String msg) {
                    tickRepo.save(msg.split(":")[0], msg.split(":")[1]);
                }
            });
        } catch (DeploymentException | IOException | URISyntaxException ex) {
            Logger.getLogger(StockServiceBootstrapBean.class.getName()).log(Level.SEVERE, null, ex);
        }
    }

    @PreDestroy
    public void destroy() {
        close();
    }

    private void close() {
        try {
            session.close();
            System.out.println("CLOSED Connection to WS endpoint " + WS_SERVER_URL);
        } catch (IOException ex) {
            Logger.getLogger(StockServiceBootstrapBean.class.getName()).log(Level.SEVERE, null, ex);
        }
    }
}

Вот и все. Хотя это был сравнительно простой пример, не сложно представить, что к информации, получаемой конечной точкой сервера веб-сокетов, можно применять любую сложную бизнес-логику. Вы также можете думать об отправке сообщений на подключенные клиент в асинхронном режиме с использованием session.getAsyncRemote # SendAsync метода