是否可以在SessionScoped CDI bean中@Observes由异步无状态EJB生成的事件?

时间:2014-05-21 01:03:23

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

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

首次尝试

来电者& Reciver:

@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: Finished processing of upload for: [sample-0.1.0-SNAPSHOT.zip,application/zip,75,80KB,3cgvet]
FINEST: After firing a CDI event for finished upload for: [sample-0.1.0-SNAPSHOT.zip,application/zip,75,80KB,3cgvet]
FINE: Before firing an UploadFinishedEvent: admin
FINE: After firing an UploadFinishedEvent: admin
SEVERE: No valid EE environment for injection of org.example.UploadController
FINEST: Recived CDI event: org.example.StatelessBean$UploadFinishedEventImpl@72514572

有可能吗?

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:0)

Mayby尝试这样的事情:

@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);
    }

    // [..]
}