JUnit and jms - events fired are not consumed in the middle of test - spring

We are using spring with junit , jms (activemq) and mySql.
We would like to create some junit tests that after their executions the db will rollback.
In order to achieve that we are using the #Transactional annotation for each tests.
Problem is, one of our tests is calling a service that sends a jms message, (in the middle of the test) the thing is the event is being consumed only after the tests ends (end of transaction maybe?)
thats why the assertion in the end of the test fails.
any ideas why the event is not being consumed right away (p.s we tried to use sleep in order to let the event be consumed, its not working)

Firstly, this is not a unit test ... that's fine ... There are 2 reasons message is not consumed:
The transaction in which the message is sent is not committed. Due to this the lock on the message on the server side is not released.
There is a timing issue and the test ends too soon and doesn't wait got the callback.

That's the whole point of transactions. The message is not available for consumption until the transaction commits (otherwise how could you roll it back if someone's already seen it?). You can do the send in a new transaction (Propagation.REQUIRES_NEW) if you don't want the send to be part of the encompassing transaction.

Related

jmslistener transactional junit rollback

I see multiple examples demonstrating on how to test jms-listeners, but what i am really looking for is testing some negative test cases such as,
test if jms transaction is rolled back if there is any error processing the message
test if jms listener handles/throws exception
Below are some examples, which jst shows positive test cases,
How to wait for #JMSListener annotated method to complete in JUnit
Writing tests to verify received msg in jms listener (Spring-Boot)
Use one of the positive test techniques to verify the listener actually received the message.
Then simply us JmsTemplate.receive() to verify the message was rolled back into the queue.
Or, if you are configured to send poison messages to a DLQ after some number of delivery attempts, you can simply receive from the DLQ in your test case.

How to disconnect from rabbitmq after each test?

I have integrated with rabbitmq in a spring application. There are two SpringRunner tests which assert on whether the amqp receiver receives a message. The tests connect to a rabbitmq broker running in a separate process.
The problem is that the application context loads on first test and registers a consumer to the queue, but does not disconnect after the test completes.
When the second test runs, the application context for it also registers a consumer, but any messages sent to the exchange as a part of the second test still go to the consumer registered by application context from the first test.
Both the tests run sequentially.
Is there a way to kill the first context completely before the second test starts so that there is just one consumer at a time ? Or any other way to solve the problem ?
Thank you
Tried #DirtiesContext before test did not help
Well, to be honest a #DirtiesContext on all the test classes level, alongside with the #RunWith(SpringRunner.class), is the way to.
The ListenerContainer is an active component which starts its own threads, so even when you are done with your test, it doesn't mean that background thread is stopped. For this purpose you indeed have to use a #DirtiesContext on every test class to ensure that all the application contexts are closed after finishing tests. It ensures that those listener containers are stopped as well.
It is just not enough to place a #DirtiesContext on your one test class, because there is no guarantee in which order they are going to be called. So, present it as much as possible on your test classes to avoid this or similar race conditions.

Wildfly 10 jms send Message to queue as part of XA transaction

I have recently had to support a colleague in verifying why some system tests are not passing in wildfly, system tests that pass consistently on weblogic and glass fish.
After analysing the log, it became clear the reason is related to a JMS message sent by a backed thread getting committed to a queue too soon, when the expectation was the message would be committed when the entry point Container Managed Transaction of an MDB commits. So the message is going out before the MDB that sends it is done running.
In weblogic, to achieve the expected behaviour, you need to make sure that when you take the connection factory given by the container , which is XA configured, you set the connection.createseesion with
transacted = true and
acknowledgement = session transacted.
In a process similar to the one depicted in this URL
http://www.mastertheboss.com/jboss-server/jboss-jms/sending-jms-messages-over-xa-with-wildfly-jboss-as
Except in the snippet above auto acknowledge is set and the first parameter is set to false.
In wildly when our weblogic and glass fish configuration is used, nothing is committed and the system behaves as if the JMS message sent were to be rolled back.
If configuration as in the example above were to be used, instead what would happen is that the JMS message is immediately and the consumer MDB immediately launches being trigerred before the producer transaction actually ends, causing the system test to fail.
According to the official JMS configuration, by using a connection-pooled factory with the transaction=XA attribute, the container should immediately bind the commit of the transaction to the lifecycle of the parent transaction.
See official documentation bellow in particular in respect to the Java:/JmsXa connection factory.
https://docs.jboss.org/author/display/WFLY10/Messaging+configuration
My colleague was initially using a non pooled connection factory, but the injection info reference has since then been fixed. I have tried all possible combinations of parameters in the shed message, but my outcome is sitll:
Either sent too soon or never sent.
To conclude all the other resources are XA. Namely the oracle db is using the XA driver.
Can anyone confirm if in wildly the send JMS message only when parent transaction commits is working and if so how the session is being configured?
I will check if perhaps my colleague has not made a mistake in terms of the configuration of the connection factory used by the Men's themselves to consume messages out of the queue.but if that one is also XA... Then it is a big problem.
So the issues is fixed.
The commit of the JMS message to the queue at the end of the transaction works perfectly.
The issue was two fold:
(a) First spot of code I was looking at address the issue was not correct. Someone had decided to write his own send telegram to queue API elsewhere, and was not using the central API for sneding telegrams, so any modification I to the injection connection factory was actually not taking effect. The stale connection factories were still being used.
(b) Once the correct API was spotted it was easy to make the mechanism work by using the widlfy XA pooled connection factory mentioned in the post above.
The one thing that was tweaked was the connection.CreationSession api.
The API in JEE 7 has been enlarged and it is now better documented than in jEE 6.
To send a JMS message in a container as part of an XA transaction one should do:
connection.createSession() without any parameters.
This can easily be seen in the connection javadoc:
https://docs.oracle.com/javaee/7/api/javax/jms/Connection.html
QUOTE 1:
This method has been superseded by the method createSession(int
sessionMode) which specifies the same information using a single
argument, and by the method createSession() which is for use in a Java
EE JTA transaction. Applications should consider using those methods
instead of this one.
QUOTE 2:
In a Java EE web or EJB container, when there is an active JTA
transaction in progress:
Both arguments transacted and acknowledgeMode are ignored. The session will participate in the JTA transaction and will be committed
or rolled back when that transaction is committed or rolled back, not
by calling the session's commit or rollback methods. Since both
arguments are ignored, developers are recommended to use
createSession(), which has no arguments, instead of this method.
Which means, the code snippet in:
http://www.mastertheboss.com/jboss-server/jboss-jms/sending-jms-messages-over-xa-with-wildfly-jboss-as
Is not appropriate. What one should be doing is creating the session without any parameter and leting the container handle the rest.
Which it does just fine.

