/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library 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 2.1 of the License, or (at your option) * any later version. * * This library 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 Lesser General Public License for more * details. */ package com.liferay.portal.spring.transaction; import com.liferay.portal.kernel.test.rule.CodeCoverageAssertor; import com.liferay.portal.kernel.util.ProxyUtil; import com.liferay.portal.kernel.util.ReflectionUtil; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Arrays; import java.util.concurrent.Callable; import java.util.function.Predicate; import org.aopalliance.intercept.MethodInvocation; import org.junit.Assert; import org.junit.ClassRule; import org.junit.Test; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.TransactionDefinition; import org.springframework.transaction.TransactionStatus; import org.springframework.transaction.interceptor.TransactionAttribute; /** * @author Shuyang Zhou */ public class CounterTransactionExecutorTest { @ClassRule public static final CodeCoverageAssertor codeCoverageAssertor = CodeCoverageAssertor.INSTANCE; @Test public void testCommit() throws Throwable { RecordPlatformTransactionManager recordPlatformTransactionManager = new RecordPlatformTransactionManager(_transactionStatus); TransactionAttributeAdapter transactionAttributeAdapter = _newTransactionAttributeAdapter((t) -> false); _transactionExecutor.execute( recordPlatformTransactionManager, transactionAttributeAdapter, _newMethodInvocation(() -> null)); recordPlatformTransactionManager.verify( transactionAttributeAdapter, _transactionStatus, null); } @Test public void testCommitWithAppException() throws Throwable { RecordPlatformTransactionManager recordPlatformTransactionManager = new RecordPlatformTransactionManager(_transactionStatus); TransactionAttributeAdapter transactionAttributeAdapter = _newTransactionAttributeAdapter((t) -> false); try { _transactionExecutor.execute( recordPlatformTransactionManager, transactionAttributeAdapter, _newMethodInvocation( () -> { throw appException; })); Assert.fail(); } catch (Throwable t) { Assert.assertSame(appException, t); } recordPlatformTransactionManager.verify( transactionAttributeAdapter, _transactionStatus, null); } @Test public void testCommitWithAppExceptionWithCommitException() throws Throwable { RecordPlatformTransactionManager recordPlatformTransactionManager = new RecordPlatformTransactionManager(_transactionStatus) { @Override public void commit(TransactionStatus transactionStatus) { ReflectionUtil.throwException(commitException); } }; TransactionAttributeAdapter transactionAttributeAdapter = _newTransactionAttributeAdapter((t) -> false); try { _transactionExecutor.execute( recordPlatformTransactionManager, transactionAttributeAdapter, _newMethodInvocation( () -> { throw appException; })); Assert.fail(); } catch (Throwable t) { Assert.assertSame(commitException, t); Throwable[] throwables = commitException.getSuppressed(); Assert.assertEquals( Arrays.toString(throwables), 1, throwables.length); Assert.assertEquals(appException, throwables[0]); } recordPlatformTransactionManager.verify( transactionAttributeAdapter, null, null); } @Test public void testCommitWithCommitException() throws Throwable { RecordPlatformTransactionManager recordPlatformTransactionManager = new RecordPlatformTransactionManager(_transactionStatus) { @Override public void commit(TransactionStatus transactionStatus) { ReflectionUtil.throwException(commitException); } }; TransactionAttributeAdapter transactionAttributeAdapter = _newTransactionAttributeAdapter((t) -> false); try { _transactionExecutor.execute( recordPlatformTransactionManager, transactionAttributeAdapter, _newMethodInvocation(() -> null)); Assert.fail(); } catch (Throwable t) { Assert.assertSame(commitException, t); } recordPlatformTransactionManager.verify( transactionAttributeAdapter, null, null); } @Test public void testRollbackOnAppException() throws Throwable { RecordPlatformTransactionManager recordPlatformTransactionManager = new RecordPlatformTransactionManager(_transactionStatus); TransactionAttributeAdapter transactionAttributeAdapter = _newTransactionAttributeAdapter((t) -> t == appException); try { _transactionExecutor.execute( recordPlatformTransactionManager, transactionAttributeAdapter, _newMethodInvocation( () -> { throw appException; })); Assert.fail(); } catch (Throwable t) { Assert.assertSame(appException, t); } recordPlatformTransactionManager.verify( transactionAttributeAdapter, null, _transactionStatus); } @Test public void testRollbackOnAppExceptionWithRollbackException() throws Throwable { RecordPlatformTransactionManager recordPlatformTransactionManager = new RecordPlatformTransactionManager(_transactionStatus) { @Override public void rollback(TransactionStatus transactionStatus) { ReflectionUtil.throwException(rollbackException); } }; TransactionAttributeAdapter transactionAttributeAdapter = _newTransactionAttributeAdapter((t) -> t == appException); try { _transactionExecutor.execute( recordPlatformTransactionManager, transactionAttributeAdapter, _newMethodInvocation( () -> { throw appException; })); Assert.fail(); } catch (Throwable t) { Assert.assertSame(rollbackException, t); Throwable[] throwables = rollbackException.getSuppressed(); Assert.assertEquals( Arrays.toString(throwables), 1, throwables.length); Assert.assertEquals(appException, throwables[0]); } recordPlatformTransactionManager.verify( transactionAttributeAdapter, null, null); } protected TransactionExecutor createTransactionExecutor() { return new CounterTransactionExecutor(); } protected final Exception appException = new Exception(); protected final Exception commitException = new Exception(); protected final Exception rollbackException = new Exception(); private static MethodInvocation _newMethodInvocation(Callable<?> callable) { return (MethodInvocation)ProxyUtil.newProxyInstance( MethodInvocation.class.getClassLoader(), new Class<?>[] {MethodInvocation.class}, new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Exception { if ("proceed".equals(method.getName())) { return callable.call(); } throw new UnsupportedOperationException(method.toString()); } }); } private static TransactionAttributeAdapter _newTransactionAttributeAdapter( Predicate<Throwable> predicate) { return new TransactionAttributeAdapter( (TransactionAttribute)ProxyUtil.newProxyInstance( TransactionAttribute.class.getClassLoader(), new Class<?>[] {TransactionAttribute.class}, new InvocationHandler() { @Override public Object invoke( Object proxy, Method method, Object[] args) { if ("rollbackOn".equals(method.getName())) { return predicate.test((Throwable)args[0]); } throw new UnsupportedOperationException( method.toString()); } })); } private final TransactionExecutor _transactionExecutor = createTransactionExecutor(); private final TransactionStatus _transactionStatus = (TransactionStatus)ProxyUtil.newProxyInstance( TransactionStatus.class.getClassLoader(), new Class<?>[] {TransactionStatus.class}, new InvocationHandler() { @Override public Object invoke( Object proxy, Method method, Object[] args) { throw new UnsupportedOperationException(method.toString()); } }); private class RecordPlatformTransactionManager implements PlatformTransactionManager { public RecordPlatformTransactionManager( TransactionStatus transactionStatus) { _transactionStatus = transactionStatus; } @Override public void commit(TransactionStatus transactionStatus) { _commitTransactionStatus = transactionStatus; } @Override public TransactionStatus getTransaction( TransactionDefinition transactionDefinition) { _transactionDefinition = transactionDefinition; return _transactionStatus; } @Override public void rollback(TransactionStatus transactionStatus) { _rollbackTransactionStatus = transactionStatus; } public void verify( TransactionDefinition transactionDefinition, TransactionStatus commitTransactionStatus, TransactionStatus rollbackTransactionStatus) { Assert.assertSame(transactionDefinition, _transactionDefinition); Assert.assertSame( commitTransactionStatus, _commitTransactionStatus); Assert.assertSame( rollbackTransactionStatus, _rollbackTransactionStatus); } private TransactionStatus _commitTransactionStatus; private TransactionStatus _rollbackTransactionStatus; private TransactionDefinition _transactionDefinition; private final TransactionStatus _transactionStatus; } }