Java tutorial
/* * Copyright (c) 2008-2011, Martijn Brinkers, Djigzo. * * This file is part of Djigzo email encryption. * * Djigzo is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License * version 3, 19 November 2007 as published by the Free Software * Foundation. * * Djigzo is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public * License along with Djigzo. If not, see <http://www.gnu.org/licenses/> * * Additional permission under GNU AGPL version 3 section 7 * * If you modify this Program, or any covered work, by linking or * combining it with aspectjrt.jar, aspectjweaver.jar, tyrex-1.0.3.jar, * freemarker.jar, dom4j.jar, mx4j-jmx.jar, mx4j-tools.jar, * spice-classman-1.0.jar, spice-loggerstore-0.5.jar, spice-salt-0.8.jar, * spice-xmlpolicy-1.0.jar, saaj-api-1.3.jar, saaj-impl-1.3.jar, * wsdl4j-1.6.1.jar (or modified versions of these libraries), * containing parts covered by the terms of Eclipse Public License, * tyrex license, freemarker license, dom4j license, mx4j license, * Spice Software License, Common Development and Distribution License * (CDDL), Common Public License (CPL) the licensors of this Program grant * you additional permission to convey the resulting work. */ package mitm.common.hibernate; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import javassist.util.proxy.RuntimeSupport; import mitm.common.hibernate.annotations.InjectHibernateSession; import mitm.common.hibernate.annotations.InjectHibernateSessionSource; import mitm.common.hibernate.annotations.StartTransaction; import mitm.common.reflection.BasicProxyFactory; import mitm.common.reflection.ProxyFactoryException; import mitm.common.reflection.ProxyMethodHandler; import mitm.common.reflection.ReflectionUtils; import org.apache.commons.lang.exception.ExceptionUtils; import org.hibernate.Session; import org.hibernate.Transaction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class AutoCommitProxyFactory<T> extends BasicProxyFactory<T> { private static final Logger logger = LoggerFactory.getLogger(AutoCommitProxyFactory.class); private final HibernateSessionSource sessionSource; private Method injectHibernateSessionMethod; private Method injectHibernateSessionSourceMethod; /* * Wrapper class to keep track of sessions and recursion depth */ private class ActiveSession { private Session session; private int recursionLevel; } /* * The proxy can recursively be called when a function with @StartTransaction annotation calls another * method of the proxy having a @StartTransaction annotation as well. We need to do some bookkeeping * to delay committing the transaction and reuse sessions. */ private final ThreadLocal<ActiveSession> activeSessions = new ThreadLocal<ActiveSession>(); class StartTransactionMethodHandler implements ProxyMethodHandler { @Override public Object invoke(Object self, Method method, Method proceed, Object[] args) throws Throwable { if (logger.isDebugEnabled()) { String methodDescriptor = ReflectionUtils.createMethodDescriptor(method); logger.debug("invoking '" + methodDescriptor + "'"); } ActiveSession activeSession = activeSessions.get(); if (activeSession == null) { activeSession = new ActiveSession(); activeSessions.set(activeSession); } if (activeSession.session == null) { assert (activeSession.recursionLevel == 0); activeSession.session = sessionSource.newSession(); } activeSession.recursionLevel++; logger.debug("Session started, beginning transaction."); Transaction tx = activeSession.session.beginTransaction(); try { /* inject the session */ injectHibernateSessionMethod.invoke(self, new Object[] { activeSession.session }); try { /* execute the real method */ Object result = proceed.invoke(self, args); activeSession.recursionLevel--; if (activeSession.recursionLevel == 0) { logger.debug("committing transaction."); tx.commit(); activeSessions.set(null); sessionSource.closeSession(activeSession.session); } return result; } finally { if (activeSession.recursionLevel == 0) { /* clear the session */ injectHibernateSessionMethod.invoke(self, new Object[] { null }); } } } catch (Exception e) { logger.debug("Exception in invoke.", e); try { if (tx.isActive()) { tx.rollback(); } } catch (Exception rbe) { logger.warn("Exception rolling back transaction.", rbe); } activeSessions.set(null); try { sessionSource.closeSession(activeSession.session); } catch (Exception rbe) { logger.warn("Exception closing session.", rbe); } /* * A checked exception gets wrapped in an InvocationTargetException. We want to throw the * cause exception so we need to get the cause. */ if (e instanceof InvocationTargetException) { /* try to re-throw the source exception. */ Throwable cause = ExceptionUtils.getCause(e); if (cause != null) { throw cause; } } throw e; } } } public AutoCommitProxyFactory(Class<T> clazz, HibernateSessionSource sessionSource) throws NoSuchMethodException { super(clazz); this.sessionSource = sessionSource; searchForStartTransactionAnnotations(clazz); findInjectMethods(clazz); if (injectHibernateSessionMethod == null) { throw new NoSuchMethodException("@InjectHibernateSession annotation was not found."); } this.setMethodHandler(new StartTransactionMethodHandler()); } @Override protected void postConstruction(T newInstance) throws ProxyFactoryException { /* inject session source */ if (injectHibernateSessionSourceMethod != null) { try { injectHibernateSessionSourceMethod.invoke(newInstance, new Object[] { sessionSource }); } catch (IllegalArgumentException e) { throw new ProxyFactoryException(e); } catch (IllegalAccessException e) { throw new ProxyFactoryException(e); } catch (InvocationTargetException e) { throw new ProxyFactoryException(e); } } } /* * Searches all methods for @StartTransaction annotations */ private void searchForStartTransactionAnnotations(Class<T> clazz) { Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(StartTransaction.class)) { addMethod(method); } } } /* * Searched the annotations for a method with the @InjectHibernateSession annotations and * checks if that method has the parameter signature (org.hibernate.Session session) */ private void findInjectMethods(Class<T> clazz) throws NoSuchMethodException { Method[] methods = clazz.getMethods(); for (Method method : methods) { if (method.isAnnotationPresent(InjectHibernateSession.class)) { String parameterDescriptor = RuntimeSupport.makeDescriptor(method); if (!parameterDescriptor.equals("(Lorg/hibernate/Session;)V")) { throw new NoSuchMethodException( "@InjectHibernateSession method does not have the correct parameters."); } injectHibernateSessionMethod = method; } else if (method.isAnnotationPresent(InjectHibernateSessionSource.class)) { String parameterDescriptor = RuntimeSupport.makeDescriptor(method); if (!parameterDescriptor.equals("(Lmitm/common/hibernate/HibernateSessionSource;)V")) { throw new NoSuchMethodException( "@InjectHibernateSessionSource method does not have the correct parameters."); } injectHibernateSessionSourceMethod = method; } } } }