In my service I use spring + hibernate to manage the persistence to the database using a DAO pattern. I also have each test run in a single database transaction (via spring) so that I can roll back the changes made to the database (tests should not permanently change the DB so I can run the tests multiple times).
To enable this I use the spring TransactionSynchronizationManager to bind the hibernate Session to be used in the current thread and also wrap the test method call in a TransactionTemplate.
The problem comes when using the LocalTransport as it contains two threads, one to send the message and one to receive the message via a Piped(Input/Output) Stream pair. As the actual service method call is performed within a different thread from the unit test that had the session and transaction bound via a thread local. So the database code runs in a separate transaction and uses a separate hibernate session for each DAO method call so you end up with session closed errors on lazy loaded collections. In production (non-tests) this isn't an issue as I run a servlet filter that wraps the whole servlet request call.
To resolve this I created a new version of LocalChannel and LocalTransport that uses one additional thread rather than two. The sending thread is still used to send the message from the client to the service. But now the receive code is run in the same thread as the unit test. In this case because the service is run in the same thread it can use the same hibernate session and transaction which solves the closed session and the transaction issues.
Below is the changed method for the LocalChannel (the LocalTransport change was just to use this version of LocalChannel instead).
private void sendViaNewChannel(final MessageContext context,
final MessageContext receivingContext, final OutMessage message,
final Channel channel, final String uri) throws XFireException {
try {
final PipedInputStream stream = new PipedInputStream();
final PipedOutputStream outStream = new PipedOutputStream(stream);
Thread writeThread = new Thread(new Runnable() {
public void run() {
try {
final XMLStreamWriter writer = STAXUtils
.createXMLStreamWriter(outStream, message
.getEncoding(), context);
message.setProperty(Channel.OUTPUTSTREAM, outStream);
message.getSerializer().writeMessage(message, writer,
context);
writer.close();
outStream.close();
} catch (Exception e) {
throw new XFireRuntimeException(
"Couldn't write stream.", e);
}
}
;
});
writeThread.start();
try {
final XMLStreamReader reader = STAXUtils.createXMLStreamReader(
(InputStream)stream, message.getEncoding(), context);
final InMessage inMessage = new InMessage(reader, uri);
inMessage.setEncoding(message.getEncoding());
channel.receive(receivingContext, inMessage);
reader.close();
stream.close();
} catch (Exception e) {
throw new XFireRuntimeException("Couldn't read stream.", e);
}
try {
writeThread.join();
} catch (InterruptedException e) {
}
} catch (IOException e) {
throw new XFireRuntimeException("Couldn't create stream.", e);
}
}
I also noticed that while trying to debug the code that when I had a breakpoint on the receiving code the test would finish without waiting for the result from the service. Which looks like it might be a timing issue that times out waiting for the return from the service invocation. The approach above also resolves this issue.
Can anyone see any reason why the above change to local channel could not be used in all circumstances or any issues that may arise?
Cheers,
Paul