事务性 @observes 是否适用于 JBoss AS 7 上的触发事件?

发布于 2024-12-07 16:41:42 字数 1293 浏览 1 评论 0原文

为了使用仅在事务成功或失败时侦听的事件,我遵循有关事务观察者的给定文档: http://docs.jboss.org/ weld/reference/1.1.0.Final/en-US/html_single/#d0e4075

...但无法使我的代码在 JBoss AS7 上运行。

这是我的 EJB:

@LocalBean
@Stateful
@TransactionAttribute(TransactionAttributeType.NEVER)
public class MyController
{
    @Inject
    private transient Event<MyEvent> myEventLauncher;

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void save()
    {
        myEventLauncher.fire(new MyEvent());
    }

    @AfterCompletion
    protected void afterSave(boolean isCommitted)
    {
        // do stuff
    }
}

这是我的基本侦听器:

public class MyHandler
{
    protected void listenMyEvent(@Observes(during=TransactionPhase.AFTER_SUCCESS) MyEvent event)
    {
        // do stuff
    }

    protected void listenMyEvent2(@Observes(during=TransactionPhase.AFTER_FAILURE) MyEvent event)
    {
        // do stuff
    }
}

当事件被触发时,我可以说我处于事务中,因为调用了 EJB 的 afterSave 方法。唉,方法 listenMyEventlistenMyEvent2 总是被同时调用,就像我不在事务上下文中一样。

我在 GlassFish 3 上尝试了相同的代码,它完美地工作,所以我猜测 JBoss AS 7 有问题,但我找不到任何关于它的错误报告。

In order to use events only listened if a transaction succeeds or fails, I'm following the given doc about transactional observers :
http://docs.jboss.org/weld/reference/1.1.0.Final/en-US/html_single/#d0e4075

... but cannot manage to make my code work on JBoss AS7.

Here's my EJB:

@LocalBean
@Stateful
@TransactionAttribute(TransactionAttributeType.NEVER)
public class MyController
{
    @Inject
    private transient Event<MyEvent> myEventLauncher;

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void save()
    {
        myEventLauncher.fire(new MyEvent());
    }

    @AfterCompletion
    protected void afterSave(boolean isCommitted)
    {
        // do stuff
    }
}

And here my basic listener:

public class MyHandler
{
    protected void listenMyEvent(@Observes(during=TransactionPhase.AFTER_SUCCESS) MyEvent event)
    {
        // do stuff
    }

    protected void listenMyEvent2(@Observes(during=TransactionPhase.AFTER_FAILURE) MyEvent event)
    {
        // do stuff
    }
}

I can say I'm in a transaction when the event is fired, because the afterSave method of the EJB is called. Alas, the methods listenMyEvent and listenMyEvent2 are always called both, like if I was not in a transactional context.

I tried the same code on GlassFish 3 and it perfectly works, so I guess there is a problem with JBoss AS 7, but I cannot find any bug report about it.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

夏天碎花小短裙 2024-12-14 16:41:42

好吧,由于我当前的测试让我认为事务观察者在 JBoss AS 7 中不起作用,所以我设法为感兴趣的人提供了一个解决方法。

首先,我们需要限定符注释:ImmediateAfterFailureAfterSuccess

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface AfterFailure
{}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface AfterSuccess
{}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface Immediate
{}

此外,还可以在运行时创建这三个注释的三个基本 AnnotationLiteral 实例。

然后,我们需要一个用于真实事件的封装器,我将其命名为 SpecialEvent

public class SpecialEvent
{
    private Object event; // the real event you want

    public SpecialEvent(Object event)
    {
        super();
        this.event = event;
    }

    public Object getEvent()
    {
        return event;
    }
}

最后,这个特殊事件的观察者和您想要触发此类事件的类的拦截器(下面有完整的解释)。

@RequestScoped
public class SpecialEventObserver
{
    @Inject
    private Event<Object> anyEventFirer; // firer for real events
    private List<Object> events; // queued events

    public SpecialEventObserver()
    {
        events = new ArrayList<Object>();
    }

    // remove all queued events
    public void reset()
    {
        this.events.clear();
    }

    public void fireAfterFailureEvents() throws Exception
    {
        this.fireAllEventsOnce(new AfterFailureLiteral());
    }

    public void fireAfterSuccessEvents() throws Exception
    {
        this.fireAllEventsOnce(new AfterSuccessLiteral());
    }

    protected void listenSpecialEvent(@Observes SpecialEvent specialEvent)
    {
        Object event = specialEvent.getEvent();
        this.events.add(event);
        this.fireEvent(event, new ImmediateLiteral());
    }

    protected void fireAllEventsOnce(Annotation qualifier) throws Exception
    {
        try
        {
            for (Object event : this.events)
            {
                this.fireEvent(event, qualifier);
            }
        }
        catch (Exception e)
        {
            throw e;
        }
        finally
        {
            this.events.clear();
        }
    }

    protected void fireEvent(Object event, Annotation qualifier)
    {
        Event eventFirer = anyEventFirer.select(event.getClass(), qualifier);
        eventFirer.fire(event);
    }
}

@Interceptor
@LocalInterception
public class MyInterceptor implements Serializable
{   
    @Inject
    private SpecialEventObserver specialEventObserver;

    @AroundInvoke
    public Object intercept(InvocationContext ic) throws Exception
    {   
        specialEventObserver.reset();
        try
        {
            // call the real method
            Object proceedResult = ic.proceed();

            // real method succeeded, fire successful events
            specialEventObserver.fireAfterSuccessEvents();

            return proceedResult;
        }
        catch (Exception e)
        {
            // real method failed, fire failed events
            specialEventObserver.fireAfterFailureEvents();

            throw e;
        }
    }
}