Spring JMS Testing and Transactional Rollback

I have a test harness using Spring's SpringJUnit4ClassRunner (which automatically rolls back DB updates at the end of each JUnit test). Now I want to test my Spring-based JMS code:
Some DB item is created
A JMS message is sent to do something
The MDP picks up the message and does something else in the DB
What is actually happening in the Test is the following:
Spring begins the transaction (tx1) on thread 1
The DB item is created in the tx1 context
The JMS message is sent
Transction tx1 is rolled back and thread 1 exits
Thread 2 spins up the MDP, begins transaction tx2, reads the message, and does DB work
Thread 2 commits tx2, as its not spun up by the test harness, and exits
What I want to have happen is for tx1 and tx2 to both be controlled by SpringJUnit4ClassRunner so I get the automatic rollback. Even better would be if they could share the same overall transactional context; right now, if something is saved in tx1, even if Thread 1 hasn't rolled back that tx, tx2 doesn't see the DB changes.
You need to use a transaction manager that supports XA, and is thus able to do a two-phase commit on the database and the JMS engine. There are several such transaction managers (Bitronix, Atomikos, etc.) freely available.
If you already use such a transaction manager (because your app runs in an application server providing it), but you don't want to configure something as heavy just for the unit tests, then implement two unit tests
one where the JMS part is mocked up (to test the DB item creation only),
and one where only the JMS message handling is tested
Note that without XA, you'll probably have to handle bizarre situations where the JMS message is handled whereas the first transaction is not committed yet, or where the database transaction is comitted but the sending of the message fails.

JBoss doesn't sends a JmsTemplate (Spring) message

Well, actually JBoss does send the message, but only when the current transaction started by the EJB is finished.
We have this problem with JBoss 4.0.3 and Spring's JmsTemplate. An EJB sends a message to a queue with a temporary queue as the reply_to field. Then, inside the same transaction, we listen for the response given by the first MDB. The problem is that the JmsTemplate's method "send" isn't executed after the transaction have finished. So, by the time the message is sent to the queue, and processed by the MDB, the listener of the temporary queue is gone.
This is called "Synchronous Reception"
Two things change this behavior but does raise some concerns:
Change the EJB's transaction type to BMT. (Concern: BMT sucks)
Create a thread that all it does is to call the JmsTemplate.send() method.
As a side note, this is an EJB that is working correctly on a weblogic environment, and the message does get sent when it should, in the middle of the transaction not when it's over.
Thanks for any help.
JBoss's behaviour is correct. JMS is a transactional API, and sends should only be executed when the tx commits.
It may be possible to convince JmsTemplate not use the current transactional context, although it makes a point of trying to hide the unpleasantness of the JMS API from you.
You could wrap the JMS template in code, either a Stateless session bean or a service method using Spring's transaction management, that uses a transaction propagation of REQUIRES_NEW. That way the sending of the message is in its own transaction that will commit the sending of the message outside the scope of the wrapping transaction.
I'm not sure why this would work on Weblogic though. My guess would be that on Weblogic it's not recognizing the queue as an XA Queue.

Resources