I basically created a quick and dirty little "ServiceSession" interface. It's similar to this example:
Code:
public interface ServiceSession {
String startSession(TransactionDefinition ...);
void commitSession(String sessionId);
void rollbackSession(String sessionId);
}
I export this from the server using Spring's http invoker stuff. Technically, it's up to the client to invoke 'startSession' when it needs to, record the session id, and pass that session id to any remote method invocation on the server side. Once startSession is invoked, a new thread is created on the server and stays alive until either commitSession or rollbackSession is invoked. However, I'm going to make this transparent to both client code and server side code (see below). I've got another interface available on the server side only that allows server side code to execute a Callable within a session's dedicated thread. It looks something like this:
Code:
public interface ServiceSessionExecutor {
<T> T invokeInSession(String sessionId, Callable<T> invocation);
}
Right now, I'm in the process of implementing these things:- Session timeout - if no activity occurs against a session within a specified amount of time, the session's transaction will be rolled back and the session will be closed.
- Client side aspect to automatically start and stop the service session when invoking a target remote service. This aspect is applied to the (client side) remote invocation proxy of the destination service (the one providing the actual service, not the ServiceSession service). When a method is invoked against the proxy, the aspect checks to see (using Spring API) if a transaction is active on the client side. If so, it checks to see if the client already has a session open on the server side for the current thread. If not, it calls 'startSession' and binds the session id to the current thread. It then registers a transaction synchronization on the client side that automatically invokes 'commitSession' when the client side transaction commits or 'rollbackSession' if the client side transaction rolls back. This is all done via Spring's TransactionSynchronizationManager.
- A RemoteInvocationFactory (client side) that decorates a Spring http invoker invocation with the service session id bound to the thread.
- A server side RemoteInvocationExecutor that checks to see if a service session id is present in the remote invocation and, if so, does something like this:
Code:
Object invoke(RemoteInvocation invocation, Object targetObject) throws ... {
if(invocation has session id) {
return sessionExecutor.invokeInSession(sessionId, new Callable<Object>() {
public Object call() throws Exception {
decoratedInvoker.invoke(invocation, targetObject);
}
});
} else {
decoratedInvoker.invoke(invocation, targetObject);
}
}
With all that in place, all one has to do to make use of "service sessions" is configure things properly via Spring. The key here is that neither the client code or your server code need know anything about this whole service session thing, as it is now all handled via aspects and other Spring abstractions. On the client side, you use your remote service like your normally would, only now it participates (though not in a fully ACID compliant way) with the client's transaction. In other words, a poor man's distributed transaction. Your client code need know nothing of the service sessions things. If it uses Spring's transaction abstraction, then the remote service will participate. The client is also free to invoke the remote service outside of a transaction, in which case it will get default Spring behavior.