Olivier J.
Olivier J.

Reputation: 3165

Understanding EJB transactions types

I have some behaviour in my application that I can't understand. I have 1 stateful bean, 1 stateless bean and I want to call from SFB 1 method of SLB (parsing of WEB pages). It's OK but SLB calls business method that send JMS message to another application and all messages are received at same time due to transaction type (REQUIRED).

So I've changed transaction type of my sendind method to REQUIRES_NEW but it's the same, all messages are received at same time.

By changing parsing method to REQUIRES_NEW, I receive messages asynchronously. So how can this behaviour be explained ?

To summarize :

SFB method -> REQUIRED

SLB method (parsing of WEB page, urls stored in String[]) -> REQUIRED

SLB method JMS -> REQUIRED

==> JMS messages are received synchronously



SFB method -> REQUIRED

SLB method -> REQUIRED

SLB method JMS -> REQUIRES_NEW

==> JMS messages are received synchronously



SFB method -> REQUIRED

SLB method -> REQUIRES_NEW

SLB method JMS -> REQUIRES_NEW

==> JMS messages are received asynchronously

I expected case 2 to send JMS messages asynchronously...

Thanks for clarification

Olivier

Upvotes: 1

Views: 688

Answers (2)

Olivier J.
Olivier J.

Reputation: 3165

Thank you JB Nizet (didn't reply quickly because I have to wait for 8 hours before replying -> I'm new in StackOverflow, need reputation)

I've already test sending JMS messages using new SLB and it was the same result but my two SLB were strongly coupled because I passed TopicPublisher and JMS message to my new testing EJB.

I just tested to create TopicPublisher and message inside my test EJB and...it's work !

Here is my new EJB :

package boursorama.stateless;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import javax.ejb.LocalBean;
import javax.ejb.Stateless;
import javax.ejb.TransactionAttribute;
import javax.ejb.TransactionAttributeType;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.ObjectMessage;
import javax.jms.Topic;
import javax.jms.TopicConnection;
import javax.jms.TopicConnectionFactory;
import javax.jms.TopicPublisher;
import javax.jms.TopicSession;


@Stateless
@LocalBean
public class Test {

    @Resource(mappedName="jms/boursoramaTopicFactory")
    TopicConnectionFactory topicConnectionFactory;

    @Resource(mappedName="jms/boursoramaTopicDestination")
    Topic topic;

    private TopicConnection _topicConnection;
    private TopicSession _topicSession;
    private TopicPublisher _topicPublisher;

    @PostConstruct
    public void postConstruct(){

        try {
            _topicConnection = topicConnectionFactory.createTopicConnection();
            _topicSession = _topicConnection.createTopicSession(false, TopicSession.AUTO_ACKNOWLEDGE);
            _topicPublisher = _topicSession.createPublisher(topic);     
        } catch (JMSException e) {          
            e.printStackTrace();
        }       
    }

    @PreDestroy
    public void preDestroy(){
        try {
            _topicConnection.close();   
            _topicConnection = null;        
        } catch (JMSException e) {          
            e.printStackTrace();
        }    
    }

    public Test() {
        // TODO Auto-generated constructor stub
    }



    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void test(){
        try {
            Message m = _topicSession.createObjectMessage();                
            _topicPublisher.send(m);
        } catch (JMSException e) {          
            e.printStackTrace();
        }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void test2(TopicPublisher to, Message m){
        try {
            to.send(m);
        } catch (JMSException e) {          
            e.printStackTrace();
        }
    }

    @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
    public void test3(Message m){
        try {
            _topicPublisher.send(m);
        } catch (JMSException e) {          
            e.printStackTrace();
        }
    }



}

When I call test() or test3() I receive all messages asynchronously but test2() send messages all together.

If I understand well, we can't manage transactions (with REQUIRES_NEW) inside same EJB or outside when some resources are shared between methods (with @Resource) ? Surely I have this behaviour to respect transaction isolation ?

Thank you again

Olivier

Upvotes: 0

JB Nizet
JB Nizet

Reputation: 691635

You don't show your code, but I suspect that you call the send method in your SLB from the parse method of the same SLB instance. In that case, the method call is a direct method call, which doesn't go through the bean proxy, and thus the transactional annotation on the send method is completely ignored.

You have

SFB -> transactional proxy -> SLB -> SLB

where you should have

SFB -> transactional proxy -> SLB -> transactional proxy -> SLB

The simplest way is to put the send method in a separate SLB.

Upvotes: 3

Related Questions