From tchemit@users.nuiton.org Wed Nov  9 16:54:16 2011
From: tchemit@users.nuiton.org
To: nuiton-web-commits@list.nuiton.org
Subject: [Nuiton-web-commits] r132 - in trunk:
	nuiton-struts2/src/main/java/org/nuiton/web/struts2/filter
	nuiton-struts2/src/main/java/org/nuiton/web/struts2/interceptor	nuiton-web
 nuiton-web/src/main/java/org/nuiton/web
	nuiton-web/src/main/java/org/nuiton/web/filter
Date: Wed, 09 Nov 2011 16:54:16 +0100
Message-ID: <20111109155416.E2FF8158B5@nuiton.codelutin.com>
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="===============8534101860566310963=="

--===============8534101860566310963==
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable

Author: tchemit
Date: 2011-11-09 16:54:16 +0100 (Wed, 09 Nov 2011)
New Revision: 132

Url: http://nuiton.org/repositories/revision/nuiton-web/132

Log:
Evolution #1800: Introduce a new neutral TopiaTransactionFilter

Added:
   trunk/nuiton-web/src/main/java/org/nuiton/web/filter/
   trunk/nuiton-web/src/main/java/org/nuiton/web/filter/TopiaTransactionFilte=
r.java
Modified:
   trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/filter/CloseTopi=
aTransactionFilter.java
   trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/interceptor/Open=
TopiaTransactionInterceptor.java
   trunk/nuiton-web/pom.xml

Modified: trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/filter/Cl=
oseTopiaTransactionFilter.java
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/filter/CloseTop=
iaTransactionFilter.java	2011-10-31 10:39:08 UTC (rev 131)
+++ trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/filter/CloseTop=
iaTransactionFilter.java	2011-11-09 15:54:16 UTC (rev 132)
@@ -49,7 +49,9 @@
  *
  * @since 1.5
  * @author bleny
+ * @deprecated since 1.6, prefer use the neutral filter org.nuiton.web.filte=
r.TopiaTransactionFilter from nuiton-web module
  */
