Фаза транзакции события EE CDI в Джакарте.Порядок событий AFTER_SUCCESS обратный

#events #jakarta-ee #cdi

Вопрос:

При аннотации метода наблюдателя AFTER_SUCCESS события принимаются в обратном порядке , в котором они фактически были запущены во время транзакции.

Пример псевдокода:

 @Transactional
void test(){
    pushEvent.fire( new PushEvent(10) );
    pushEvent.fire( new PushEvent(20) );
    pushEvent.fire( new PushEvent(30) ); 
}
 

Наблюдатель:

 void onPushEvent( @Observes( during = TransactionPhase.AFTER_SUCCESS ) PushEvent event ){
   System.out.println(event.getValue())
}
 

Неожиданный, но наблюдаемый результат:

 30
20
10
 

Можно ли это изменить?

Комментарии:

1. Наблюдатели в фазе AFTER_xxx вызываются несинхронизированно (не асинхронно, т. е. в другом потоке, просто позже). В спецификации ничего не говорится о порядке обработки событий в этом случае, поэтому я предполагаю, что порядок зависит от реализации, и вы не можете полагаться на него! Я бы подумал о том, чтобы поместить человека PushEvents в a List и запустить событие, содержащее этот список, чтобы обеспечить правильный порядок обработки.

2. В качестве альтернативы, например, если события не запускаются из одного места, у вас может быть синхронный наблюдатель, собирающий человека PushEvents в List (например, в @RequestScoped бобе). Это гарантированно будет вызвано в нужном порядке. Затем каким-то образом этот синхронный наблюдатель запускает событие, содержащее список, для обработки на этапе AFTER_SUCCESS.

Ответ №1:

Исправлено это в моем проекте с помощью локального буфера событий потока, который сбрасывается при AFTER_SUCCESS и воспроизводит события в том порядке, в котором они появились в первую очередь

 ThreadLocal<List<PushEvent>> threadEventBufferHolder = ThreadLocal.withInitial( ArrayList::new );

void onPushEvent( @Observes( during = TransactionPhase.IN_PROGRESS ) PushEvent event ){
    threadEventBufferHolder.get().add( event );
}

void onPushEventFailure( @Observes( during = TransactionPhase.AFTER_FAILURE ) PushEvent event ){
    buffer.clear();
}

void onPushEventCommit( @Observes( during = TransactionPhase.AFTER_SUCCESS ) PushEvent event ){
    List<PushEvent> buffer = threadEventBufferHolder.get();
    buffer.forEach( this::doPrintlnValue );
    buffer.clear();
}
 

Несвязанный код удален для удобства чтения