Статьи

Интеграция CDI и WebSockets

Мысль о том, чтобы поэкспериментировать с простым прототипом приложения Java EE 7, включающим JAX-RS (REST), WebSockets и CDI.

Примечание : не хочу, чтобы это было спойлером — но этот пост в основном рассказывает о проблеме, с которой я столкнулся при попытке использовать веб-сокеты и REST, используя CDI в качестве «клея» (в приложении Java EE). Интеграция не состоялась, но тем не менее, некоторые уроки были извлечены 🙂

Идея заключалась в том, чтобы использовать конечную точку REST в качестве «канала» для конечной точки веб-сокета, который, в свою очередь, «передавал» данные всем подключенным клиентам:

  • Конечная точка JAX-RS, которая получает данные (возможно, в режиме реального времени) из других источников в качестве входа в конечную точку веб-сокета
  • Используйте CDI Events в качестве клея ч / б JAX-RS и конечных точек WebSocket и «запустите» полезную нагрузку
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    @Path("/feed")
    public class RESTFeed {
     
        @Inject
        Event<String> event;
     
        @POST
        @Consumes(MediaType.TEXT_PLAIN)
        public void push(String msg) {
            event.fire(msg);
        }
    }
  • Используйте метод CDI Observer в реализации конечной точки WebSocket для передачи данных на подключенные клиенты:
    01
    02
    03
    04
    05
    06
    07
    08
    09
    10
    11
    12
    public void onMsg(@Observes String msg) {
     
            //different WS enpoint instance - notice the hash code value in the server log
            System.out.println("WS End point class ID -- " + this.hashCode());
            try {
     
                client.getBasicRemote().sendText(msg);
     
            } catch (IOException ex) {
                Logger.getLogger(ServerEndpoint.class.getName()).log(Level.SEVERE, null, ex);
            }
    }

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

Но возможно ли это вообще?

Вот шаги, которые я выполнил

Начало

  • Запустил HTTP-запрос POST в конечной точке REST, используя Postman

противопожарная отдых

Boom! Исключение NullPointerException в методе Observer — я ждал несколько секунд, а потом меня поразила реальность!

NPE

Коренная причина (из того, что я понимаю)

  • Поведение конечных точек WebSocket

Конечные точки WebSocket аналогичны классам ресурсов JAX-RS в том смысле, что для каждого подключенного клиента существует один экземпляр класса конечной точки веб-сокета (по крайней мере, по умолчанию). Это четко указано в спецификации WebSocket . Как только клиент (одноранговый) соединяется, создается уникальный экземпляр, и можно безопасно кэшировать объект Session веб-сокета (представление однорангового узла) как переменную экземпляра. ИМО, это простая и чистая модель программирования

WS-спецификации

  • Но у контейнера CDI были другие планы!

Как только конечная точка REST запускает событие CDI (в ответ на запрос POST), контейнер CDI создает другой экземпляр конечной точки WebSocket (в данном случае — Обозреватель CDI). Почему? Потому что CDI-бины имеют контекстный характер . Приложение не контролирует экземпляры компонентов CDI. Он просто использует их (через @Inject). Задача контейнера заключается в создании и уничтожении экземпляров bean-компонентов и обеспечении доступности соответствующего экземпляра для bean-компонентов, выполняющихся в том же контексте. Как контейнер определяет контекст? Это через Области — Приложение, Сессия, Запрос и т. Д.

(опять же, четко указано в спецификации CDI )

КДИ-спецификации

Итак, суть в том, что экземпляра текущего контекста конечной точки WebSocket НЕТ, поэтому CDI создает новый экземпляр для доставки сообщения. Это, конечно, означает, что переменная экземпляра будет указывать на ноль и, следовательно, NPE (Дух!)

Так что вопрос в том. , ,

Какую область CDI следует использовать для конечной точки WebSocket ??? Я попытался @ApplicationScoped, @SessionScoped и @RequestScoped без особой удачи — все еще новый экземпляр и NPE

Любые другие варианты?

  • Определение Set of Session в качестве статической переменной сделает свое дело:
    1
    private static Set<Session> peers = Collections.synchronizedSet(new HashSet());

Но это IMO — просто взлом и неосуществимо в случае, если нужно обработать специфическое состояние клиента (которое может быть обработано только как переменные экземпляра) в методе наблюдателя — он обязательно останется неинициализированным

  • Сервер отправил события ? Но в конце дня SSE! = WebSocket. В случае, если вариант использования требует «только» на стороне сервера, можно выбрать его. SSE еще не является стандартом Java EE — Java EE 8 может сделать это возможным

Решение ?

Я не эксперт, но я думаю, что это зависит от спецификации WebSocket, чтобы дать больше ясности о том, как использовать его с CDI. Учитывая, что CDI является неотъемлемой частью спецификации Java EE, чрезвычайно важно, чтобы он легко интегрировался с другими спецификациями — особенно с HTML5-ориентированными спецификациями, такими как JAX-RS, WebSocket и т. Д.

Эта статья Бруно Борхеса содержит ссылки на похожие вопросы, связанные с JMS, CDI и WebSocket, а также с тем, как они интегрируются друг с другом.

Я что-то упустил очевидное? Есть ли у вас какие-либо входы / решения? Пожалуйста, не стесняйтесь вмешиваться! 🙂

Пример кода доступен на GitHub (на случай, если вы захотите взглянуть). Я пробовал это на GlassFish 4.1 и Wildfly 8.2.0

Это все на данный момент, я думаю … 🙂

Ура!

Ссылка: Интеграция CDI и WebSockets от нашего партнера JCG Абхишека Гупты в блоге Object Oriented ..