Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring integration: difficulty with transaction between 2 activators

I have this use case.

First chain:

<int:chain input-channel="inserimentoCanaleActivate" output-channel="inserimentoCanalePreRouting">      
    <int:service-activator ref="inserimentoCanaleActivator" method="activate" />                
</int:chain>

This is the relative code:

@Override
@Transactional(propagation = Propagation.REQUIRES_NEW)
public EventMessage<ModificaOperativitaRapporto> activate(EventMessage<InserimentoCanale> eventMessage) {
    ...
    // some Database changes
    dao.save(myObject);
}

All is working great.

Then I have another chain:

<int:chain id="onlineCensimentoClienteChain" input-channel="ONLINE_CENSIMENTO_CLIENTE" output-channel="inserimentoCanaleActivate">
    <int:service-activator ref="onlineCensimentoClienteActivator" method="activate" />
    <int:splitter expression="payload.getPayload().getCanali()" />
</int:chain>

And the relative activator:

@Override
public EventMessage<CensimentoCliente> activate(EventMessage<CensimentoCliente> eventMessage) {
    ...
    // some Database changes
    dao.save(myObject);
}

The CensimentoCliente payload as described below has a List of payload of the first chain, so with a splitter I split on the list and reuse the code of the first chain.

public interface CensimentoCliente extends Serializable {

    Collection<? extends InserimentoCanale> getCanali();

    void setCanali(Collection<? extends InserimentoCanale> canali);
    ...
}

But since every activator gets his transaction definition (since the first one can live without the second one) I have a use case where the transactions are separated.

The goal is to have the db modifies of the two chains been part of the same transaction.

Any help?

Kind regards Massimo

like image 454
Massimo Ugues Avatar asked Sep 09 '11 13:09

Massimo Ugues


2 Answers

You can accomplish this by creating a custom channel (or other custom component, but this is the simplest approach) that wraps the message dispatch in a TransactionTemplate callback execution:

public class TransactionalChannel extends AbstractSubscribableChannel {

    private final MessageDispatcher dispatcher = new UnicastingDispatcher();
    private final TransactionTemplate transactionTemplate;

    TransactionalChannel(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }

    @Override
    protected boolean doSend(final Message<?> message, long timeout) {
        return transactionTemplate.execute(new TransactionCallback<Boolean>() {
            @Override
            public Boolean doInTransaction(TransactionStatus status) {
                return getDispatcher().dispatch(message);
            }
        });
    }

    @Override
    protected MessageDispatcher getDispatcher() {
        return dispatcher;
    }

}

In your XML, you can define your channel and transaction template and reference your custom channel just as you would any other channel:

    <bean id="transactionalChannel" class="com.stackoverflow.TransactionalChannel">
        <constructor-arg>
           <bean class="org.springframework.transaction.support.TransactionTemplate">
              <property name="transactionManager" ref="transactionManager"/>
              <property name="propagationBehavior" value="#{T(org.springframework.transaction.TransactionDefinition).PROPAGATION_REQUIRES_NEW}"/>
          </bean>
       </constructor-arg>
    </bean>

For your example, you could perhaps use a bridge to pass the message through the new channel:

<int:bridge input-channel="inserimentoCanaleActivate" output-channel="transactionalChannel" /> 
<int:chain input-channel="transactionalChannel" output-channel="inserimentoCanalePreRouting">      
    <int:service-activator ref="inserimentoCanaleActivator" method="activate" />                
</int:chain>
like image 80
rees Avatar answered Nov 04 '22 15:11

rees


You you have <service-activator> and @Transactional on service method, the transaction will be bounded only to that method invocation. If you want to have a transction for entire message flow (or its part) you should declare TX advice somewhere before. If your channels are direct all service invocations will be wrapped with the same transaction. The most simple way to accomplish your wishes, write simple @Gateway interface with @Transactional and call it from the start of your message flow.

To clarify a bit regarding transactions Understanding Transactions in Message flows

like image 22
Artem Bilan Avatar answered Nov 04 '22 14:11

Artem Bilan