Следующая версия 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
|
@Test public 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 позволяет отправлять и получать сообщения через веб-сокеты всего с несколькими строками кода.