Как получить существующий экземпляр websocket

Я работаю над приложением, использующим Websockets (Java EE 7) для асинхронного посылки сообщений всем подключенным клиентам. Сервер (конечная точка Websocket) должен отправлять эти сообщения всякий раз, когда создается новая статья (модальность взаимодействия в моем приложении).

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

Но проблема у меня была, когда я получаю доступ к этой созданной конечной точке websocket, к которой все клиенты подключены извне (любой другой бизнес-класс), я получаю существующий экземпляр (например, singleton).

Итак, можете ли вы предложить мне способ получить существующий экземпляр конечной точки websocket, так как я не могу создать его как новый MyWebsocketEndPoint(), потому что он будет создан внутренним механизмом websocket всякий раз, когда запрос от клиент получает.

Для ссылки:

private static WebSocketEndPoint INSTANCE = null;

public static WebSocketEndPoint getInstance() {
if(INSTANCE == null) {
// Instead of creating a new instance, I need an existing one
    INSTANCE = new WebSocketEndPoint ();
}
        return INSTANCE;
}

Спасибо заранее.

Ответ 1

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

Класс javax.websocket.Session имеет метод getBasicRemote для извлечения экземпляра RemoteEndpoint.Basic, который представляет конечную точку, связанную с этим сеансом.

Вы можете получить все открытые сеансы, вызвав Session.getOpenSessions(), а затем повторите их. Цикл отправит каждому клиентскому соединению сообщение. Вот простой пример:

@ServerEndpoint("/myendpoint")
public class MyEndpoint {
  @OnMessage
  public void onMessage(Session session, String message) {
    try {  
      for (Session s : session.getOpenSessions()) {
        if (s.isOpen()) {
          s.getBasicRemote().sendText(message);
        }
    } catch (IOException ex) { ... }
  } 
} 

Но в вашем случае вы, вероятно, хотите использовать события CDI для запуска обновления для всех клиентов. В этом случае вы создадите событие CDI, которое наблюдает метод в классе конечных точек Websocket:

@ServerEndpoint("/myendpoint")
public class MyEndpoint {
  // EJB that fires an event when a new article appears
  @EJB
  ArticleBean articleBean;
  // a collection containing all the sessions
  private static final Set<Session> sessions = 
          Collections.synchronizedSet(new HashSet<Session>());

  @OnOpen
  public void onOpen(final Session session) {
    // add the new session to the set
    sessions.add(session);
    ...
  }

  @OnClose
  public void onClose(final Session session) {
    // remove the session from the set
    sessions.remove(session);
  }

  public void broadcastArticle(@Observes @NewArticleEvent ArticleEvent articleEvent) {
    synchronized(sessions) {
      for (Session s : sessions) {
        if (s.isOpen()) {
          try {
            // send the article summary to all the connected clients
            s.getBasicRemote().sendText("New article up:" + articleEvent.getArticle().getSummary());
          } catch (IOException ex) { ... }
        }
      }
    }
  }
}

EJB в приведенном выше примере будет делать что-то вроде:

...
@Inject
Event<ArticleEvent> newArticleEvent;

public void publishArticle(Article article) {
  ...
  newArticleEvent.fire(new ArticleEvent(article));
  ...
}

См. главы 7 учебного курса Java EE 7 на WebSockets и CDI События.

Изменить: Изменен метод @Observer для использования события в качестве параметра.

Редактирование 2: завершение цикла в широковещательной статье в синхронизации, на @gcvt.

Изменить 3: Обновлены ссылки на Java EE 7 Tutorial. Хорошая работа, Oracle. Sheesh.

Ответ 2

Фактически, API WebSocket предоставляет способ управления контентом конечной точки. См. https://tyrus.java.net/apidocs/1.2.1/javax/websocket/server/ServerEndpointConfig.Configurator.html

простой пример (взятый из тест Tyrus - WebSocket RI):

    public static class MyServerConfigurator extends ServerEndpointConfig.Configurator {

        public static final MyEndpointAnnotated testEndpoint1 = new MyEndpointAnnotated();
        public static final MyEndpointProgrammatic testEndpoint2 = new MyEndpointProgrammatic();

        @Override
        public <T> T getEndpointInstance(Class<T> endpointClass) throws InstantiationException {
            if (endpointClass.equals(MyEndpointAnnotated.class)) {
                return (T) testEndpoint1;
            } else if (endpointClass.equals(MyEndpointProgrammatic.class)) {
                return (T) testEndpoint2;
            }

            throw new InstantiationException();
        }
    }

Вам необходимо зарегистрировать это в конечной точке:

@ServerEndpoint(value = "/echoAnnotated", configurator = MyServerConfigurator.class)
public static class MyEndpointAnnotated {

    @OnMessage
    public String onMessage(String message) {

        assertEquals(MyServerConfigurator.testEndpoint1, this);

        return message;
    }
}

и вы также можете использовать его с программными конечными точками:

public static class MyApplication implements ServerApplicationConfig {
    @Override
    public Set<ServerEndpointConfig> getEndpointConfigs(Set<Class<? extends Endpoint>> endpointClasses) {
        return new HashSet<ServerEndpointConfig>
          (Arrays.asList(ServerEndpointConfig.Builder
            .create(MyEndpointProgrammatic.class, "/echoProgrammatic")
            .configurator(new MyServerConfigurator())
            .build()));
    }

    @Override
    public Set<Class<?>> getAnnotatedEndpointClasses(Set<Class<?>> scanned) {
        return new HashSet<Class<?>>(Arrays.asList(MyEndpointAnnotated.class));
    }

Конечно, это зависит от вас, если у вас будет один конфигуратор, используемый для всех конечных точек (уродливый ifs, как в представленном фрагменте), или если вы создадите отдельный конфигуратор для каждой конечной точки.

Пожалуйста, не копируйте представленный код так, как есть - это только часть тестов Tyrus, и это нарушает некоторые основные парадигмы OOM.

См. https://github.com/tyrus-project/tyrus/blob/1.2.1/tests/e2e/src/test/java/org/glassfish/tyrus/test/e2e/GetEndpointInstanceTest.java для полного тестирования.