Работает ли транзакционный @oborters для событий, инициируемых в JBoss AS 7? - PullRequest
0 голосов
/ 03 октября 2011

Чтобы использовать события, прослушиваемые только в случае успешной или неудачной транзакции, я следую данному документу о наблюдателях транзакций: http://docs.jboss.org/weld/reference/1.1.0.Final/en-US/html_single/#d0e4075

... но не могу заставить мой код работать на JBossAS7.

Вот мой 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
    }
}

Я могу сказать, что я нахожусь в транзакции, когда происходит событиепотому что вызывается метод afterSave EJB.Увы, методы listenMyEvent и listenMyEvent2 всегда вызываются обоими, как если бы я не находился в транзакционном контексте.

Я попробовал один и тот же код на GlassFish 3, и он отлично работает, так что я думаю,проблема с JBoss AS 7, но я не могу найти никакого сообщения об ошибке.

Ответы [ 3 ]

0 голосов
/ 17 февраля 2012

Это работает с версией 7.1.0.Final, которая предположительно (-> с Jboss вы никогда не знаете) полностью совместима с Java EE. Также ваш bean-компонент не является потокобезопасным, поскольку он использует список вместо параллельной очереди.

0 голосов
/ 02 июля 2012

Ваши методы-наблюдатели требуют REQUIRES_NEW, как указано здесь:

http://www.seamframework.org/Documentation/WhyIsThereNoActiveTransactionInMySFSBTransactionalObserver

0 голосов
/ 11 октября 2011

Ну, так как мои текущие тесты заставили меня подумать, что в JBoss AS 7 не работают наблюдатели транзакций, мне удалось сделать обходной путь, который я дал здесь для людей, которые заинтересованы.

Во-первых, нам нужны аннотации классификатора: Immediate, AfterFailure и 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
{}

Также три основных 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.
  • В этом обходном пути наблюдатели, находящиеся на этапах после успеха или после сбоя, всегда будут вызываться в конце метода, и только в случае успеха или неудачи.
...