Search code examples
javahibernatermi

How to automate Hibernate boilerplate in RMI remote methods


I have an RMI class that accepts remote calls from clients.

This class uses Hibernate to load entities and perform some business logic, in general read-only.

Currently most of the remote methods bodies look like that :

try {
    HibernateUtil.currentSession().beginTransaction();

    //load entities, do some business logic...

} catch (HibernateException e) {
   logger.error("Hibernate problem...", e);
   throw e;
} catch (other exceptions...) {
   logger.error("other problem happened...", e);
   throw e;
} finally {
   HibernateUtil.currentSession().getTransaction().rollback(); //this because it's read-only, we make sure we don't commit anything
   HibernateUtil.currentSession().close();
}

I would like to know if there is some pattern that I could (relatively easily) implement in order to automatically have this "try to open session/catch hibernate exception/finally close hibernate resources" behavior without having to code it in every method.

Something similar to "open session in view" that is used in webapps, but that could be applied to remotr RMI method calls instead of HTTP requests.

Ideally I would like to be able to still call the methods directly, not to use some reflexion passing method names as strings.


Solution

  • All i wanted was a "quick and clean" solution, if possible, so no new framework for now (I might use Spring+Hibernate stack later on though).

    So I ended up using a "quick-and-not-so-dirty" solution involving a variant of the "Command" pattern, where the hibernate calls are encapsulated inside anonymous inner classes implementing my generic Command interface, and the command executer wraps the call with the Hibernate session and exception handling. The generic bit is in order to have different return value types for the execute method.

    I am not 100% satisfied with this solution since it still implies some boilerplate code wrapped around my business logic (I am especially unhappy about the explicit casting needed for the return value) and it makes it slightly more complicated to understand and debug.

    However the gain in repetitive code is still significant (from about 10 lines to 3-4 lines per method), and more importantly the Hibernate handling logic is concentrated in one class, so it can be changed easily there if needed and it's less error-prone.

    Here is some of the code :

    The Command interface :

    public interface HibernateCommand<T> {
        public T execute(Object... args) throws Exception; 
    }
    

    The Executer :

    public class HibernateCommandExecuter {
    
        private static final Logger logger = Logger.getLogger(HibernateCommandExecuter.class);
    
        public static Object executeCommand(HibernateCommand<?> command, boolean commit, Object... args) throws RemoteException{
            try {
                HibernateUtil.currentSession().beginTransaction();
    
                return command.execute(args);
    
            } catch (HibernateException e) {
                logger.error("Hibernate problem : ", e);
                throw new RemoteException(e.getMessage());
            }catch(Exception e){
                throw new RemoteException(e.getMessage(), e);
            }
            finally {
                try{
                    if(commit){
                        HibernateUtil.currentSession().getTransaction().commit();
                    }else{
                        HibernateUtil.currentSession().getTransaction().rollback();
                    }
                    HibernateUtil.currentSession().close();
                }catch(HibernateException e){
                    logger.error("Error while trying to clean up Hibernate context :", e);
                }
            }
        }   
    }
    

    Sample use in a remotely called method (but it could be used locally also) :

    @Override
        public AbstractTicketingClientDTO doSomethingRemotely(final Client client) throws RemoteException {
            return (MyDTO) HibernateCommandExecuter.executeCommand(new HibernateCommand<MyDTO>() {
                public AbstractTicketingClientDTO execute(Object...args) throws Exception{
                    MyDTO dto = someService.someBusinessmethod(client);
                    return dto;
                }
            },false);
        }
    

    Note how the client argument is declared final, so it can be referrenced inside the inner class. If not possible to declare final, it could be passed as parameter to the executeCommand method.