From 3a24beecf3bcfccc98c571b5ff4e4e2271e70f83 Mon Sep 17 00:00:00 2001 From: Oliver Gierke Date: Wed, 14 Dec 2016 14:58:30 +0100 Subject: [PATCH] DATACMNS-959 - RepositoryFactorySupport now registers interceptor to detect transactions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The interceptor discovers whether a transaction was running before the call entered the repository proxy and exposes that fact via ….isSurroundingTransactionActive() for donwstream usage. Related ticket: DATAJPA-1023. --- .../support/RepositoryFactorySupport.java | 1 + ...gTransactionDetectorMethodInterceptor.java | 62 +++++++++++++++ ...ionDetectorMethodInterceptorUnitTests.java | 76 +++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 src/main/java/org/springframework/data/repository/core/support/SurroundingTransactionDetectorMethodInterceptor.java create mode 100644 src/test/java/org/springframework/data/repository/core/support/SurroundingTransactionDetectorMethodInterceptorUnitTests.java diff --git a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java index a8c3d6f6a..d2917e0dc 100644 --- a/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java +++ b/src/main/java/org/springframework/data/repository/core/support/RepositoryFactorySupport.java @@ -203,6 +203,7 @@ public abstract class RepositoryFactorySupport implements BeanClassLoaderAware, result.setTarget(target); result.setInterfaces(new Class[] { repositoryInterface, Repository.class }); + result.addAdvice(SurroundingTransactionDetectorMethodInterceptor.INSTANCE); result.addAdvisor(ExposeInvocationInterceptor.ADVISOR); if (TRANSACTION_PROXY_TYPE != null) { diff --git a/src/main/java/org/springframework/data/repository/core/support/SurroundingTransactionDetectorMethodInterceptor.java b/src/main/java/org/springframework/data/repository/core/support/SurroundingTransactionDetectorMethodInterceptor.java new file mode 100644 index 000000000..081ed5600 --- /dev/null +++ b/src/main/java/org/springframework/data/repository/core/support/SurroundingTransactionDetectorMethodInterceptor.java @@ -0,0 +1,62 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.repository.core.support; + +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * {@link MethodInterceptor} detecting whether a transaction is already running and exposing that fact via + * {@link #isSurroundingTransactionActive()}. Useful in case subsequent interceptors might create transactions + * themselves but downstream components have to find out whether there was one running before the call entered the + * proxy. + * + * @author Oliver Gierke + * @since 1.13 + * @soundtrack Hendrik Freischlader Trio - Openness (Openness) + */ +public enum SurroundingTransactionDetectorMethodInterceptor implements MethodInterceptor { + + INSTANCE; + + private final ThreadLocal SURROUNDING_TX_ACTIVE = new ThreadLocal(); + + /** + * Returns whether a transaction was active before the method call entered the repository proxy. + * + * @return + */ + public boolean isSurroundingTransactionActive() { + return Boolean.TRUE == SURROUNDING_TX_ACTIVE.get(); + } + + /* + * (non-Javadoc) + * @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation) + */ + @Override + public Object invoke(MethodInvocation invocation) throws Throwable { + + SURROUNDING_TX_ACTIVE.set(TransactionSynchronizationManager.isActualTransactionActive()); + + try { + return invocation.proceed(); + } finally { + SURROUNDING_TX_ACTIVE.remove(); + } + } +} diff --git a/src/test/java/org/springframework/data/repository/core/support/SurroundingTransactionDetectorMethodInterceptorUnitTests.java b/src/test/java/org/springframework/data/repository/core/support/SurroundingTransactionDetectorMethodInterceptorUnitTests.java new file mode 100644 index 000000000..0f9c83ecf --- /dev/null +++ b/src/test/java/org/springframework/data/repository/core/support/SurroundingTransactionDetectorMethodInterceptorUnitTests.java @@ -0,0 +1,76 @@ +/* + * Copyright 2016 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.data.repository.core.support; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.*; +import static org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.*; + +import org.junit.Test; +import org.springframework.aop.framework.ReflectiveMethodInvocation; +import org.springframework.transaction.support.TransactionSynchronizationManager; + +/** + * Unit tests for {@link SurroundingTransactionDetectorMethodInterceptor}. + * + * @author Oliver Gierke + * @soundtrack Hendrik Freischlader Trio - Openness (Openness) + */ +public class SurroundingTransactionDetectorMethodInterceptorUnitTests { + + /** + * @see DATACMNS-959 + */ + @Test + public void registersActiveSurroundingTransaction() throws Throwable { + + TransactionSynchronizationManager.setActualTransactionActive(true); + + INSTANCE.invoke(new StubMethodInvocation(true)); + } + + /** + * @see DATACMNS-959 + */ + @Test + public void registersNoSurroundingTransaction() throws Throwable { + + TransactionSynchronizationManager.setActualTransactionActive(false); + + INSTANCE.invoke(new StubMethodInvocation(false)); + } + + static class StubMethodInvocation extends ReflectiveMethodInvocation { + + boolean transactionActive; + + StubMethodInvocation(boolean expectTransactionActive) throws Exception { + super(null, null, Object.class.getMethod("toString"), null, null, null); + this.transactionActive = expectTransactionActive; + } + + /* + * (non-Javadoc) + * @see org.aopalliance.intercept.Joinpoint#proceed() + */ + public Object proceed() throws Throwable { + + assertThat(INSTANCE.isSurroundingTransactionActive(), is(transactionActive)); + + return null; + } + } +}