Bug 734874

Summary: Cannot send a JMS message in a transaction from MDB
Product: Red Hat Enterprise MRG Reporter: Martin Vecera <mvecera>
Component: qpid-jcaAssignee: messaging-bugs <messaging-bugs>
Status: CLOSED WONTFIX QA Contact: MRG Quality Engineering <mrgqe-bugs>
Severity: urgent Docs Contact:
Priority: unspecified    
Version: 2.0CC: iboverma, wprice
Target Milestone: 2.0.4   
Target Release: ---   
Hardware: All   
OS: All   
Whiteboard:
Fixed In Version: Doc Type: Bug Fix
Doc Text:
Story Points: ---
Clone Of: Environment:
Last Closed: 2011-09-05 14:24:57 UTC Type: ---
Regression: --- Mount Type: ---
Documentation: --- CRM:
Verified Versions: Category: ---
oVirt Team: --- RHEL 7.3 requirements from Atomic Host:
Cloudforms Team: --- Target Upstream Version:
Embargoed:

Description Martin Vecera 2011-08-31 17:32:34 UTC
The only way how to send a JMS message that participates in the current transaction from a meesage driven bean is to use transactional connection factory which is bound in JNDI unde java:/QpidJMSXA. The problematic code is:
   @Resource(mappedName = "java:/QpidJMSXA")                     
   private ConnectionFactory connectionFactory;
   @Resource(mappedName = "queue/mrg_mrg_jca_mdb_transactions_out")     
   private Destination queue;            
   private MessageProducer sender;         
   private Connection connection;                  
   private Session session;

@PostConstruct
public void init() throws Exception {
   connection = connectionFactory.createConnection();
   session =  connection.createSession(true, Session.AUTO_ACKNOWLEDGE);
   sender = session.createProducer(queue);
}

@Override
public void onMessage(Message message) {
   ...
   TextMessage response = session.createTextMessage("performance01 " + time + " ACK"); <<-- PROBLEMATIC LINE
   sender.send(response);
   ...
}

On the marked line, the following exception is thrown:
Caused by: java.lang.NullPointerException
        at org.apache.qpid.ra.QpidRASession.createTextMessage(QpidRASession.java:319)
        at org.jboss.test.mdb.MRGJCAMessageBean.onMessage(MRGJCAMessageBean.java:84)
        at sun.reflect.GeneratedMethodAccessor295.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeTarget(MethodInvocation.java:122)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:111)
        at org.jboss.ejb3.interceptors.container.ContainerMethodInvocationWrapper.invokeNext(ContainerMethodInvocationWrapper.java:72)
        at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.invoke(InterceptorSequencer.java:76)
        at org.jboss.ejb3.interceptors.aop.InterceptorSequencer.aroundInvoke(InterceptorSequencer.java:62)
        at sun.reflect.GeneratedMethodAccessor290.invoke(Unknown Source)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.jboss.aop.advice.PerJoinpointAdvice.invoke(PerJoinpointAdvice.java:174)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
        at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.fillMethod(InvocationContextInterceptor.java:72)
        at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_fillMethod_28985350.invoke(InvocationContextInterceptor_z_fillMethod_28985350.java)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
        at org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor.setup(InvocationContextInterceptor.java:88)
        at org.jboss.aop.advice.org.jboss.ejb3.interceptors.aop.InvocationContextInterceptor_z_setup_28985350.invoke(InvocationContextInterceptor_z_setup_28985350.java)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
        at org.jboss.ejb3.connectionmanager.CachedConnectionInterceptor.invoke(CachedConnectionInterceptor.java:62)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
        at org.jboss.ejb3.entity.TransactionScopedEntityManagerInterceptor.invoke(TransactionScopedEntityManagerInterceptor.java:56)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
        at org.jboss.ejb3.AllowedOperationsInterceptor.invoke(AllowedOperationsInterceptor.java:47)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
        at org.jboss.ejb3.tx.NullInterceptor.invoke(NullInterceptor.java:42)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
        at org.jboss.ejb3.stateless.StatelessInstanceInterceptor.invoke(StatelessInstanceInterceptor.java:68)
        at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
        at org.jboss.aspects.tx.TxPolicy.invokeInCallerTx(TxPolicy.java:126)
        ... 31 more


Unfortunately, this problem blocks MRG-EAP integration and performance test development.

Comment 1 Martin Vecera 2011-08-31 17:42:47 UTC
The NPE comes from a line where QpidRASession gets its internal session which is null. But now I realized from my server log, that sometimes it works. I have 15 instances of the same MDB in a pool. First, all 15 failed to send the message. Then after redelivery and 12 failures, one of them succeeded. Maybe it won a battle for a valid session? 

