Skip to content

Commit

Permalink
bugfix: fix the failure of @BusinessActionContextParamete annotation …
Browse files Browse the repository at this point in the history
…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". apache#6473
  • Loading branch information
TakeActionNow2019 committed Apr 10, 2024
1 parent d318409 commit 75f612d
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 12 deletions.
Original file line number Diff line number Diff line change
@@ -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<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

0 comments on commit 75f612d

Please sign in to comment.