【问题标题】:Is it possible to @Observes in SessionScoped CDI bean a event generated by asynchronous stateless EJB?是否可以在 SessionScoped CDI bean 中 @Observes 由异步无状态 EJB 生成的事件?
【发布时间】:2014-05-21 01:03:22
【问题描述】:

在完成长时间的用户上传处理后,我需要接收一个事件。我需要为用户会话设置返回值,以便我可以正确地显示它。我正在尝试做一些 CDI 魔术,如下所述:http://piotrnowicki.com/2013/05/asynchronous-cdi-events/#toc-solution-3-ejb-producer-and-cdi-consumer (Async EJB Producer -> CDI Consumer)。

第一次尝试

来电者和接收者:

@SessionScoped
public class UploadController implements Serializable {
    private static final long serialVersionUID = 1L;

    private final Logger logger = Logger.getLogger(this.getClass());

    @EJB
    private StatelessBeanLocal statelessBean;

    @Inject
    private SomeSessionCdi cdi;

    private Future<UploadedDTO> futureDto;

    public void observeUpload(@Observes(during = TransactionPhase.AFTER_COMPLETION) UploadFinishedEvent event) {
        logger.trace("Recived CDI event: %s", event);
        // FIXME: without @SessionScoped i'm reaching here, but futureDto == null
        cdi.setStateAfterUpload(dto);
    }

    public void upload(ByteArrayOutputStream baos, User user, UploadedFileInfo info) {
        logger.trace("Initiating an async upload processing of %s", info);
        futureDto = statelessBean.upload(baos, user);
        logger.trace("Initialized an async upload processing of %s", info);
    }

}

处理器:

@Stateless
public class StatelessBean implements StatelessBeanLocal {

    @Inject
    private transient Event<UploadFinishedEvent> eventTrigger;

    @Asynchronous
    public Future<UploadedDTO> upload(ByteArrayOutputStream baos, User user) {
        UploadedEntity entity = Unpacker.unpackBaos(baos);
        logger.trace("Uploaded file for: %s", entity);
        // [..] Long processing here
        // Invoking Thread.sleep(5000); to simulate a long process
        Thread.sleep(5000);

        UploadedDTO dto = null; // here a real value, of course
        logger.debug("Finished processing of upload for: %s", entity);
        eventTrigger.fire(new UploadFinishedEventImpl());
        logger.trace("After firing a CDI event for finished upload for: %s", entity);
        return new AsyncResult<>(dto);
    }

    // [..]
}

如果 CDI bean 有 @SessionScoped 注释,那么我得到:SEVERE: WELD-000401 Failure while notifying an observer of event org.example.StatelessBean$UploadFinishedEventImpl@54c03555

如果 CDI bean 没有 @SessionScoped 注释,那么我得到了 futureDto == null

第二次尝试

将 EJB bean 更改为更改日志但仍然无效:

更改了无状态 EJB:

@Stateless
public class StatelessBean implements StatelessBeanLocal {

    @Inject
    private transient Event<UploadFinishedEvent> eventTrigger;

    @Resource
    private SessionContext sctx;

    @Asynchronous
    public Future<UploadedDTO> upload(ByteArrayOutputStream baos, User user) {
        UploadedEntity entity = Unpacker.unpackBaos(baos);
        logger.trace("Uploaded file for: %s", entity);
        // [..] Long processing here
        // Invoking Thread.sleep(5000); to simulate a long process
        Thread.sleep(5000);

        UploadedDTO dto = null; // here a real value, of course
        logger.debug("Finished processing of upload for: %s", entity);
        sctx.getBusinessObject(TemplateSetBeanLocal.class).fireUploadFinished();
        logger.trace("After firing a CDI event for finished upload for: %s", entity);
        return new AsyncResult<>(dto);
    }

    @Asynchronous
    public void fireUploadFinished() {
        logger.debug("Before firing an UploadFinishedEvent: %s", sctx.getCallerPrincipal().toString());
        eventTrigger.fire(new UploadFinishedEventImpl());
        logger.debug("After firing an UploadFinishedEvent: %s", sctx.getCallerPrincipal().toString());
    }

    // [..]
}

日志:

FINE:完成上传处理:[sample-0.1.0-SNAPSHOT.zip,application/zip,75,80KB,3cgvet] 最好:触发 CDI 事件以完成上传后:[sample-0.1.0-SNAPSHOT.zip,application/zip,75,80KB,3cgvet] FINE:在触发 UploadFinishedEvent 之前:admin FINE:触发 UploadFinishedEvent 后:admin 严重:没有用于注入 org.example.UploadController 的有效 EE 环境 最好:收到 CDI 事件:org.example.StatelessBean$UploadFinishedEventImpl@72514572

有可能吗?

【问题讨论】:

    标签: jakarta-ee asynchronous cdi ejb-3.1 weld


    【解决方案1】:

    是的,这是可能的。 没有@SessionScoped,bean 会再次创建,因此futureDTO 为空。 使用@SessionScoped @AfterCompletion,您将在事务提交后进行处理。您得到的焊接错误可能是由于某些事务行为。虽然不在示例中,但您可能正在调用另一个 EJB 方法来处理 observeUpload() 中的 uploadDTO,导致 committed 事务 (@AfterCompletion) 被挂起和恢复,这是不可能的。解决方案是使用有效事务进入观察者方法,或者在该方法中不进行任何事务处理。有时,具有适当事务上下文的额外层/方法会有所帮助。

    【讨论】:

    • 是的,我在observeUpload() 中进行处理,但仅在 CDI 中进行。我实现了 CDI 会话视图。我不会在整个堆栈中调用任何属于 observerUpload() 的 bean。
    【解决方案2】:

    不妨试试这样的:

    @SessionScoped
    public class UploadController implements Serializable {
        private static final long serialVersionUID = 1L;
    
        private final Logger logger = Logger.getLogger(this.getClass());
    
        @EJB
        private StatelessBeanLocal statelessBean;
    
        private Future<UploadedDTO> futureDto;
    
        public void uploadEnded(UploadedDTO dto) {
            logger.trace("Uploaded " + dto);
        }
    
        public void upload(ByteArrayOutputStream baos, User user, UploadedFileInfo info) {
            logger.trace("Initiating an async upload processing of %s", info);
            futureDto = statelessBean.upload(baos, user, this);
            logger.trace("Initialized an async upload processing of %s", info);
        }
    
    }
    

    EJB:

    @Stateless
    public class StatelessBean implements StatelessBeanLocal {
    
        @Asynchronous
        public Future<UploadedDTO> upload(ByteArrayOutputStream baos, User user, UploadController controller) {
            UploadedEntity entity = Unpacker.unpackBaos(baos);
            logger.trace("Uploaded file for: %s", entity);
            // [..] Long processing here
            // Invoking Thread.sleep(5000); to simulate a long process
            Thread.sleep(5000);
    
            UploadedDTO dto = null; // here a real value, of course
            logger.debug("Finished processing of upload for: %s", entity);
            controller.uploadEnded(dto);
            logger.trace("After firing a CDI event for finished upload for: %s", entity);
            return new AsyncResult(dto);
        }
    
        // [..]
    }
    

    【讨论】:

    • 仅适用于UploadController。如果UploadController 有其他@Inject 字段,则会话在那里丢失。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多