Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions changes/en-us/2.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ Add changes here for all PR submitted to the 2.x branch.
- [[#6385](https://github.com/apache/incubator-seata/pull/6385)] fix the bug where Role.participant does not execute hooks but clears them.
- [[#6465](https://github.com/apache/incubator-seata/pull/6465)] fix(6257): fix saga mode replay context lost start in 2.x
- [[#6469](https://github.com/apache/incubator-seata/pull/6469)] fix Error in insert sql of [lock_table] data table to sqlserver database
- [[#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
Expand Down
1 change: 1 addition & 0 deletions changes/zh-cn/2.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
- [[#6385](https://github.com/apache/incubator-seata/pull/6385)] 修复Role.Participant不执行hook但会清理的问题
- [[#6465](https://github.com/apache/incubator-seata/pull/6465)] 修复2.0下saga模式的context replay丢失start问题
- [[#6469](https://github.com/apache/incubator-seata/pull/6469)] 修复在sqlserver数据库下[lock_table]数据表的插入操作sql中存在的错误
- [[#6475](https://github.com/apache/incubator-seata/pull/6475)] 修复TCC模式@BusinessActionContextParamete注解无法将参数设置到BusinessActionContext的问题


### optimize:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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;
Comment thread
funky-eyes marked this conversation as resolved.

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<String>() {{
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);

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;


Expand All @@ -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);

Expand All @@ -54,7 +58,7 @@ public class TccActionInterceptorHandler extends AbstractProxyInvocationHandler
private Set<String> methodsToProxy;
protected Object targetBean;

protected Map<Method, Annotation> parseAnnotationCache = new ConcurrentHashMap<>();
protected Map<Method, Object[]> parseAnnotationCache = new ConcurrentHashMap<>();

public TccActionInterceptorHandler(Object targetBean, Set<String> methodsToProxy) {
this.targetBean = targetBean;
Expand All @@ -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) {
Expand Down Expand Up @@ -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<Class<?>> interfaceClasses = ReflectionUtil.getInterfaces(targetBean.getClass());
Method tryMethod = method;
if (twoPhaseBusinessAction == null && invocation.getTarget() != null) {
Set<Class<?>> 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) {
Expand Down