机制非常简单:

  • 当您想要触发事件时,触发保存真实事件的 SpecialEvent
  • SpecialEventObserver 将捕获任何 SpecialEvent 并立即使用 Immediate 限定符触发您自己的事件。它还会将完成后部分的事件排队。
  • 在您自己的方法调用结束时(拦截器中的ic.proceed),MyInterceptor将要求SpecialEventObserver再次触发所有事件AfterFailure 限定符或 AfterSuccess 限定符,具体取决于您的方法是否成功。
  • 您自己的观察者必须使用正确的限定符来观察事件,而不是 @Observes(during=...),例如 @Observes @Immediate@Observes @AfterFailure@Observes @AfterSuccess

该行为并不完全是提供本机 @Observes(during=...) 的行为。完成后部分不是基于事务状态,而是基于您自己的方法调用成功:

  • 在 JaveEE6 中,如果您不在事务中,则必须立即调用成功后或失败后阶段的事务观察者,例如 IN_PROGRESS 就可以了。
  • 在此解决方法中,成功后或失败后阶段的观察者将始终在方法结束时调用,并且仅在成功或失败时调用。

Well, as my current tests made me think that transactional observers are not working in JBoss AS 7, I managed to do a workaround I gave here for people who are interested.

First, we need qualifier annotations: Immediate, AfterFailure and AfterSuccess.

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface AfterFailure
{}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface AfterSuccess
{}

@Qualifier
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD, ElementType.PARAMETER })
public @interface Immediate
{}

Also, three basic AnnotationLiteral to create in runtime instances of this three annotations.

Then, we need a encapsulator for our true events, that I named SpecialEvent.

public class SpecialEvent
{
    private Object event; // the real event you want

    public SpecialEvent(Object event)
    {
        super();
        this.event = event;
    }

    public Object getEvent()
    {
        return event;
    }
}

And at last, an observer for this special event and an interceptor for classes where you want to fire this kind of events (full explanation below).

@RequestScoped
public class SpecialEventObserver
{
    @Inject
    private Event<Object> anyEventFirer; // firer for real events
    private List<Object> events; // queued events

    public SpecialEventObserver()
    {
        events = new ArrayList<Object>();
    }

    // remove all queued events
    public void reset()
    {
        this.events.clear();
    }

    public void fireAfterFailureEvents() throws Exception
    {
        this.fireAllEventsOnce(new AfterFailureLiteral());
    }

    public void fireAfterSuccessEvents() throws Exception
    {
        this.fireAllEventsOnce(new AfterSuccessLiteral());
    }

    protected void listenSpecialEvent(@Observes SpecialEvent specialEvent)
    {
        Object event = specialEvent.getEvent();
        this.events.add(event);
        this.fireEvent(event, new ImmediateLiteral());
    }

    protected void fireAllEventsOnce(Annotation qualifier) throws Exception
    {
        try
        {
            for (Object event : this.events)
            {
                this.fireEvent(event, qualifier);
            }
        }
        catch (Exception e)
        {
            throw e;
        }
        finally
        {
            this.events.clear();
        }
    }

    protected void fireEvent(Object event, Annotation qualifier)
    {
        Event eventFirer = anyEventFirer.select(event.getClass(), qualifier);
        eventFirer.fire(event);
    }
}

@Interceptor
@LocalInterception
public class MyInterceptor implements Serializable
{   
    @Inject
    private SpecialEventObserver specialEventObserver;

    @AroundInvoke
    public Object intercept(InvocationContext ic) throws Exception
    {   
        specialEventObserver.reset();
        try
        {
            // call the real method
            Object proceedResult = ic.proceed();

            // real method succeeded, fire successful events
            specialEventObserver.fireAfterSuccessEvents();

            return proceedResult;
        }
        catch (Exception e)
        {
            // real method failed, fire failed events
            specialEventObserver.fireAfterFailureEvents();

            throw e;
        }
    }
}

The mechanism is quite simple:

  • When you want to fire an event, fire a SpecialEvent that hold the true event.
  • The SpecialEventObserver will catch any SpecialEvent and will immediately fire your own event with an Immediate qualifier. It will also queue the events for the after completion part.
  • At the end of your own method call (ic.proceed in the interceptor), MyInterceptor will ask the SpecialEventObserver either to fire again all events with a AfterFailure qualifier or a AfterSuccess qualifier, depending of the success of your method.
  • In place of @Observes(during=...), your own observers have to observe events with the right qualifier, like @Observes @Immediate, @Observes @AfterFailure or @Observes @AfterSuccess.

The behavior is not exactly the one that provides the native @Observes(during=...). The after completion part is not based on the transaction state, but on your own method call success:

  • In JaveEE6, transactional observers on after success or after failure phases must be immediately called if you're not in a transaction, like a IN_PROGRESS would do.
  • In this workaround, observers on after success or after failure phases will always be called at the end of the method, and only if it succeeded or failed.
忘你却要生生世世 2024-12-14 16:41:42

这适用于版本 7.1.0.Final,据说(-> 与 Jboss 你永远不知道)完全兼容 Java EE。此外,您的 bean 不是线程安全的,因为它使用列表而不是并发队列。

This works with version 7.1.0.Final which is supposedly (-> with Jboss you never know) fully Java EE compliant. Also your bean is not thread-safe as it uses list instead of a concurrent queue.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文