Настройте веб-сокеты с помощью Jetty 11

Я пытаюсь перейти с Jetty 9.4 на Jetty 11 (может быть, слишком рано?) и не могу адаптировать код для настройки веб-сокетов. Способ, которым я достиг этого в 9.4, был следующим:

Server server = new Server();
HttpConfiguration httpConfig = new HttpConfiguration();
httpConfig.setSendServerVersion(false);
HttpConnectionFactory httpFactory = new HttpConnectionFactory(httpConfig);
ServerConnector httpConnector = new ServerConnector(server, httpFactory);
httpConnector.setPort(port);
server.setConnectors(new Connector[] { httpConnector });

// Setup the basic application "context" for this application at "/"
// This is also known as the handler tree (in jetty speak)
ServletContextHandler context = new ServletContextHandler(ServletContextHandler.SESSIONS);
context.setContextPath("/");

// Add a websocket to a specific path spec
ServletHolder holderEvents2 = new ServletHolder("websocket", EventsServlet.class);
context.addServlet(holderEvents2, "/events/*");

HandlerList handlers = new HandlerList();
handlers.setHandlers(new Handler[] { context, new DefaultHandler() });

server.setHandler(handlers);

public class EventsServlet extends WebSocketServlet {

    @Override
    public void configure(WebSocketServletFactory factory) {
        // register a socket class as default
        factory.register(EchoSocket.class);
    }
}

public class EchoSocket implements WebSocketListener {
    // ...
}

Поскольку класса WebSocketServlet больше нет, я немного повозился и нашел класс JettyWebSocketServlet. Согласно его JavaDoc, я думал, что он должен выглядеть следующим образом:

public class EventsServlet extends JettyWebSocketServlet {

    @Override
    protected void configure(JettyWebSocketServletFactory factory) {
        // register a socket class as default
//      factory.register(EchoSocket.class);
           factory.addMapping("/", (req,res)->new EchoSocket());

    }
}

но строка с addMapping на самом деле никогда не выполняется. Также JettyWebSocketServletFactory не имеет метода setDefaultMaxFrameSize, как это предлагается в JavaDoc JettyWebSocketServlet.

Кажется, все, что я могу найти в Интернете, это для Jetty <= 9.4, даже https://github.com/jetty-project/embedded-jetty-websocket-examples.

Любая помощь будет высоко оценен.

Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
7
0
4 445
3
Перейти к ответу Данный вопрос помечен как решенный

Ответы 3

Ответ принят как подходящий

У меня была похожая проблема, хотя моя версия, работающая под Jetty 9.4, немного отличалась от вашей, используя WebSocketHandler, а не WebSocketServlet. У меня были некоторые проблемы со старым подходом, так как в Jetty 9.4 мне приходилось передавать свой класс слушателя как объект Class, что делало внедрение зависимостей болезненным.

Теперь у меня это работает под Jetty 11.0.0. Я нашел ваш вопрос пару дней назад, когда пытался понять, как это сделать в Jetty 11, и это вдохновило меня на то, чтобы это действительно заработало, так что спасибо!

FWIW, моя версия Jetty 9.4 (для тривиального теста) выглядела так:

public static void main(String[] argv) throws Exception
{
    int serverPort = Integer.getInteger("server.port", 8080);

    Server server = new Server(serverPort);
    ContextHandlerCollection handlers = new ContextHandlerCollection();

    WebSocketHandler wsh = new WebSocketHandler.Simple (TestWebSocketListener.class);
    handlers.addHandler(createContextHandler("/ws", wsh));

    ResourceHandler rh = new ResourceHandler();
    rh.setDirectoriesListed(false);
    rh.setBaseResource(Resource.newClassPathResource("/WEB-STATIC/"));
    handlers.addHandler(createContextHandler("/", rh));

    server.setHandler(handlers);

    server.start();
    server.join();
}

// Convenience method to create and configure a ContextHandler.
private static ContextHandler createContextHandler(String contextPath, Handler wrappedHandler)
{
    ContextHandler ch = new ContextHandler (contextPath);
    ch.setHandler(wrappedHandler);
    ch.clearAliasChecks();
    ch.setAllowNullPathInfo(true);
    return ch;
}

Здесь TestWebSocketListener — это тривиальная реализация WebSocketListener, которая просто реализует каждый метод слушателя и выводит аргументы в System.err. (Я сказал, что это был тривиальный тест.) Я также отправляю сообщение клиенту в обратном вызове onWebSocketText, просто чтобы проверить, что это работает.

Я не использую здесь DefaultHandler — вместо этого я явно создаю ResourceHandler, который обслуживает несколько простых статических ресурсов из дерева ресурсов, хранящегося в пути к классам (под префиксом /WEB-STATIC/).

Версия, которая у меня работает под Jetty 11.0.0, просто меняет метод main выше на это:

