9 авг. 2015 г.

Архитектура GWT WebSocket приложения

В прошлом посте я выложил библиотеку на GWT, которая оборачивает sockJS, тем самым делая доступным протокол websocket для GWT.   В этом посте я хочу предложить архитектуру, для GWT приложения, которое использует веб сокеты. Сразу же ссылка на проект на гитхабе
Приложение будет состоять из серверной и клиентской части. Клиент по кнопке будет отправлять два числа на сервер (например, 2 и 3), сервер будет возвращать сумму этих чисел, и выражение, по которому он эту сумму написал (2+3). Ну и дополнительно пусть возвращает текущее время.

Вообще фишка GWT в том, что одни и те же классы могут быть использованы так и на клиенте, так и на сервере, этого же хочется и для приложения с вебсокетами. Также хочется разделить серверную и клиентскую часть с возможностью заменить какую-либо не трогая остальное. Напрашивается следующая структура модулей:


Создается модуль DTO, в котором объявлены интерфейсы DTO объектов и выполнены их реализации. Этот модуль используют два других модуля: GWT модуль и WebSocket Server модуль. Здесь ссылка на демо-проект, сделанный по данной архитектуре.

Dto модуль

Как я писал ранее для dto нужно объявлять и реализовывать интерфейсы. Это нужно для конвертации данных в json и обратно в условиях gwt-autobean и отсутствия рефлексии.

Информация, от клиента к серверу (те самые 2 числа):
public interface ClientInfo {
    Integer getA();
    void setA(Integer a);
    Integer getB();
    void setB(Integer b);
} 
От сервера к клиенту для сложности пусть летит объект в объекте. Внутренний объект будет содержать время:
public interface ServerAddInfo {
    Date getTime();
    void setTime(Date time);
}
А внешний задачу, результат и объект со временем:
public interface ServerInfo {
    Integer getResult();
    void setResult(Integer result);
    String getProblem();
    void setProblem(String problem);
    ServerAddInfo getServerAddInfo();
    void setServerAddInfo(ServerAddInfo serverAddInfo);
}
Реализовать интерфейсы нужно в этом же модуле и делается это без проблем.

Server модуль

Этот мануал  рассказывает как за несколько минут сделать серверное websocket приложение. В моем случае суть приложения состоит в контроллере: 
@Controller
public class WebSocketController {

    @Autowired
    private SimpMessagingTemplate messagingTemplate;

    @MessageMapping("/say")
    public void say(ClientInfoImpl clientInfoImpl) {
        int a = clientInfoImpl.getA();
        int b = clientInfoImpl.getB();
        String problem = a  + "+" + b;
        int result = a + b;
        sendResult(new ServerInfoImpl(problem, result, new ServerAddInfoImpl(new Date())));
    }

    private void sendResult(ServerInfoImpl serverInfoImpl) {
        messagingTemplate.convertAndSend("/topic/info", serverInfoImpl);
    }
}
Нужно заметить, что сервер должен работать не с интерфейсами, а с реализациями. Это касается только методов, где происходит конвертация объектов в json. И еще раз хочу обратить внимание, что метод sendResult может быть вызван не только в ответ на сообщение от клиента, но и в любой момент.

Client модуль

Этот мануал  рассказывает как за несколько минут сделать клиентское websocket приложение.  Суть приложения заключается в создании WS компонента:
//create WS configuration
        WSConfiguration<ClientInfo, ServerInfo> wsConfiguration = new WSConfiguration<ClientInfo, ServerInfo>()
                .withUrl("http://127.0.0.1:8080/websocketservice")  //ws URL
                .withSubscribeUrl("/topic/info") //URL to subscribe
                .withGClass(ServerInfo.class) //dto class for Getting Data
                .withSClass(ClientInfo.class) //dto class for Sending Data
                .withAutoBeanFactory(GWT.<DtoFactory>create(DtoFactory.class)); //factory

        //creating component
        final WSComponent<ClientInfo, ServerInfo> wsComponent = new WSComponent<>(wsConfiguration);

        //setting callback for income messages
        wsComponent.setCallback(new WSCallback<ServerInfo>() {
            @Override
            public void onMessage(ServerInfo serverInfo) {
                String msg = serverInfo.getProblem() + "=" + serverInfo.getResult();
                msg += " " + serverInfo.getServerAddInfo().getTime();
                Window.alert(msg);
            }
        });
Интерфейсы dto передаются в конфигурацию. Отправка сообщения осуществляется таким образом:
wsComponent.send(new ClientInfoImpl(3,2), "/app/say");

Сборка

Я сделал так, чтобы клиентская и серверная часть собирались в разные war. Тогда они получаются независимыми друг от друга, а единственная связь между ними: зависимость от общего модуля dto.

Комментариев нет :

Отправить комментарий