Следующая версия JBoss Application Server больше не будет использовать Tomcat в качестве интегрированного веб-сервера, а заменит его на undertow . Архитектура undertow основана на обработчиках, которые можно динамически добавлять через API-интерфейс Builder на сервер. Этот подход аналогичен способу создания веб-сервера в Node.js. Это позволяет разработчикам легко встраивать веб-сервер undertow в свои приложения. Поскольку добавление функций осуществляется через API-интерфейс Builder, можно добавлять только те функции, которые действительно необходимы в приложении. Помимо этого, поддерживается версия WebSockets и Servlet API в версии 3.1. Его можно запускать как блокирующий или неблокирующий сервер, и говорят, что первые тесты доказали, что undertow является самым быстрым веб-сервером, написанным на Java.
Поскольку все это звучит очень многообещающе, давайте попробуем настроить простой сервер веб-сокетов. Как обычно, мы начинаем с создания простого Java-проекта и добавляем зависимость undertow maven:
|
1
2
3
4
5
|
<dependency> <groupId>io.undertow</groupId> <artifactId>undertow-core</artifactId> <version>1.0.0.Beta20</version></dependency> |
С помощью API Undertow Builder наш метод buildAndStartServer () выглядит следующим образом:
|
1
2
3
4
5
6
7
|
public void buildAndStartServer(int port, String host) { server = Undertow.builder() .addListener(port, host) .setHandler(getWebSocketHandler()) .build(); server.start();} |
Мы просто добавляем прослушиватель, который указывает порт и хост для прослушивания входящих соединений, а затем добавляем обработчик websocket. Поскольку код обработчика веб-сокетов немного более полон, я поместил его в собственный метод:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
|
private PathHandler getWebSocketHandler() { return path().addPath("/websocket", websocket(new WebSocketConnectionCallback() { @Override public void onConnect(WebSocketHttpExchange exchange, WebSocketChannel channel) { channel.getReceiveSetter().set(new AbstractReceiveListener() { @Override protected void onFullTextMessage(WebSocketChannel channel, BufferedTextMessage message) { String data = message.getData(); lastReceivedMessage = data; LOGGER.info("Received data: "+data); WebSockets.sendText(data, channel, null); } }); channel.resumeReceives(); } })) .addPath("/", resource(new ClassPathResourceManager(WebSocketServer.class.getClassLoader(), WebSocketServer.class.getPackage())) .addWelcomeFiles("index.html"));} |
Давайте пройдем по строке через этот фрагмент кода. Прежде всего мы добавим новый путь: / websocket. Второй аргумент методов addPath () позволяет нам указать, какой протокол мы хотим использовать для этого пути. В нашем случае мы создаем новый WebSocket. Анонимная реализация имеет метод onConnect (), в котором мы устанавливаем реализацию AbstractReceiveListener. Здесь у нас есть удобный метод onFullTextMessage (), который вызывается, когда клиент отправил нам текстовое сообщение. Вызов getData () извлекает фактическое сообщение, которое мы получили. В этом простом примере мы просто возвращаем эту строку клиенту, чтобы проверить, работает ли туда-обратно клиент и сервер и обратно.
Для выполнения простых ручных тестов мы также добавляем второй ресурс под путем /, который обслуживает некоторые статические файлы HTML и JavaScript. Каталог, содержащий эти файлы, предоставляется в качестве экземпляра ClassPathResourceManager. Вызов addWelcomeFiles () сообщает, какой файл серверу, когда клиент запрашивает путь /.
Index.html выглядит так:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
</pre><html><head><title>Web Socket Test</title></head><body> <script src="jquery-2.0.3.min.js"></script> <script src="jquery.gracefulWebSocket.js"></script> <script src="websocket.js"></script> <form onsubmit="return false;"> <input type="text" name="message" value="Hello, World!"/> <input type="button" value="Send Web Socket Data" onclick="send(this.form.message.value)"/> </form> <div id="output"></div></body></html><pre> |
Наш код JavaScript выгружается в файл websocket.js. Мы используем jquery и jquery-плагин gracefulWebSocket, чтобы упростить разработку на стороне клиента:
|
1
2
3
4
5
6
7
8
9
|
var ws = $.gracefulWebSocket("ws://127.0.0.1:8080/websocket");ws.onmessage = function(event) { var messageFromServer = event.data; $('#output').append('Received: '+messageFromServer+'');}function send(message) { ws.send(message);} |
После создания объекта WebSocket путем вызова $ .gracefulWebSocket () мы можем зарегистрировать функцию обратного вызова для входящих сообщений. В этом методе мы только добавляем строку сообщения в DOM страницы. Метод send () — это просто вызов метода send () gracefulWebSocket.
Теперь, когда мы запускаем наше приложение и открываем URL-адрес http://127.0.0.1:8080/ в нашем веб- браузере, мы видим следующую страницу:
Ввод какой-либо строки и нажатие кнопки «Отправить данные веб-сокета» отправляет сообщение на сервер, который в ответ возвращает его клиенту.
Теперь, когда мы знаем, что все работает как положено, мы хотим защитить наш код от регрессии с помощью тестового примера junit. В качестве клиента websocket я выбрал библиотеку jetty-websocket:
|
1
2
3
4
5
6
|
<dependency> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-websocket</artifactId> <version>8.1.0.RC5</version> <scope>test</scope></dependency> |
В тестовом примере мы создаем и запускаем сервер websocket, чтобы открыть новое соединение с портом websocket. Реализация jetty-websocket в WebSocket позволяет нам реализовать два метода обратного вызова для событий open и close. В рамках открытого обратного вызова мы отправляем тестовое сообщение клиенту. Остальная часть кода ожидает установления соединения, закрывает его и подтверждает, что сервер получил сообщение:
|
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
|
@Testpublic void testStartAndBuild() throws Exception { subject = new WebSocketServer(); subject.buildAndStartServer(8080, "127.0.0.1"); WebSocketClient client = new WebSocketClient(); Future connectionFuture = client.open(new URI("ws://localhost:8080/websocket"), new WebSocket() { @Override public void onOpen(Connection connection) { LOGGER.info("onOpen"); try { connection.sendMessage("TestMessage"); } catch (IOException e) { LOGGER.error("Failed to send message: "+e.getMessage(), e); } } @Override public void onClose(int i, String s) { LOGGER.info("onClose"); } }); WebSocket.Connection connection = connectionFuture.get(2, TimeUnit.SECONDS); assertThat(connection, is(notNullValue())); connection.close(); subject.stopServer(); Thread.sleep(1000); assertThat(subject.lastReceivedMessage, is("TestMessage"));} |
- Как обычно, вы можете найти исходный код на github .
Вывод
API-интерфейс Undertow Builder упрощает создание сервера веб-сокетов и в целом встроенного веб-сервера, который соответствует вашим потребностям. Это также упрощает автоматическое тестирование, поскольку вам не требуется какой-либо специальный плагин maven, который запускает и останавливает ваш сервер до и после ваших интеграционных тестов. Помимо этого, плагин jquery jquery-graceful-websocket позволяет отправлять и получать сообщения через веб-сокеты всего с несколькими строками кода.
