This is an automated email from the ASF dual-hosted git repository. ningjiang pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/incubator-servicecomb-saga.git
commit 91634874da8703203476db725d90cd8b97c0e687 Author: cherrylzhao <[email protected]> AuthorDate: Fri Oct 19 18:00:10 2018 +0800 SCB-963 Make TCC coordinate method transactional. --- .../saga/omega/context/CallbackContext.java | 4 +- .../transaction/spring/MethodCheckingCallback.java | 46 +++++++++++++++++++++- .../spring/ParticipateAnnotationProcessor.java | 4 +- .../transaction/spring/TccInterceptorTest.java | 7 +--- .../omega/transaction/spring/TccUserService.java | 23 +++++------ .../spring/TransactionalUserService.java | 12 +++--- 6 files changed, 69 insertions(+), 27 deletions(-) diff --git a/omega/omega-context/src/main/java/org/apache/servicecomb/saga/omega/context/CallbackContext.java b/omega/omega-context/src/main/java/org/apache/servicecomb/saga/omega/context/CallbackContext.java index 0fe2613..cf5ab50 100644 --- a/omega/omega-context/src/main/java/org/apache/servicecomb/saga/omega/context/CallbackContext.java +++ b/omega/omega-context/src/main/java/org/apache/servicecomb/saga/omega/context/CallbackContext.java @@ -35,9 +35,9 @@ public class CallbackContext { this.omegaContext = omegaContext; } - public void addCallbackContext(Method compensationMethod, Object target) { + public void addCallbackContext(String key, Method compensationMethod, Object target) { compensationMethod.setAccessible(true); - contexts.put(compensationMethod.toString(), new CallbackContextInternal(target, compensationMethod)); + contexts.put(key, new CallbackContextInternal(target, compensationMethod)); } public void apply(String globalTxId, String localTxId, String callbackMethod, Object... payloads) { diff --git a/omega/omega-spring-tx/src/main/java/org/apache/servicecomb/saga/omega/transaction/spring/MethodCheckingCallback.java b/omega/omega-spring-tx/src/main/java/org/apache/servicecomb/saga/omega/transaction/spring/MethodCheckingCallback.java index 8a2ac13..6c52313 100644 --- a/omega/omega-spring-tx/src/main/java/org/apache/servicecomb/saga/omega/transaction/spring/MethodCheckingCallback.java +++ b/omega/omega-spring-tx/src/main/java/org/apache/servicecomb/saga/omega/transaction/spring/MethodCheckingCallback.java @@ -18,11 +18,15 @@ package org.apache.servicecomb.saga.omega.transaction.spring; import java.lang.invoke.MethodHandles; +import java.lang.reflect.Field; import java.lang.reflect.Method; import org.apache.servicecomb.saga.omega.context.CallbackContext; import org.apache.servicecomb.saga.omega.transaction.OmegaException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import org.springframework.aop.framework.AdvisedSupport; +import org.springframework.aop.framework.AopProxy; +import org.springframework.aop.support.AopUtils; import org.springframework.util.ReflectionUtils.MethodCallback; public abstract class MethodCheckingCallback implements MethodCallback { @@ -45,12 +49,50 @@ public abstract class MethodCheckingCallback implements MethodCallback { for (String each : candidates) { try { Method signature = bean.getClass().getDeclaredMethod(each, method.getParameterTypes()); - callbackContext.addCallbackContext(signature, bean); + String key = getTargetBean(bean).getClass().getDeclaredMethod(each, method.getParameterTypes()).toString(); + callbackContext.addCallbackContext(key, signature, bean); LOG.debug("Found callback method [{}] in {}", each, bean.getClass().getCanonicalName()); - } catch (NoSuchMethodException ex) { + } catch (Exception ex) { throw new OmegaException( "No such " + callbackType + " method [" + each + "] found in " + bean.getClass().getCanonicalName(), ex); } } } + + private Object getTargetBean(Object proxy) throws Exception { + if(!AopUtils.isAopProxy(proxy)) { + return proxy; + } + + if(AopUtils.isJdkDynamicProxy(proxy)) { + return getJdkDynamicProxyTargetObject(proxy); + } else { + return getCglibProxyTargetObject(proxy); + } + } + + private Object getCglibProxyTargetObject(Object proxy) throws Exception { + Field h = proxy.getClass().getDeclaredField("CGLIB$CALLBACK_0"); + h.setAccessible(true); + Object dynamicAdvisedInterceptor = h.get(proxy); + + Field advised = dynamicAdvisedInterceptor.getClass().getDeclaredField("advised"); + advised.setAccessible(true); + + Object result = ((AdvisedSupport)advised.get(dynamicAdvisedInterceptor)).getTargetSource().getTarget(); + return result; + } + + + private Object getJdkDynamicProxyTargetObject(Object proxy) throws Exception { + Field h = proxy.getClass().getSuperclass().getDeclaredField("h"); + h.setAccessible(true); + AopProxy aopProxy = (AopProxy) h.get(proxy); + + Field advised = aopProxy.getClass().getDeclaredField("advised"); + advised.setAccessible(true); + + Object result = ((AdvisedSupport)advised.get(aopProxy)).getTargetSource().getTarget(); + return result; + } } diff --git a/omega/omega-spring-tx/src/main/java/org/apache/servicecomb/saga/omega/transaction/spring/ParticipateAnnotationProcessor.java b/omega/omega-spring-tx/src/main/java/org/apache/servicecomb/saga/omega/transaction/spring/ParticipateAnnotationProcessor.java index 5cef9ff..80583bd 100644 --- a/omega/omega-spring-tx/src/main/java/org/apache/servicecomb/saga/omega/transaction/spring/ParticipateAnnotationProcessor.java +++ b/omega/omega-spring-tx/src/main/java/org/apache/servicecomb/saga/omega/transaction/spring/ParticipateAnnotationProcessor.java @@ -36,13 +36,13 @@ class ParticipateAnnotationProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - checkMethod(bean); - checkFields(bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { + checkMethod(bean); + checkFields(bean); return bean; } diff --git a/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TccInterceptorTest.java b/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TccInterceptorTest.java index bc9148b..7f43476 100644 --- a/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TccInterceptorTest.java +++ b/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TccInterceptorTest.java @@ -69,9 +69,6 @@ public class TccInterceptorTest { private List<String> messages; @Autowired - private OmegaContext omegaContext; - - @Autowired private TccUserServiceMain tccUserServiceMain; @Autowired @@ -95,7 +92,6 @@ public class TccInterceptorTest { public void tearDown() throws Exception { messages.clear(); userRepository.deleteAll(); - omegaContext.clear(); } @AfterClass @@ -152,7 +148,8 @@ public class TccInterceptorTest { ); User result = userRepository.findByUsername(user.username()); - assertThat(result, is(nullValue())); + assertThat(result.username(), is(user.username())); + assertThat(result.email(), is(user.email())); result = userRepository.findByUsername(jack.username()); assertThat(result, is(nullValue())); diff --git a/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TccUserService.java b/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TccUserService.java index 8223a87..31da4c9 100644 --- a/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TccUserService.java +++ b/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TccUserService.java @@ -20,26 +20,28 @@ package org.apache.servicecomb.saga.omega.transaction.spring; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; -import java.util.HashMap; -import java.util.Map; - -import org.apache.servicecomb.saga.omega.context.OmegaContext; +import javax.annotation.Resource; import org.apache.servicecomb.saga.omega.transaction.annotations.Participate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component -class TccUserService { +@Transactional +public class TccUserService { static final String ILLEGAL_USER = "Illegal User"; - private final UserRepository userRepository; + @Autowired TccUserService(UserRepository userRepository) { this.userRepository = userRepository; } + @Resource + private UserRepository userRepository; + @Participate(confirmMethod = "confirm", cancelMethod = "cancel") - User add(User user) { + public User add(User user) { // Only for the validation check if (ILLEGAL_USER.equals(user.username())) { throw new IllegalArgumentException("User is illegal"); @@ -47,15 +49,14 @@ class TccUserService { return userRepository.save(user); } - void confirm(User user) { + public void confirm(User user) { User result = userRepository.findByUsername(user.username()); // Just make sure we can get the resource and keep doing other business assertThat(result, is(user)); } - void cancel(User user) { + public void cancel(User user) { userRepository.delete(user); + throw new RuntimeException("transaction test"); } - - } diff --git a/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TransactionalUserService.java b/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TransactionalUserService.java index 0618109..11bc437 100644 --- a/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TransactionalUserService.java +++ b/omega/omega-spring-tx/src/test/java/org/apache/servicecomb/saga/omega/transaction/spring/TransactionalUserService.java @@ -20,9 +20,11 @@ package org.apache.servicecomb.saga.omega.transaction.spring; import org.apache.servicecomb.saga.omega.transaction.annotations.Compensable; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; @Component -class TransactionalUserService { +@Transactional +public class TransactionalUserService { static final String ILLEGAL_USER = "Illegal User"; private final UserRepository userRepository; @@ -38,19 +40,19 @@ class TransactionalUserService { } @Compensable(compensationMethod = "delete") - User add(User user) { + public User add(User user) { if (ILLEGAL_USER.equals(user.username())) { throw new IllegalArgumentException("User is illegal"); } return userRepository.save(user); } - void delete(User user) { + public void delete(User user) { userRepository.delete(user); } @Compensable(retries = 2, compensationMethod = "delete") - User add(User user, int count) { + public User add(User user, int count) { if (this.count < count) { this.count += 1; throw new IllegalStateException("Retry harder"); @@ -59,7 +61,7 @@ class TransactionalUserService { return userRepository.save(user); } - void delete(User user, int count) { + public void delete(User user, int count) { resetCount(); userRepository.delete(user); }
