Bezdepbonusu7Программирование: статьи и ответы

Вопрос

Передайте данные области запроса асинхронным методам в CDI

Автор вопроса: Iaroslav SavytskyiОтветов: 1

Приложение Java EE 7 работает на Wildfly 9.0.2.Final. Существует проблема с доступом к данным области запроса из методов @Asynchronous.

В веб-фильтре данные (например, токен) устанавливаются в bean-компонент RequestScoped CDI. Позже мы хотим получить доступ к этим данным. Все отлично работает, если мы работаем в одном потоке. Но если есть необходимость запускать код асинхронно, возникает проблема. CDI вводит пустой bean-компонент, и данные запроса теряются.

Вот пример:

@RequestScoped
public class CurrentUserService implements Serializable {
  public String token;
}

@Stateless
public class Service {
   @Inject
   private RestClient client;

    @Resource
    private ManagedExecutorService executorService;

    @Resource
    private ContextService contextService;

    @Asynchronous
    private <T> Future<T> getFuture(Supplier<T> supplier) {
        Callable<T> task = supplier::get;
        Callable<T> callable = contextService.createContextualProxy(task, Callable.class);
        return executorService.submit(callable);
    }

   public String getToken() throws Exception {
      return getFuture(client::getToken).get();
   }
}

@ApplicationScoped
public class RestClient {
    @Inject
    private CurrentUserService currentUserBean;

    public String getToken() {
        return currentUserBean.token;
    }
}

В данном примере мы хотим получить доступ к токену текущего пользователя (CurrentUserService#token) из асинхронного метода Service.getToken. В результате получим null.

Ожидается, что данные "области запроса" должны быть доступны из задач, выполняемых в области запроса. Что-то вроде InheritableThreadLocal следует использовать, чтобы разрешить доступ к исходным данным потока из новых потоков.

Это ошибка? Может я что-то не так делаю? Если да, то как правильно распространять такие данные в асинхронные вызовы?

Заранее спасибо.

  • Кроме того, у вас есть bean-компонент RequestScoped внутри ApplicationScoped. Вместо этого вы можете использовать @Inject Instance<CurrentUserService> currentUserBean. jpkrohling 22.12.2015
  • @Гимби, ты прав. Вполне возможно, что новые темы будут жить дольше, чем исходные. Как видите, я обернул вызов прокси-сервером ContextService. Я ожидал, что прокси предоставит необходимую контекстную информацию. В любом случае, как решить проблему? Мне нужно получить доступ к данным запроса из нового потока. Можно как-то пройти? Iaroslav Savytskyi 22.12.2015
  • @jpkrohling, спасибо. Я пробовал, но безуспешно :( Iaroslav Savytskyi 22.12.2015
  • Ваш метод Service.getToken() принципиально синхронен. Он вызывает get для будущего, которое будет блокироваться до тех пор, пока будущее не будет выполнено. Так что весь этот асинхронный механизм, который у вас есть, все равно ничего не сделает. Steve C 23.12.2015
  • @ Стив С, это только пример. Iaroslav Savytskyi 23.12.2015

javaasynchronouswildflycdijava-ee-7

Ответы

Ответ 1 · рейтинг 4 · Steve C

Согласно §2.3.2.1 спецификации Java EE Concurrency Utilities не следует пытаться сделать это:

  • Задачи, отправленные управляемому экземпляру ExecutorService, могут продолжать выполняться после завершения жизненного цикла отправляющего компонента. Поэтому компоненты CDI с областью действия @RequestScoped, @SessionScoped или @ConversationScoped не рекомендуется использовать в качестве задач, поскольку нельзя гарантировать, что задачи будут завершены до того, как контекст CDI будет уничтожен.

Вам необходимо собрать данные области запроса и передать их асинхронной задаче при ее создании, независимо от того, используете ли вы утилиты параллелизма или методы @Asynchronous.

Перевод вопроса и ответов сообщества Stack Overflow, лицензия CC BY-SA. Оригинал на Stack Overflow.

Похожие материалы