+@Deprecated
 public class CloseTopiaTransactionFilter implements Filter {
=20
     /** Logger. */

Modified: trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/intercept=
or/OpenTopiaTransactionInterceptor.java
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/interceptor/Ope=
nTopiaTransactionInterceptor.java	2011-10-31 10:39:08 UTC (rev 131)
+++ trunk/nuiton-struts2/src/main/java/org/nuiton/web/struts2/interceptor/Ope=
nTopiaTransactionInterceptor.java	2011-11-09 15:54:16 UTC (rev 132)
@@ -86,7 +86,9 @@
  * @author tchemit <chemit@codelutin.com>
  * @author bleny <leny@codelutin.com>
  * @since 1.5
+ * @deprecated since 1.6, prefer use the neutral filter org.nuiton.web.filte=
r.TopiaTransactionFilter from nuiton-web module
  */
+@Deprecated
 public abstract class OpenTopiaTransactionInterceptor extends AbstractInterc=
eptor {
=20
     /** Logger. */

Modified: trunk/nuiton-web/pom.xml
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/nuiton-web/pom.xml	2011-10-31 10:39:08 UTC (rev 131)
+++ trunk/nuiton-web/pom.xml	2011-11-09 15:54:16 UTC (rev 132)
@@ -32,6 +32,12 @@
       <scope>provided</scope>
     </dependency>
=20
+    <dependency>
+      <groupId>org.nuiton.topia</groupId>
+      <artifactId>topia-persistence</artifactId>
+      <scope>provided</scope>
+    </dependency>
+
   </dependencies>
=20
=20

Added: trunk/nuiton-web/src/main/java/org/nuiton/web/filter/TopiaTransactionF=
ilter.java
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=
=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D=3D
--- trunk/nuiton-web/src/main/java/org/nuiton/web/filter/TopiaTransactionFilt=
er.java	                        (rev 0)
+++ trunk/nuiton-web/src/main/java/org/nuiton/web/filter/TopiaTransactionFilt=
er.java	2011-11-09 15:54:16 UTC (rev 132)
@@ -0,0 +1,349 @@
+/*
+ * #%L
+ * Nuiton Web :: Nuiton Web
+ *=20
+ * $Id$
+ * $HeadURL$
+ * %%
+ * Copyright (C) 2011 CodeLutin
+ * %%
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as=20
+ * published by the Free Software Foundation, either version 3 of the=20
+ * License, or (at your option) any later version.
+ *=20
+ * 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.
+ *=20
+ * You should have received a copy of the GNU General Lesser Public=20
+ * License along with this program.  If not, see
+ * <http://www.gnu.org/licenses/lgpl-3.0.html>.
+ * #L%
+ */
+package org.nuiton.web.filter;
+
+import org.apache.commons.lang.StringUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.nuiton.topia.TopiaContext;
+import org.nuiton.topia.TopiaException;
+import org.nuiton.topia.TopiaRuntimeException;
+import org.nuiton.topia.framework.TopiaContextImplementor;
+
+import javax.servlet.Filter;
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import java.io.IOException;
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * <h2>Purpose of this filter</h2>
+ * This filter purpose is to inject in the request a transaction from
+ * {@link TopiaContext} and deal with the complete lifecycle of a topia
+ * transaction while a request.
+ * <p/>
+ * The injected transaction will be closed (if was really opened) at the end=
 of
+ * the request.
+ * <h2>Configuration of the filter</h2>
+ * The filter accepts two configuration parameters:
+ * <ul>
+ * <li>{@code excludeMethods}: This parameters configure a set of method nam=
es
+ * which should never be called on the proxied transaction.
+ * When a such method is called on the transaction then the filter will pass=
 in
+ * the hook {@link #onExcludeMethod(Object, Method, Object[])}.
+ * <p/>
+ * Default implementation of this hook is to throw an exception.
+ * </li>
+ * <li>{@code unusedMethods}: This parameters configure a set of method names
+ * which should be by-pass when the proxied transaction was not still open (=
via a {@link TopiaContext#beginTransaction()}.
+ * When a such method is called on the transaction then the filter will pass=
 in
+ * the hook {@link #onUnusedMethod(Object, Method, Object[])}.
+ * <p/>
+ * Default implementation of this hook is to not return null values.
+ * </li>
+ * </ul>
+ * <h2>Obtain the transaction</h2>
+ * The (proxied) transaction is pushed as an attribute in the servlet reques=
t.
+ * <p/>
+ * The attribute name is {@link #TOPIA_TRANSACTION_REQUEST_ATTRIBUTE}.
+ * <p/>
+ * A convience method is created here to obtain the transaction  {@link #get=
Transaction(ServletRequest)} :
+ * <pre>
+ * TopiaContext tx =3D TopiaTransactionFilter.getTransaction(ServletRequest);
+ * </pre>
+ * <p/>
+ * If you prefer to not use this nice method, you can also do this:
+ * <pre>
+ * TopiaContext tx =3D (TopiaContext) request.getAttribute(TopiaTransactionF=
ilter#TOPIA_TRANSACTION_REQUEST_ATTRIBUTE);
+ * </pre>
+ *
+ * @author tchemit <chemit@codelutin.com>
+ * @since 1.6
+ */
+public abstract class TopiaTransactionFilter implements Filter {
+
+    public static final String TOPIA_TRANSACTION_REQUEST_ATTRIBUTE =3D
+            "topiaTransaction";
+
+    public static final String[] DEFAULT_EXCLUDE_METHODS =3D {
+            "beginTransaction",
+            "closeContext",
+            "clear"
+    };
+
+    public static final String[] DEFAULT_UNUSED_METHODS =3D {
+            "toString",
+            "isClosed",
+            "closeContext",
+            "clear",
+            "equals",
+            "hashCode",
+            "finalize",
+            "getClass"
+    };
+
+    /** Logger. */
+    private static final Log log =3D
+            LogFactory.getLog(TopiaTransactionFilter.class);
+
+    /** names of methods to forbid access while using proxy. */
+    protected Set<String> excludeMethods;
+
+    /** names of methods to by-pass if no transaction opened on proxy. */
+    protected Set<String> unusedMethods;
+
+    public Set<String> getExcludeMethods() {
+        return excludeMethods;
+    }
+
+    public Set<String> getUnusedMethods() {
+        return unusedMethods;
+    }
+
+    public static TopiaContext getTransaction(ServletRequest request) {
+        TopiaContext topiaContext =3D (TopiaContext)
+                request.getAttribute(TOPIA_TRANSACTION_REQUEST_ATTRIBUTE);
+        return topiaContext;
+    }
+
+    /**
+     * Method to open a new transaction.
+     *
+     * @return the new freshly opened transaction
+     * @throws TopiaRuntimeException if any problem while opening a new tran=
saction
+     */
+    protected abstract TopiaContext beginTransaction() throws TopiaRuntimeEx=
ception;
+
+    @Override
+    public void destroy() {
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+        String methodsFromConfig;
+
+        methodsFromConfig =3D filterConfig.getInitParameter("execludeMethods=
");
+
+        String[] methods;
+        if (StringUtils.isNotEmpty(methodsFromConfig)) {
+            methods =3D methodsFromConfig.split(",");
+        } else {
+            methods =3D DEFAULT_EXCLUDE_METHODS;
+        }
+        excludeMethods =3D new HashSet<String>(Arrays.asList(methods));
+
+        methodsFromConfig =3D
+                filterConfig.getInitParameter("unusedMethods ");
+
+        if (StringUtils.isNotEmpty(methodsFromConfig)) {
+            methods =3D methodsFromConfig.split(",");
+        } else {
+            methods =3D DEFAULT_UNUSED_METHODS;
+        }
+        unusedMethods =3D new HashSet<String>(Arrays.asList(methods));
+    }
+
+    @Override
+    public void doFilter(ServletRequest request,
+                         ServletResponse response,
+                         FilterChain chain) throws IOException, ServletExcep=
tion {
+
+        // creates a proxy of a lazy transaction
+
+        TopiaTransactionProxyInvocationHandler proxyInvocationHandler =3D
+                new TopiaTransactionProxyInvocationHandler();
+
+        TopiaContext proxy =3D (TopiaContext) Proxy.newProxyInstance(
+                getClass().getClassLoader(),
+                new Class<?>[]{TopiaContext.class,
+                               TopiaContextImplementor.class},
+                proxyInvocationHandler
+        );
+        request.setAttribute(TOPIA_TRANSACTION_REQUEST_ATTRIBUTE, proxy);
+        try {
+            chain.doFilter(request, response);
+        } finally {
+            proxyInvocationHandler.closeTransaction();
+        }
+    }
+
+    /**
+     * Hook method called when a method with his name in
+     * {@link #excludeMethods} was invoked on the proxied transaction.
+     *
+     * @param proxy  proxied transaction
+     * @param method method invoked
+     * @param args   arguments of the invoked method
+     * @return the return code of the method
+     * @throws Throwable if any error to do.
+     */
+    protected Object onExcludeMethod(Object proxy,
+                                     Method method,
+                                     Object[] args) throws Throwable {
+
+        // not authorized
+        throw new IllegalAccessException(
+                "Not allowed to access method " + method.getName() + " on " +
+                proxy);
+    }
+
+    /**
+     * Hook method called when a method with his name in
+     * {@link #unusedMethods} was invoked on the proxied transaction
+     * while the underlying transaction is still not opened.
+     *
+     * @param proxy  proxied transaction
+     * @param method method invoked
+     * @param args   arguments of the invoked method
+     * @return the return code of the method
+     * @throws Throwable if any error to do.
+     */
+    protected Object onUnusedMethod(Object proxy,
+                                    Method method,
+                                    Object[] args) throws Throwable {
+
+        // by-pass method since no transaction found
+
+        String methodName =3D method.getName();
+        if (log.isDebugEnabled()) {
+            log.debug("Skip execution of method " + methodName +
+                      " since no transaction is instanciated.");
+        }
+
+        Set<String> methods =3D getUnusedMethods();
+        if (methods.contains("toString")) {
+
+            return "No transaction opened for proxy " + proxy;
+        }
+
+        if (methods.contains("isClosed")) {
+
+            return false;
+        }
+
+        if (methods.contains("equals")) {
+
+            return false;
+        }
+
+        if (methods.contains("hashCode")) {
+
+            return 0;
+        }
+
+        if (methods.contains("getClass")) {
+
+            return TopiaContext.class;
+        }
+
+        return null;
+    }
+
+    /**
+     * Handler of a proxy on a {@link TopiaContext}.
+     *
+     * @see #excludeMethods
+     */
+    public class TopiaTransactionProxyInvocationHandler implements Invocatio=
nHandler {
+
+        /** Target to use for the proxy. */
+        protected TopiaContext transaction;
+
+        @Override
+        public Object invoke(Object proxy,
+                             Method method,
+                             Object[] args) throws Throwable {
+
+            String methodName =3D method.getName();
+
+            if (getExcludeMethods().contains(methodName)) {
+
+                Object result =3D onExcludeMethod(proxy, method, args);
+                return result;
+            }
+
+            if (transaction =3D=3D null) {
+
+                if (log.isTraceEnabled()) {
+                    log.trace("transaction started due to a call to " + meth=
odName);
+                }
+
+                if (getUnusedMethods().contains(methodName)) {
+
+                    Object result =3D onUnusedMethod(proxy, method, args);
+                    return result;
+                }
+
+                // first time transaction is required, create its
+                transaction =3D beginTransaction();
+
+                if (log.isDebugEnabled()) {
+                    log.debug("Open transaction " + transaction);
+                }
+            }
+
+            // can invoke the method on the transaction
+            try {
+                Object result =3D method.invoke(transaction, args);
+                return result;
+            } catch (Exception eee) {
+                if (log.isErrorEnabled()) {
+                    log.error("Could not execute method " + method.getName()=
, eee);
+                }
+                throw eee;
+            }
+        }
+
+        protected void closeTransaction() {
+            if (transaction =3D=3D null) {
+                if (log.isTraceEnabled()) {
+                    log.trace("no transaction to close");
+                }
+            } else if (transaction.isClosed()) {
+                if (log.isTraceEnabled()) {
+                    log.trace("transaction " + transaction + " is already cl=
osed");
+                }
+            } else {
+                if (log.isDebugEnabled()) {
+                    log.debug("closing transaction " + transaction);
+                }
+                try {
+                    transaction.closeContext();
+                } catch (TopiaException e) {
+                    throw new TopiaRuntimeException(e);
+                }
+            }
+        }
+    }
+
+}


Property changes on: trunk/nuiton-web/src/main/java/org/nuiton/web/filter/Top=
iaTransactionFilter.java
___________________________________________________________________
Added: svn:keywords
   + Author Date Id Revision HeadURL
Added: svn:eol-style
   + native


--===============8534101860566310963==--


