From 89d29675341628859ab0aa4bccf489fd77e64b2a Mon Sep 17 00:00:00 2001 From: InnerPeace <1260073925@qq.com> Date: Thu, 11 Apr 2024 00:20:14 +0800 Subject: [PATCH 1/3] bugfix: fix the failure of @BusinessActionContextParamete annotation to set parameters into io.seata.rm.tcc.api.BusinessActionContext. A interface instead of its implementation class should be passed to io.seata.integration.tx.api.interceptor.ActionInterceptorHandler#fetchActionRequestContext to get parameters noted by "@BusinessActionContextParameter". #6473 --- changes/en-us/2.x.md | 1 + changes/zh-cn/2.x.md | 1 + .../tcc/TccActionInterceptorHandlerTest.java | 59 +++++++++++++++++++ .../TccActionInterceptorHandler.java | 41 +++++++++---- 4 files changed, 90 insertions(+), 12 deletions(-) create mode 100644 spring/src/test/java/org/apache/seata/spring/tcc/TccActionInterceptorHandlerTest.java diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md index be4b6ce8079..552192eb1d2 100644 --- a/changes/en-us/2.x.md +++ b/changes/en-us/2.x.md @@ -34,6 +34,7 @@ Add changes here for all PR submitted to the 2.x branch. - [[#6372](https://github.com/apache/incubator-seata/pull/6372)] fix initializing the sql file postgresql.sql index name conflict - [[#6380](https://github.com/apache/incubator-seata/pull/6380)] fix sql exception when checking for the existence of the UNDO_LOG table on SQL server - [[#6385](https://github.com/apache/incubator-seata/pull/6385)] fix the bug where Role.participant does not execute hooks but clears them. +- [[#6475](https://github.com/apache/incubator-seata/pull/6475)] fix the failure of @BusinessActionContextParamete annotation to set parameters into io.seata.rm.tcc.api.BusinessActionContext at TCC mode. ### optimize: - [[#6031](https://github.com/apache/incubator-seata/pull/6031)] add a check for the existence of the undolog table diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md index 61229c5ce66..7fbf2339e1f 100644 --- a/changes/zh-cn/2.x.md +++ b/changes/zh-cn/2.x.md @@ -34,6 +34,7 @@ - [[#6372](https://github.com/apache/incubator-seata/pull/6372)] 修复初始化sql文件postgresql.sql 索引名称冲突问题 - [[#6380](https://github.com/apache/incubator-seata/pull/6380)] 修复针对sql server检查UNDO_LOG表是否存在时的SQL异常 - [[#6385](https://github.com/apache/incubator-seata/pull/6385)] 修复Role.Participant不执行hook但会清理的问题 +- [[#6475](https://github.com/apache/incubator-seata/pull/6475)] 修复TCC模式@BusinessActionContextParamete注解无法将参数设置到BusinessActionContext的问题 diff --git a/spring/src/test/java/org/apache/seata/spring/tcc/TccActionInterceptorHandlerTest.java b/spring/src/test/java/org/apache/seata/spring/tcc/TccActionInterceptorHandlerTest.java new file mode 100644 index 00000000000..8c0b6c60c32 --- /dev/null +++ b/spring/src/test/java/org/apache/seata/spring/tcc/TccActionInterceptorHandlerTest.java @@ -0,0 +1,59 @@ +package org.apache.seata.spring.tcc; + +import org.aopalliance.intercept.MethodInvocation; +import org.apache.seata.integration.tx.api.interceptor.InvocationWrapper; +import org.apache.seata.rm.tcc.api.BusinessActionContext; +import org.apache.seata.rm.tcc.api.TwoPhaseBusinessAction; +import org.apache.seata.rm.tcc.interceptor.TccActionInterceptorHandler; +import org.apache.seata.spring.annotation.AdapterInvocationWrapper; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.HashSet; + +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + + +class TccActionInterceptorHandlerTest { + + protected TccActionInterceptorHandler tccActionInterceptorHandler = new TccActionInterceptorHandler( + null, + new HashSet() {{ + add("prepare"); + }} + ); + + /** + * Test method "parseAnnotation" of TccActionInterceptorHandler + * + * @throws Throwable + */ + @Test + void testParseAnnotation() throws Throwable { + // mock MethodInvocation + NormalTccActionImpl tccAction = new NormalTccActionImpl(); + Method classMethod = NormalTccActionImpl.class.getMethod("prepare", BusinessActionContext.class); + MethodInvocation mockInvocation = mock(MethodInvocation.class); + when(mockInvocation.getMethod()).thenReturn(classMethod); + when(mockInvocation.getArguments()).thenReturn(new Object[]{new BusinessActionContext()}); + when(mockInvocation.proceed()).thenAnswer(invocation -> classMethod.invoke(tccAction, mockInvocation.getArguments())); + + // mock AdapterInvocationWrapper + AdapterInvocationWrapper invocationWrapper = new AdapterInvocationWrapper(mockInvocation); + when(invocationWrapper.getTarget()).thenReturn(tccAction); + + // invoke private method "parseAnnotation" of TccActionInterceptorHandler + Method method = TccActionInterceptorHandler.class.getDeclaredMethod("parseAnnotation", InvocationWrapper.class); + method.setAccessible(true); + Object[] results = (Object[]) method.invoke(tccActionInterceptorHandler, invocationWrapper); + System.out.println(results); + + // test results + Method interfaceMethod = NormalTccAction.class.getMethod("prepare", BusinessActionContext.class); + Assertions.assertEquals(interfaceMethod, results[0]); + Assertions.assertEquals(true, results[1] instanceof TwoPhaseBusinessAction); + + } +} diff --git a/tcc/src/main/java/org/apache/seata/rm/tcc/interceptor/TccActionInterceptorHandler.java b/tcc/src/main/java/org/apache/seata/rm/tcc/interceptor/TccActionInterceptorHandler.java index acf84663c1d..e79515a38f6 100644 --- a/tcc/src/main/java/org/apache/seata/rm/tcc/interceptor/TccActionInterceptorHandler.java +++ b/tcc/src/main/java/org/apache/seata/rm/tcc/interceptor/TccActionInterceptorHandler.java @@ -38,6 +38,8 @@ import org.apache.seata.integration.tx.api.interceptor.TwoPhaseBusinessActionParam; import org.apache.seata.integration.tx.api.interceptor.handler.AbstractProxyInvocationHandler; import org.apache.seata.rm.tcc.api.TwoPhaseBusinessAction; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.slf4j.MDC; @@ -46,6 +48,8 @@ public class TccActionInterceptorHandler extends AbstractProxyInvocationHandler { + private static final Logger LOGGER = LoggerFactory.getLogger(TccActionInterceptorHandler.class); + private static final int ORDER_NUM = ConfigurationFactory.getInstance().getInt(TCC_ACTION_INTERCEPTOR_ORDER, DefaultValues.TCC_ACTION_INTERCEPTOR_ORDER); @@ -54,7 +58,7 @@ public class TccActionInterceptorHandler extends AbstractProxyInvocationHandler private Set methodsToProxy; protected Object targetBean; - protected Map parseAnnotationCache = new ConcurrentHashMap<>(); + protected Map parseAnnotationCache = new ConcurrentHashMap<>(); public TccActionInterceptorHandler(Object targetBean, Set methodsToProxy) { this.targetBean = targetBean; @@ -67,8 +71,10 @@ protected Object doInvoke(InvocationWrapper invocation) throws Throwable { //not in transaction, or this interceptor is disabled return invocation.proceed(); } - Method method = invocation.getMethod(); - Annotation businessAction = parseAnnotation(method); + + Object[] methodAndAnnotation = parseAnnotation(invocation); + Method method = (Method) methodAndAnnotation[0]; + Annotation businessAction = (Annotation) methodAndAnnotation[1]; //try method if (businessAction != null) { @@ -99,30 +105,41 @@ protected Object doInvoke(InvocationWrapper invocation) throws Throwable { return invocation.proceed(); } - private Annotation parseAnnotation(Method methodKey) throws NoSuchMethodException { - Annotation result = parseAnnotationCache.computeIfAbsent(methodKey, method -> { + /** + * Get try method and the corresponding annotation of TCC mode. + * + * @param invocation + * @return + */ + private Object[] parseAnnotation(InvocationWrapper invocation) { + Object[] results = parseAnnotationCache.computeIfAbsent(invocation.getMethod(), method -> { Annotation twoPhaseBusinessAction = method.getAnnotation(getAnnotationClass()); - if (twoPhaseBusinessAction == null && targetBean.getClass() != null) { - Set> interfaceClasses = ReflectionUtil.getInterfaces(targetBean.getClass()); + Method tryMethod = method; + if (twoPhaseBusinessAction == null && invocation.getTarget() != null) { + Set> interfaceClasses = ReflectionUtil.getInterfaces(invocation.getTarget().getClass()); if (interfaceClasses != null) { for (Class interClass : interfaceClasses) { try { Method m = interClass.getMethod(method.getName(), method.getParameterTypes()); twoPhaseBusinessAction = m.getAnnotation(getAnnotationClass()); if (twoPhaseBusinessAction != null) { - // init common fence clean task if enable useTccFence - initCommonFenceCleanTask(twoPhaseBusinessAction); + tryMethod = m; break; } } catch (NoSuchMethodException e) { - throw new RuntimeException(e); + LOGGER.debug(method.getName() + ", no such method found", e); } } } } - return twoPhaseBusinessAction; + if (twoPhaseBusinessAction == null) { + throw new RuntimeException("No such method with annotation" + getAnnotationClass()); + } + // init common fence clean task if enable useTccFence + initCommonFenceCleanTask(twoPhaseBusinessAction); + return new Object[] {tryMethod, twoPhaseBusinessAction}; }); - return result; + return results; } protected TwoPhaseBusinessActionParam createTwoPhaseBusinessActionParam(Annotation annotation) { From 14f4c067fa85b0f710b6a993871a301bd3f6a13a Mon Sep 17 00:00:00 2001 From: funkye <364176773@qq.com> Date: Thu, 11 Apr 2024 09:35:54 +0800 Subject: [PATCH 2/3] Update spring/src/test/java/org/apache/seata/spring/tcc/TccActionInterceptorHandlerTest.java --- .../tcc/TccActionInterceptorHandlerTest.java | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/spring/src/test/java/org/apache/seata/spring/tcc/TccActionInterceptorHandlerTest.java b/spring/src/test/java/org/apache/seata/spring/tcc/TccActionInterceptorHandlerTest.java index 8c0b6c60c32..b4f18f20061 100644 --- a/spring/src/test/java/org/apache/seata/spring/tcc/TccActionInterceptorHandlerTest.java +++ b/spring/src/test/java/org/apache/seata/spring/tcc/TccActionInterceptorHandlerTest.java @@ -1,3 +1,19 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.seata.spring.tcc; import org.aopalliance.intercept.MethodInvocation; From 69f2f1f087a5f11c9c4177b973c68209b35f9a35 Mon Sep 17 00:00:00 2001 From: jianbin Date: Tue, 23 Apr 2024 10:14:34 +0800 Subject: [PATCH 3/3] update --- changes/en-us/2.x.md | 1 + changes/zh-cn/2.x.md | 1 + 2 files changed, 2 insertions(+) diff --git a/changes/en-us/2.x.md b/changes/en-us/2.x.md index c9439ab2748..f22361f14bd 100644 --- a/changes/en-us/2.x.md +++ b/changes/en-us/2.x.md @@ -188,5 +188,6 @@ Thanks to these contributors for their code commits. Please report an unintended - [yixia](https://github.com/wt-better) - [MikhailNavitski](https://github.com/MikhailNavitski) - [deung](https://github.com/deung) +- [TakeActionNow2019](https://github.com/TakeActionNow2019) Also, we receive many valuable issues, questions and advices from our community. Thanks for you all. diff --git a/changes/zh-cn/2.x.md b/changes/zh-cn/2.x.md index e1d099617e9..4683bcd09f0 100644 --- a/changes/zh-cn/2.x.md +++ b/changes/zh-cn/2.x.md @@ -186,5 +186,6 @@ - [yixia](https://github.com/wt-better) - [MikhailNavitski](https://github.com/MikhailNavitski) - [deung](https://github.com/deung) +- [TakeActionNow2019](https://github.com/TakeActionNow2019) 同时,我们收到了社区反馈的很多有价值的issue和建议,非常感谢大家。