PDA

View Full Version : AutoProxied bean not propagating transaction rollback



kirt
May 24th, 2005, 01:15 PM
I've set up auto proxying and created a TransactionInterceptor to rollback on exceptions thrown from methods named "processMessage". It's configured with the following attribute:



<property name="properties">
<props>
<prop key="processMessage">
PROPAGATION_REQUIRED,-MessageProcException
</prop>
</props>
</property>


My bean is an ActiveMQ container connector that is injected with an implementation of javax.jms.MessageListener.



<bean id="mqConnector" ...>
...
<property name="messageListener">
<ref bean="listener"/>
</property>
</bean>

<bean id="listener" class="MyProcessor">
<property name="destination"><value>my.Queue</value></property>
...
</bean>


Because the MessageListener interface (method 'onMessage()') doesn't throw checked exceptions, my implementation is an abstract class that includes an abstract 'processMessage()' method that does, and that is called by onMessage(). Implementations of this class for various processing needs implement processMessage():



abstract class my.MessageListenerImpl implements javax.jms.MessageListener &#123;

public void onMessage&#40;Message m&#41; &#123;
try &#123;
processMessage&#40;m&#41;;
&#125; catch &#40;Exception e&#41; &#123;
logger.error&#40;e.getMessage&#40;&#41;&#41;;
&#125;

public abstract void processMessage&#40;Message m&#41;
throws MessageProcException;

&#125;

class MyProcessor extends MessageListenerImpl &#123;

public void processMessage throws MessageProcException &#123;
...
&#125;

&#125;


The onMessage() method is called by the connector when a message arrives for processing. It in turn calls processMessage(). If processMessage() throws, however, the transaction is not rolled back.

When I add "PROPAGATION_REQUIRED,..." for "onMessage" in the interceptor's attributes and generate unchecked exceptions in onMessage(), the transaction is rolled back, but my MQ provider (ActiveMQ) doesn't seem to handle exceptions from MessageListener implementations gracefully (although I could be misinterpreting the logs.)

I've also tried turning the two classes into two concrete classes, listener and processor, and injecting the listener with the processor but still no rollback on exception (the processor in this case still implements a processor interface). I was thinking that local method calls within a class might not execute proxy code.

I'd love advice (no pun intended) on what to try next (or whether to just throw unchecked exceptions from MessagListener) from anyone who's configured transacted JMS message processing like this -- especially with ActiveMQ.

thanks,

kirt

tentacle
May 25th, 2005, 02:31 AM
Let onMessage throw MessageProcException wrapped in an unchecked exception, that's the only way to notify the transaction interceptor to rollback, apart from doing a programmatic rollback.

katentim
May 25th, 2005, 03:49 AM
that's the only way to notify the transaction interceptor to rollback, apart from doing a programmatic rollback
You can specify rollback of checked exceptions declaritively as shown in the original post. See 7.4. Declarative transaction management (http://www.springframework.org/docs/reference/transaction.html#d0e4771) of the reference manual.

tentacle
May 25th, 2005, 03:55 AM
The checked exception is never thrown by the onMessage method, as illustrated in the code example.

katentim
May 25th, 2005, 04:16 AM
The checked exception is never thrown by the onMessage method, as illustrated in the code example.
I see. kirt, if processMessage() is called directly by onMessage(), which it is, the proxy will never see the exception to enforce the rollback.

kirt
May 25th, 2005, 07:31 AM
I see. kirt, if processMessage() is called directly by onMessage(), which it is, the proxy will never see the exception to enforce the rollback.

If I change this so that onMessage() calls a processMessage() method on another bean, the proxy should then see exceptions thrown from the latter, right? So the proxy doesn't affect the inner workings of a class at all, just external calls to proxied objects?

Thanks for your responses.

tentacle
May 25th, 2005, 07:38 AM
I see. kirt, if processMessage() is called directly by onMessage(), which it is, the proxy will never see the exception to enforce the rollback.

If I change this so that onMessage() calls a processMessage() method on another bean, the proxy should then see exceptions thrown from the latter, right? So the proxy doesn't affect the inner workings of a class at all, just external calls to proxied objects?

Not quite. The proxy is a vehicle to decorate method calls with - in this case - an interceptor. The transactional interceptor will catch exceptions thrown by the target bean and based on its transaction attributes may do a rollback. The onMessage method catches the exception and does not rethrow so the exception will never reach the interceptor, hence no rollback will occur.

tentacle
May 25th, 2005, 07:45 AM
I see. kirt, if processMessage() is called directly by onMessage(), which it is, the proxy will never see the exception to enforce the rollback.

If I change this so that onMessage() calls a processMessage() method on another bean, the proxy should then see exceptions thrown from the latter, right? So the proxy doesn't affect the inner workings of a class at all, just external calls to proxied objects?

Something just occured to me. The messages are processed asynchronously, so indeed, if you call a proxy instance from within the onMessage method a rollback would indeed occur.

kirt
May 25th, 2005, 09:36 AM
Right -- the autoproxy mechanism would be used to proxy any bean implementation of a processMessage() method. Thanks for your help.