Author: tchemit Date: 2011-07-08 09:19:00 +0200 (Fri, 08 Jul 2011) New Revision: 93 Url: http://nuiton.org/repositories/revision/nuiton-web/93 Log: Evolution #1624: Remove TopiaTransactionAware Evolution #1628: Improve the TopiaTransactionInterceptor Removed: trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/TopiaTransactionAware.java Modified: trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/interceptor/TopiaTransactionInterceptor.java Deleted: trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/TopiaTransactionAware.java =================================================================== --- trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/TopiaTransactionAware.java 2011-07-07 18:02:25 UTC (rev 92) +++ trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/TopiaTransactionAware.java 2011-07-08 07:19:00 UTC (rev 93) @@ -1,45 +0,0 @@ -/* - * #%L - * Nuiton Web :: Nuiton Struts 2 - * - * $Id$ - * $HeadURL$ - * %% - * Copyright (C) 2010 - 2011 CodeLutin, Tony Chemit - * %% - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program 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 General Lesser Public License for more details. - * - * You should have received a copy of the GNU General Lesser Public - * License along with this program. If not, see - * <http://www.gnu.org/licenses/lgpl-3.0.html>. - * #L% - */ -package org.nuiton.web.struts2; - -import org.nuiton.topia.TopiaContext; - -/** - * Use this contract on a object which use a {@code TopiaContext} as a - * transaction. - * <p/> - * The method {@link #getTransaction()} returns the internal transaction used. - * <p/> - * the method {@link #setTransaction(TopiaContext)} put the internal - * transaction. - * - * @author tchemit <chemit@codelutin.com> - * @since 1.2 - * @deprecated since 1.4, prefer use the same class in topia project., will be remove soon - */ -@Deprecated -public interface TopiaTransactionAware extends org.nuiton.topia.framework.TopiaTransactionAware { - -} Modified: trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/interceptor/TopiaTransactionInterceptor.java =================================================================== --- trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/interceptor/TopiaTransactionInterceptor.java 2011-07-07 18:02:25 UTC (rev 92) +++ trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/interceptor/TopiaTransactionInterceptor.java 2011-07-08 07:19:00 UTC (rev 93) @@ -32,8 +32,12 @@ import org.nuiton.topia.TopiaContext; import org.nuiton.topia.TopiaException; import org.nuiton.topia.framework.TopiaContextImplementor; -import org.nuiton.web.struts2.TopiaTransactionAware; +import org.nuiton.topia.framework.TopiaTransactionAware; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; @@ -45,25 +49,28 @@ /** * <!-- START SNIPPET: description --> * <p/> - * The aim of this Interceptor is to inject a freshly opened {@code transaction} - * in a action which implements {@link TopiaTransactionAware} contract and - * after result of action, to close the transaction. + * The aim of this Interceptor is to manage a {@code transaction} all along + * a action which implements {@link TopiaTransactionAware} + * contract. * <p/> + * Technicaly, the action will receive only a proxy of a transaction and a real + * transaction will only be created as soon as a method will be asked on it. + * <p/> * The interceptor is abstract and let user to implement the way how to open a * new transaction via the method {@link #beginTransaction()}. * <p/> - * Note that the transaction pushed in the action is in fact a proxy of the - * freshly opened transaction to forbid some method to be call on it. The list - * of method to forbid can be customized using the interceptor parameter - * {@link #excludeMethods}. + * Note that the transaction pushed in the action can be limited using a list + * of methods to exclude on it. The list of methods to forbid can be customized + * using the interceptor parameter {@link #excludeMethods}. * <p/> - * Note also that the transaction is closed after all stack of interceptor - * consumed, this means that the transaction will still be opened while - * rendering the result, this is a particular interesting thing to avoid + * Note also that the transaction is commited and closed after all stack of + * interceptor consumed, this means that the transaction will still be opened + * while rendering the result, this is a particular interesting thing to avoid * pre-loading of entities due to lazy strategy of hibernate for example. * With this mecanism you can feel free to just obtain the obtain from database * via a DAO and then really load it in the rendering result. * <p/> + * If you do not want any commit for a given action, just put on the class a commit * <p/> * This interceptor, as it provides connection to database should be in the * interceptor stack before any other interceptor requiring access to database. @@ -89,6 +96,12 @@ */ public abstract class TopiaTransactionInterceptor extends AbstractInterceptor { + /** To specify on your action that you never want any commit. */ + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.TYPE, ElementType.METHOD}) + public @interface NoCommit { + } + /** Logger. */ private static final Log log = LogFactory.getLog(TopiaTransactionInterceptor.class); @@ -109,7 +122,8 @@ } public void setExcludeMethods(String excludeMethods) { - this.excludeMethods = TextParseUtil.commaDelimitedStringToSet(excludeMethods); + this.excludeMethods = + TextParseUtil.commaDelimitedStringToSet(excludeMethods); } /** @@ -136,59 +150,109 @@ @Override public String intercept(ActionInvocation invocation) throws Exception { - org.nuiton.topia.framework.TopiaTransactionAware transactionAware = null; + TopiaTransactionAware transactionAware = null; Object action = invocation.getProxy().getAction(); - if (action instanceof org.nuiton.topia.framework.TopiaTransactionAware) { - transactionAware = (org.nuiton.topia.framework.TopiaTransactionAware) action; + if (action instanceof TopiaTransactionAware) { + transactionAware = (TopiaTransactionAware) action; } if (transactionAware == null) { - // not a transaction aware action + // not a transaction aware action, direct skip this interceptor return invocation.invoke(); } - // action need a transaction - TopiaContext transaction = beginTransaction(); + // creates a proxy of a lazy transaction - if (log.isDebugEnabled()) { - log.debug("Open transaction " + transaction); - } + TopiaTransactionProxyInvocationHandler proxyInvocationHandler = + new TopiaTransactionProxyInvocationHandler(); - // creates a proxy on the transaction to push back in action TopiaContext proxy = (TopiaContext) Proxy.newProxyInstance( getClass().getClassLoader(), new Class<?>[]{TopiaContext.class, TopiaContextImplementor.class}, - new TopiaTransactionProxyInvocationHandler(transaction) + proxyInvocationHandler ); // set the transaction in the action transactionAware.setTransaction(proxy); - boolean doCommit = true; + boolean doCommit = isCommitNeeded(action, invocation); try { return invocation.invoke(); } catch (Exception e) { doCommit = false; - transaction.rollbackTransaction(); + TopiaContext transaction = proxyInvocationHandler.getTransaction(); + if (transaction != null && !transaction.isClosed()) { + if (log.isDebugEnabled()) { + log.debug("rollback transaction " + transaction); + } + transaction.rollbackTransaction(); + } + throw e; } finally { - if (doCommit) { - transaction.commitTransaction(); + TopiaContext transaction = proxyInvocationHandler.getTransaction(); + + if (transaction != null && !transaction.isClosed()) { + try { + if (doCommit) { + + // commit the opened transaction + if (log.isDebugEnabled()) { + log.debug("Commit transaction " + transaction); + } + transaction.commitTransaction(); + } + } finally { + + // close the opened transaction + if (log.isDebugEnabled()) { + log.debug("Close transaction " + transaction); + } + transaction.closeContext(); + } + } + } + } + + protected boolean isCommitNeeded(Object action, + ActionInvocation invocation) throws NoSuchMethodException { + Class<?> actionType = action.getClass(); + boolean noCommit = actionType.isAnnotationPresent(NoCommit.class); + if (noCommit) { + + // a no commit annotation was found on the top of action if (log.isDebugEnabled()) { - log.debug("Close transaction " + transaction); + log.debug("NoCommit annotation found on action " + + actionType.getName()); } - // close the opened transaction - transaction.closeContext(); + return false; } + + // try to look on the methodName + String methodName = invocation.getProxy().getMethod(); + if (methodName == null) { + + // no methodName specify, means the execute one + methodName = "execute"; + } + Method method = actionType.getMethod(methodName); + noCommit = method.isAnnotationPresent(NoCommit.class); + if (noCommit) { + if (log.isDebugEnabled()) { + log.debug("NoCommit annotation found on action methodName " + + actionType.getName() + "#" + methodName); + } + } + return !noCommit; } /** @@ -196,13 +260,13 @@ * * @see TopiaTransactionInterceptor#excludeMethods */ - class TopiaTransactionProxyInvocationHandler implements InvocationHandler { + public class TopiaTransactionProxyInvocationHandler implements InvocationHandler { /** Target to use for the proxy. */ - protected final TopiaContext tx; + protected TopiaContext transaction; - TopiaTransactionProxyInvocationHandler(TopiaContext tx) { - this.tx = tx; + public TopiaContext getTransaction() { + return transaction; } @Override @@ -220,9 +284,18 @@ proxy); } - // can invoke the method on the tx + if (transaction == null) { + + // first time transaction is required, create its + transaction = beginTransaction(); + + if (log.isDebugEnabled()) { + log.debug("Open transaction " + transaction); + } + } + // can invoke the method on the transaction try { - Object result = method.invoke(tx, args); + Object result = method.invoke(transaction, args); return result; } catch (Exception eee) { if (log.isErrorEnabled()) {