From other exceptions I saw, I have a feeling that there is an internal limit in JCA adapter to have a single session per connection factory and not per connection. But this is just a wild guess.

Comment 2 Weston M. Price 2011-09-01 13:41:08 UTC
You should not construct your connection, session and producer in a PostConstruct method. Sessions are pooled by the underlying JCA layer and as such need to be opened and closed on each use rather than trying to maintain them over successive message deliveries. Example:

//Remove @PostContruct


public void onMessage(Message message)
{
    Connection conn = null;
    Session sess = null;
    Producer p = null;

   try
   {
     conn = connectionFactory.createConnection
     //Create session, producer here is in your @PostConstruct method


   }
   catch(Exception e)
  {
     
  }
  finally
  {
     try
     {
       if(p != null)
          p.close();
     }catch(Exception ignore){}


     try
     {
       if(sess != null)
          sess.close();
     }catch(Exception ignore){}
     
     try
     {
       if(conn != null)
          conn.close();
     }catch(Exception ignore){}

}

Comment 3 Martin Vecera 2011-09-01 15:08:54 UTC
According to J2EE tutorial, at least creating Connection should be possible in @PostConstruct method http://download.oracle.com/javaee/5/tutorial/doc/bncgw.html

Do you have any reference that forbids creating session?

Comment 4 Weston M. Price 2011-09-01 15:10:15 UTC
Connection is ok, but you really don't want to do this. Session is not. Sessions are pooled and as such you cannot hold them over message delivery. 

This is common knowledge in general EE development and is tantamount to using a JDBC/JCA DataSource.

Comment 6 Martin Vecera 2011-09-02 06:40:05 UTC
I fully understand your point of view. It is very sane not to chache pooled resources. However, I'm looking for a specification that forbids it for a simple purpose - what if a customer used JCA adapter in that way? We need more than "common knowledge" to explain them that this is not a valid use case...

Comment 7 Weston M. Price 2011-09-02 08:50:17 UTC
Please read:

http://community.jboss.org/wiki/ShouldICacheJMSConnectionsAndJMSSessions

and close this issue.

Comment 8 Martin Vecera 2011-09-05 12:35:05 UTC
Hmmm, nice. I already said that I understand this. I know that there is cache for connections and sessions in JCA adapter. However, there is nothing that forbids my approach in the specification. This means that a customer might want us to support this. So we need to document this. How could I rise a documentation type issue/bug for MRG JCA adapter?

Comment 9 Weston M. Price 2011-09-05 12:45:08 UTC
'However, there is nothing that
forbids my approach in the specification. '

There is nothing to forbid you from caching JDBC connections either, you simply don't do this if you want your application to work correctly. This is well known in the industry and is simply a error on the part of the developer.

'This means that a customer might want
us to support this.'

There is no 'support' for this as it makes no sense and does not work for Stateless components (i.e. Stateless Session Beans and MessageDrivenBeans). If you want this behavior, use a STATEFUL Session bean, not an SLSB or an MDB. 

Again, this is NOT a defect.

Comment 10 Weston M. Price 2011-09-05 12:46:14 UTC
Note, the 'caching' part of this is provided by JCA is the form of a connection pool. THIS is why you don't cache JCA resources since it's already provided!

Comment 11 Weston M. Price 2011-09-05 12:51:41 UTC
And for something more definitive, from the JCA specification Section 6.4.1

6. After the component finishes with the connection, it closes the connection using the close method on the Connection interface.
cx.close();

7. If an application component fails to close an allocated connection after its use, that connection is considered an unused connection. The application server manages the cleanup of unused connections. When a container terminates a component instance, the container cleans up all connections used by that component instance. Refer section Section 6.5.4 “ManagedConnection” and Section 6.8.3 “Scenario: Connection Event Notifications and Connection Close” for details on the cleanup of connections.

Note, don't misread this. The JCA infrastructure will close the connection/session for you, but since you are keeping this as a state variable in your MDB instance, you are never getting a new one from the pool and as such, your connection/session handle is invalid at this point. 

Now please, close this issue.

Comment 12 Martin Vecera 2011-09-05 14:24:57 UTC
Strange thing is, that getting a Connection and preserving it in an instance variable is fine works. This is even used in J2EE Tutorial. And according to Comment 11, my connection ahdnle should be invalid as well, which isn't.
Even storing JMS Session works with JBoss Messaging. Once obtained Session should not just vanish. It should not be given to another requester while I hold it.
The only problem I can think of is that it stores something in a ThreadLocal variable...
However, I do not see any point in this argument and I'm closing the issue. There is only a little chance it would cause a trouble later and I gave it a shot...