Может ли сервер Tyrus использовать конечные точки внутреннего класса?

У меня возникла проблема с запуском чрезвычайно простого автономного сервера веб-сокетов Tyrus. Я заставил это работать при очень специфических обстоятельствах, которые не имеют для меня смысла.

Правильно работает случай, когда я определяю класс конечной точки сервера верхнего уровня (в своем собственном файле) и аннотирую его с помощью @ServerEndpoint. Этот класс включает в себя методы, аннотированные @OnOpen, @OnMessage и @OnClose, все типичные вещи. Я передаю этот класс конструктору сервера. Мой простой клиент может подключиться к этому серверу и успешно отправить ему сообщения, полученные сервером.

Проблема возникает, когда я изменяю класс конечной точки сервера верхнего уровня на внутренний класс класса, который инициализирует сервер (это ЕДИНСТВЕННОЕ изменение, которое я сделал). В этом случае мой клиент может подключиться, и вызывается клиентский метод @OnOpen. Но сервер не создает экземпляр конечной точки сервера и, следовательно, его метод @OnOpen никогда не вызывается. Очевидно, что получение сообщения сервера не происходит.

Есть ли в Tyrus требование, чтобы аннотированные классы конечных точек сервера не могли быть внутренними классами? Если нет, существуют ли определенные разрешения для классов конечных точек сервера (все было обнародовано, пытаясь заставить это работать, но безуспешно)? Я использую Tyrus 1.9 с JDK 1.7 на Mac, OSX 1.9.5.

Простой сервер (включая и настроенный для использования внутренней конечной точки сервера, которая выходит из строя):

package tyrus.example;

import java.util.concurrent.TimeUnit;

import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

import org.glassfish.tyrus.server.Server;

public class SimpleServer
{
    private static final String HOST_ADDR = "localhost";
    private static final int HOST_PORT = 8025;

    public static void main(String[] args) {
        Server websocketServer = new Server(HOST_ADDR, HOST_PORT, "/ws", null, InnerSimpleServerEndpoint.class);

        try {
            websocketServer.start();
            Thread.sleep(TimeUnit.MINUTES.toMillis(5));
        }
        catch (Exception e) {
            e.printStackTrace();
        }

        websocketServer.stop();

        System.out.println("Done.");
    }

    @ServerEndpoint("/myapp")
    public class InnerSimpleServerEndpoint {
        @OnOpen
        public void onOpen(Session session) {
            System.out.println("Connection received for "+session.getRequestURI());
        }

        @OnMessage
        public void onMessage(String message, Session session) {
            System.out.println("Message received: "+message);
        }

        @OnClose
        public void onClose(Session session, CloseReason closeReason) {
            System.out.println("Session closed, reason: "+closeReason);
        }
    }
}

Простой клиент:

package tyrus.example;

import java.io.IOException;
import java.net.URI;

import javax.websocket.ClientEndpointConfig;
import javax.websocket.Endpoint;
import javax.websocket.EndpointConfig;
import javax.websocket.MessageHandler;
import javax.websocket.Session;

import org.glassfish.tyrus.client.ClientManager;

public class SimpleClient
{
    private static final String DEF_WS_URL = "ws://localhost:8025/ws/myapp";

    public static void main(String[] args) {
        ClientEndpointConfig cec = ClientEndpointConfig.Builder.create().build();
        ClientManager client = ClientManager.createClient();

        try {
            client.connectToServer(new ClientEndpoint(), cec, new URI(DEF_WS_URL));
        } catch (Exception e) {
            e.printStackTrace();
        }

        System.out.println("Done.");
    }

    private static class ClientEndpoint extends Endpoint {
        @Override
        public void onOpen(Session session, EndpointConfig config) {
            System.out.println("ClientEndpoint: server session opened: "+session);

            session.addMessageHandler(new MessageHandler.Whole<String>() {
                @Override
                public void onMessage(String message) {
                    System.out.println("ClientEndpoint: received message: "+message);
                }
            });

            try {
                session.getBasicRemote().sendText("Hello server!");
            }
            catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

И, наконец, конечная точка сервера, не являющаяся внутренним классом, которая работает, когда сервер ее использует:

package tyrus.example;

import javax.websocket.CloseReason;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;

@ServerEndpoint("/myapp")
public class SimpleServerEndpoint {
    @OnOpen
    public void onOpen(Session session) {
        System.out.println("Connection received for "+session.getRequestURI());
    }

    @OnMessage
    public void onMessage(String message, Session session) {
        System.out.println("Message received: "+message);
    }

    @OnClose
    public void onClose(Session session, CloseReason closeReason) {
        System.out.println("Session closed, reason: "+closeReason);
    }
}

person Bill Balloni    schedule 09.03.2015    source источник


Ответы (3)


На сервере конечная точка может быть внутренним классом, но она также должна быть статической. Несмотря на то, что код работает и работает, по какой-то причине я могу заставить его работать только в том случае, если конечная точка сервера также является статической. То есть: Изменить, чтобы добавить статический код в серверный код:

@ServerEndpoint("/myapp") public static class InnerSimpleServerEndpoint { }

Вы также можете добавить

@OnError public void onError(Session session, Throwable throwable) { //... }

person jazeee    schedule 09.04.2016
comment
Спасибо, у меня такая же проблема. класс должен быть как публичным, так и статическим. - person Samuel; 29.08.2017

Конечная точка может быть внутренним классом, НО она должна быть инстанцируемой — класс ClientEndpoint в вашем неудачном примере равен private, поэтому Tyrus не может создать его экземпляр.

Измените его на общедоступный, и он должен работать как положено.

person Pavel Bucek    schedule 09.03.2015
comment
Павел проблема с сервером, а не с клиентом. Поскольку мой клиентский код создает экземпляр конечной точки, клиент работает нормально. Но сервер, как показано выше, использует конечную точку сервера внутреннего класса InnerSimpleServerEndpoint , которая полностью общедоступна, включая конструктор, и это не удается. Однако при использовании автономного класса SimpleServerEndpoint (также показанного выше) все работает. Что такого в конечной точке сервера внутреннего класса, которая вызывает сбой? Я что-то не так делаю или это баг Тайруса? - person Bill Balloni; 09.03.2015
comment
Ах, хорошо, я был слишком быстр, чтобы оценить этот вопрос. В любом случае, в этом случае Tyrus ничего не делает — сканирование пакетов делегируется контейнеру сервлетов — см. аннотацию @HandlesTypes javadoc. Я не вижу никаких исключений внутренних классов, так что это может быть проблемой в реализации сервлета (или я просто неправильно читаю спецификацию). В любом случае, вы можете обойти это, используя класс ServerApplicationConfig из javax.websocket.server API. - person Pavel Bucek; 09.03.2015
comment
Спасибо Павел. Есть ли пример кода для использования ServerApplicationConfig в отдельном приложении? Я играл с этим и получаю исключение нулевого указателя (TyrusServerConfiguration.java:191, версия 1.10 tyrus). Если у вас есть ссылка на что-либо, пожалуйста, дайте ее. - person Bill Balloni; 09.03.2015
comment
конечно, например здесь: github.com/tyrus-project/tyrus/blob/ - person Pavel Bucek; 10.03.2015

Внутренний класс должен быть статическим, иначе он не может быть создан без экземпляра внешнего класса. Посмотрите на это: https://www.javatpoint.com/why-we-use-static-class-in-java Итак, если класс, аннотированный с помощью @ServerEndpoint, не является статическим, он не может быть создан.

person omid    schedule 22.07.2021