views:

232

answers:

1

Hi !
I'm trying to create integration tests for a legacy application deployed on Weblogic 8.1 using a subclass of AbstractTransactionalJUnit4SpringContextTests.

My test method has the following annotations :

@Test
@Rollback(true)
public void testDeployedEJBCall throws Exception {...}

My test class also references beans of type org.springframework.ejb.access.SimpleRemoteStatelessSessionProxyFactoryBean, which proxy the EJBs deployed on my weblogic server.

When I call methods on this proxy bean in a sequencial manner in my test method, the transaction rolls back correctly at the end of the test.

e.g. :

@Test
@Rollback(true)
public void testDeployedEJBCall throws Exception {
    Long result1 = myejb.method(100L);
    Long result2 = myejb.method(200L);
    ...
}

However, I would like to make 2 parallel calls to the same EJB method. Therefore I've made an inner class that implements Callable, in order to call my methods in 2 different Threads and hope to run those in parallel.
However, doing this seems to make the ejb methods to be called outside my transaction, and nothing is rolled back.

Here is what the full test class would like when I run the method calls in parallel :

import org.springframework.test.annotation.*;

@RunWith(SpringJUnit4ClassRunner.class)
@Transactional
@ContextConfiguration(locations = {"classpath:path/to/tests-config.xml"})
@TransactionConfiguration(defaultRollback=true)
public final class IntegrationTests extends AbstractTransactionalJUnit4SpringContextTests {
    @Autowired
    protected JndiTemplate jndiTemplate;
    @Resource
    protected Proxy myEJB;

    public IntegrationTests() {
        super();
        this.logger = Logger.getLogger(IntegrationTests.class);
    }

    @Test
    @Rollback(true)
    public void testDeployedEJBCall() throws Exception {
        // Create a thread pool for parallel execution. 
        ExecutorService exec = Executors.newFixedThreadPool(2);

        // Prepare the tasks for parallel execution
        List<CallEJBTask> tasks = new ArrayList<CallEJBTask>();
        tasks.add(new CallEJBTask(100L, this.myEJB));
        tasks.add(new CallEJBTask(200L, this.myEJB));

        // Execute all pending tasks in the exec Threadpool
        List<Future<Long>> results = exec.invokeAll(tasks);

        // Get the results of each task
        Long result1 = results.get(0).get();
        Long result2 = results.get(1).get();

        ...
    }
}

private class CallEBJTask implements Callable<Long> {
    private final Long valueToTest;
    private final MyEJB myEJB;

    public CallEJBTask(Long valueToTest, Proxy myEJBProxy)
        this.valueToTest = valueToTest;
        this.myEJB = (MyEJB)myEJBProxy;
    }

    public Long call() throws Exception {
        return getResult();
    }

    public Long getResult() {
        Long result = null;

        try {
            result = this.myEJB.method(this.patient);

        } catch (Exception e) {
            ...
        }
        return result;   
    } 
}

Is there a way to make this rollback ???

Thanks for your help.

Regards,

Philippe

+1  A: 

Not automatically, no. The problem is that the two extra threads don't participate in the transaction, hence their actions don't rollback.

What is the purpose of the two parallel executions? You will unlikely be able to test for concurrency issues with this approach, if that is what you're aiming for.

Edit: The problem is that testing for concurrency issues is very hard, because your tests are, at best, probabilistic ­­­­– success or failure depend on subtle timing issues that may only surface on the billionth run. See this Serverside article for a good summary of the basics.

The rule of thumb should be to avoid hand-coding threading whenever possible, as it is hard to get right and difficult to test. If you can, avoid shared state between threads, and if there is no way around it, rely on the concurrent data structures and asynchronous executors from the java.util.concurrent package.

Henning
Yes it's exactly what I'm aiming for... what approach should I use instead ?The other possibility I thought about is running the same test on 2 different machines using @Repeat(someBigNumber) in the header of my test method... would that work ?
Philippe
Hmmm... I had seen that article... isn't that exactly what I'm doing in my example ?
Philippe