public static void main(String[] argv) throws Exception
{
    int serverPort = Integer.getInteger("server.port", 8080);

    Server server = new Server(serverPort);
    ContextHandlerCollection handlers = new ContextHandlerCollection();

    ResourceHandler rh = new ResourceHandler();
    rh.setDirectoriesListed(false);
    rh.setBaseResource(Resource.newClassPathResource("/WEB-STATIC/"));
    handlers.addHandler(createContextHandler("/", rh));

    Servlet websocketServlet = new JettyWebSocketServlet() {
        @Override protected void configure(JettyWebSocketServletFactory factory) {
            factory.addMapping("/", (req, res) -> new TestWebSocketListener());
        }
    };
    ServletContextHandler servletContextHandler = new ServletContextHandler();
    servletContextHandler.addServlet(new ServletHolder(websocketServlet), "/ws");
    JettyWebSocketServletContainerInitializer.configure(servletContextHandler, null);
    handlers.addHandler(servletContextHandler);

    server.setHandler(handlers);

    server.start();
    server.join();
}

Вызов JettyWebSocketServletContainerInitializer.configure важен: без него я получал исключения, жалующиеся на то, что компоненты WebSocket не были инициализированы.

Следует отметить, что порядок двух обработчиков был изменен — ранее WebSocketHandler добавлялся перед ResourceHandler. Однако при использовании ServletContextHandler это возвращало 404 для путей, которые должны были быть обработаны ResourceHandler, поэтому я поменял порядок.

TestWebSocketListener одинаков в двух версиях. Очевидно, мне намного проще добавить внедрение зависимостей, теперь я контролирую вызов конструктора!

Еще одна вещь, которую мне пришлось изменить, — это имена артефактов Maven, которые я загрузил. Артефакт websocket-server, похоже, больше не существует в Jetty 11, поэтому я изменил это:

<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>9.4.35.v20201120</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty.websocket</groupId>
    <artifactId>websocket-server</artifactId>
    <version>9.4.35.v20201120</version>
</dependency>

к этому:

<dependency>
    <groupId>org.eclipse.jetty</groupId>
    <artifactId>jetty-server</artifactId>
    <version>11.0.0</version>
</dependency>
<dependency>
    <groupId>org.eclipse.jetty.websocket</groupId>
    <artifactId>websocket-jetty-server</artifactId>
    <version>11.0.0</version>
</dependency>

Большое спасибо за подробный и полезный ответ, который помог мне решить мою проблему. Я добавлю окончательное решение для моего кода в качестве еще одного ответа.

naf 30.12.2020 22:22

Для тех, кому нужен TestWebSocketListener : класс TestWebSocketListener реализует WebSocketListener { @Override public void onWebSocketText (сообщение java.lang.String) { System.out.println (сообщение); } }

Ben 02.11.2022 18:29

строка -> JettyWebSocketServletContainerInitializer.configure(servletC‌​ontextHandler, null); сделал разницу. Я получал ошибку Компоненты Websocket не инициализировали ошибку без этого.

Sujal Mandal 28.03.2023 08:55

Благодаря подробному объяснению mdf я смог исправить свой код. В итоге осталось только заменить

ServletHolder holderEvents = new ServletHolder("websocket", EventsServlet.class);
context.addServlet(holderEvents, "/events/*");

с

Servlet websocketServlet = new JettyWebSocketServlet() {
    @Override
    protected void configure(JettyWebSocketServletFactory factory) {
        factory.addMapping("/", (req, res) -> new EchoSocket());
    }
};
context.addServlet(new ServletHolder(websocketServlet), "/events/*");
JettyWebSocketServletContainerInitializer.configure(context, null);

При этом я также мог избавиться от класса EventsServlet.

Вы можете найти информацию в примерах Jetty 11 https://github.com/jetty-project/embedded-jetty-websocket-examples/blob/11.0.x/native-jetty-websocket-example/src/main/java /org/eclipse/jetty/demo/EventServer.java

Обновлен с пристани 9 со следующими пакетами

  1. org.eclipse.jetty:jetty-server:11.0.0
  2. org.eclipse.jetty:jetty-servlet:11.0.0
  3. org.eclipse.jetty:jetty-annotations:11.0.0
  4. org.eclipse.jetty.websocket:websocket-jetty-server:11.0.0
  5. org.eclipse.jetty.websocket:websocket-jetty-client:11.0.0

Обратите внимание, что имена websocket-client и websocket-server изменились на websocket-jetty-client и websocket-jetty-server.

как указывает @mdf JettyWebSocketServletContainerInitializer.configure, позволяет избавиться от следующего сообщения:

WebSocketComponents не был создан

Теперь мое приложение работает с причалом 11.

это мой WebsocketServlet

import org.eclipse.jetty.websocket.server.JettyWebSocketServlet;
import org.eclipse.jetty.websocket.server.JettyWebSocketServletFactory;

public class KernelServlet extends JettyWebSocketServlet {
    @Override
    public void configure(JettyWebSocketServletFactory factory) {
        factory.register(KernelHandler.class);
    }
}

а это код инициализации сервера

Server server = new Server(port);
ServletContextHandler contextHandler = new ServletContextHandler(ServletContextHandler.SESSIONS);
contextHandler.setContextPath("/");
contextHandler.addServlet(WebClientServlet.class, "/client");
contextHandler.addServlet(KernelServlet.class, "/kernel");
JettyWebSocketServletContainerInitializer.configure(contextHandler, null);
try {
    server.setHandler(contextHandler);
    server.start();
    server.join();
} catch (Exception e) {
    e.printStackTrace();
}

Другие вопросы по теме