diff --git a/all/pom.xml b/all/pom.xml
index 433a06ba493..caf0b559761 100644
--- a/all/pom.xml
+++ b/all/pom.xml
@@ -332,6 +332,11 @@
+
+ io.seata
+ seata-saga-annotation-mode
+ ${project.version}
+
diff --git a/common/src/main/java/io/seata/common/ConfigurationKeys.java b/common/src/main/java/io/seata/common/ConfigurationKeys.java
index 4e928951089..65ba81b1e07 100644
--- a/common/src/main/java/io/seata/common/ConfigurationKeys.java
+++ b/common/src/main/java/io/seata/common/ConfigurationKeys.java
@@ -231,6 +231,11 @@ public interface ConfigurationKeys {
*/
String TCC_ACTION_INTERCEPTOR_ORDER = CLIENT_RM_PREFIX + "tccActionInterceptorOrder";
+ /**
+ * The constant SAGA_ACTION_INTERCEPTOR_ORDER.
+ */
+ String SAGA_ACTION_INTERCEPTOR_ORDER = CLIENT_RM_PREFIX + "sagaActionInterceptorOrder";
+
/**
* The constant CLIENT_TM_PREFIX.
*/
diff --git a/common/src/main/java/io/seata/common/Constants.java b/common/src/main/java/io/seata/common/Constants.java
index aa4a0241740..4aeb0a42500 100644
--- a/common/src/main/java/io/seata/common/Constants.java
+++ b/common/src/main/java/io/seata/common/Constants.java
@@ -69,7 +69,7 @@ public interface Constants {
/**
* Use TCC fence
*/
- String USE_TCC_FENCE = "useTCCFence";
+ String USE_COMMON_FENCE = "useTCCFence";
/**
* phase one method name
@@ -94,7 +94,7 @@ public interface Constants {
/**
* branch context
*/
- String TCC_ACTION_CONTEXT = "actionContext";
+ String TX_ACTION_CONTEXT = "actionContext";
/**
* default charset name
@@ -160,5 +160,10 @@ public interface Constants {
* The constant SKIP_CHECK_LOCK
*/
String SKIP_CHECK_LOCK = "skipCheckLock";
+
+ /**
+ * phase two compensation method name
+ */
+ String COMPENSATION_METHOD = "sys::compensation";
}
diff --git a/common/src/main/java/io/seata/common/DefaultValues.java b/common/src/main/java/io/seata/common/DefaultValues.java
index bebf182566a..d0d1b4f41db 100644
--- a/common/src/main/java/io/seata/common/DefaultValues.java
+++ b/common/src/main/java/io/seata/common/DefaultValues.java
@@ -147,23 +147,28 @@ public interface DefaultValues {
*/
int TCC_ACTION_INTERCEPTOR_ORDER = Integer.MIN_VALUE + 1000;
+ /**
+ * the constant SAGA_ACTION_INTERCEPTOR_ORDER
+ */
+ int SAGA_ACTION_INTERCEPTOR_ORDER = Integer.MIN_VALUE + 1000;
+
/**
* the constant DEFAULT_DISTRIBUTED_LOCK_EXPIRE
*/
int DEFAULT_DISTRIBUTED_LOCK_EXPIRE = 10000;
/**
- * the constant DEFAULT_TCC_FENCE_CLEAN_PERIOD
+ * the constant DEFAULT_COMMON_FENCE_CLEAN_PERIOD
*/
- int DEFAULT_TCC_FENCE_CLEAN_PERIOD = 1;
+ int DEFAULT_COMMON_FENCE_CLEAN_PERIOD = 1;
/**
- * the constant DEFAULT_TCC_FENCE_LOG_TABLE_NAME
+ * the constant DEFAULT_COMMON_FENCE_LOG_TABLE_NAME
*/
- String DEFAULT_TCC_FENCE_LOG_TABLE_NAME = "tcc_fence_log";
+ String DEFAULT_COMMON_FENCE_LOG_TABLE_NAME = "tcc_fence_log";
/**
- * the constant TCC_FENCE_BEAN_NAME
+ * the constant COMMON_FENCE_BEAN_NAME
*/
- String TCC_FENCE_BEAN_NAME = "tccFenceConfig";
+ String COMMON_FENCE_BEAN_NAME = "tccFenceConfig";
/**
* the constant DEFAULT_RPC_RM_REQUEST_TIMEOUT
diff --git a/core/src/main/java/io/seata/core/model/BranchType.java b/core/src/main/java/io/seata/core/model/BranchType.java
index f7ecfa09848..459655ffe57 100644
--- a/core/src/main/java/io/seata/core/model/BranchType.java
+++ b/core/src/main/java/io/seata/core/model/BranchType.java
@@ -37,6 +37,11 @@ public enum BranchType {
* The SAGA.
*/
SAGA,
+
+ /**
+ * The SAGA annotation mode
+ */
+ SAGA_ANNOTATION,
/**
* The XA.
diff --git a/saga/pom.xml b/saga/pom.xml
index 9f781e5f84f..792b99a1c7a 100644
--- a/saga/pom.xml
+++ b/saga/pom.xml
@@ -36,6 +36,7 @@
seata-saga-rm
seata-saga-tm
seata-saga-engine-store
+ seata-saga-annotation-mode
diff --git a/saga/seata-saga-annotation-mode/pom.xml b/saga/seata-saga-annotation-mode/pom.xml
new file mode 100644
index 00000000000..4f1176c57ed
--- /dev/null
+++ b/saga/seata-saga-annotation-mode/pom.xml
@@ -0,0 +1,27 @@
+
+
+
+ seata-saga
+ io.seata
+ ${revision}
+
+ 4.0.0
+ seata-saga-annotation-mode ${project.version}
+ seata-saga-annotation-mode
+
+
+
+ ${project.groupId}
+ seata-spring
+ ${project.version}
+
+
+ io.seata
+ seata-saga-rm
+ ${project.version}
+
+
+
+
\ No newline at end of file
diff --git a/saga/seata-saga-annotation-mode/src/main/java/io/seata/saga/annotation/api/SagaTransactional.java b/saga/seata-saga-annotation-mode/src/main/java/io/seata/saga/annotation/api/SagaTransactional.java
new file mode 100644
index 00000000000..4566a3c952f
--- /dev/null
+++ b/saga/seata-saga-annotation-mode/src/main/java/io/seata/saga/annotation/api/SagaTransactional.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.saga.annotation.api;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import io.seata.rm.tcc.api.BusinessActionContextParameter;
+import io.seata.rm.tcc.api.BusinessActionContextUtil;
+import io.seata.saga.annotation.interceptor.SagaActionInterceptor;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+
+/**
+ * SAGA annotation.
+ * Define a SAGA interface, which added on the saga commit method.
+ * Must be used with `@LocalService`.
+ *
+ * @author ruishansun
+ * @see io.seata.spring.annotation.LocalService // local transaction annotation, which added on the transaction interface. It can't be left out.
+ * @see io.seata.spring.annotation.GlobalTransactionScanner#wrapIfNecessary(Object, String, Object) // the scanner for TM, GlobalLock, TCC mode, SAGA mode
+ * @see SagaActionInterceptor // the interceptor of SAGA mode
+ * @see BusinessActionContext
+ * @see BusinessActionContextUtil
+ * @see BusinessActionContextParameter
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD})
+@Inherited
+public @interface SagaTransactional {
+
+ /**
+ * Saga bean name, must be unique
+ *
+ * @return the string
+ */
+ String name();
+
+ /**
+ * compensation method name
+ *
+ * @return the string
+ */
+ String compensationMethod() default "compensation";
+
+ /**
+ * delay branch report while sharing params to saga phase 2 to enhance performance
+ *
+ * @return isDelayReport
+ */
+ boolean isDelayReport() default false;
+
+ /**
+ * whether use common fence (idempotent,non_rollback,suspend)
+ *
+ * @return the boolean
+ */
+ boolean useCommonFence() default false;
+
+ /**
+ * compensation method's args
+ *
+ * @return the Class[]
+ */
+ Class>[] compensationArgsClasses() default {BusinessActionContext.class};
+}
diff --git a/saga/seata-saga-annotation-mode/src/main/java/io/seata/saga/annotation/autoproxy/SagaTransactionAutoProxy.java b/saga/seata-saga-annotation-mode/src/main/java/io/seata/saga/annotation/autoproxy/SagaTransactionAutoProxy.java
new file mode 100644
index 00000000000..e70e96a5edd
--- /dev/null
+++ b/saga/seata-saga-annotation-mode/src/main/java/io/seata/saga/annotation/autoproxy/SagaTransactionAutoProxy.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.saga.annotation.autoproxy;
+
+import io.seata.common.exception.FrameworkException;
+import io.seata.rm.DefaultResourceManager;
+import io.seata.saga.annotation.interceptor.SagaActionInterceptor;
+import io.seata.saga.annotation.api.SagaTransactional;
+import io.seata.saga.rm.annotation.SagaAnnotationResource;
+import io.seata.spring.autoproxy.IsTransactionProxyResult;
+import io.seata.spring.autoproxy.TransactionAutoProxy;
+import io.seata.spring.remoting.Protocols;
+import io.seata.spring.remoting.RemotingDesc;
+import io.seata.spring.remoting.parser.DefaultRemotingParser;
+
+import java.lang.reflect.Method;
+
+/**
+ * the saga implements of TransactionAutoProxy
+ *
+ * @author ruishansun
+ */
+public class SagaTransactionAutoProxy implements TransactionAutoProxy {
+
+ @Override
+ public IsTransactionProxyResult isTransactionProxyTargetBean(RemotingDesc remotingDesc) {
+
+ if (remotingDesc == null) {
+ return new IsTransactionProxyResult();
+ }
+ //check if it is saga bean
+ Class> sagaInterfaceClazz = remotingDesc.getInterfaceClass();
+ Method[] methods = sagaInterfaceClazz.getMethods();
+ for (Method method : methods) {
+ SagaTransactional sagaTransactional = method.getAnnotation(SagaTransactional.class);
+ if (sagaTransactional != null && (Protocols.IN_JVM == remotingDesc.getProtocol() || remotingDesc.isReference())) {
+ this.registryResource(remotingDesc);
+ IsTransactionProxyResult result = new IsTransactionProxyResult();
+ result.setProxyTargetBean(true);
+ result.setUseCommonFence(sagaTransactional.useCommonFence());
+ result.setMethodInterceptor(new SagaActionInterceptor(remotingDesc));
+ return result;
+ }
+ }
+ return new IsTransactionProxyResult();
+ }
+
+ /**
+ * register saga resource
+ *
+ * @param remotingDesc the remotingDesc
+ */
+ private void registryResource(RemotingDesc remotingDesc) {
+ if (remotingDesc.isService()) {
+ try {
+ Class> interfaceClass = remotingDesc.getInterfaceClass();
+ Method[] methods = interfaceClass.getMethods();
+ //service bean, registry resource
+ Object targetBean = remotingDesc.getTargetBean();
+ for (Method m : methods) {
+ SagaTransactional sagaTransactional = m.getAnnotation(SagaTransactional.class);
+ if (sagaTransactional != null) {
+ SagaAnnotationResource sagaAnnotationResource = new SagaAnnotationResource();
+ sagaAnnotationResource.setActionName(sagaTransactional.name());
+ sagaAnnotationResource.setTargetBean(targetBean);
+ sagaAnnotationResource.setCommitMethod(m);
+ sagaAnnotationResource.setCompensationMethodName(sagaTransactional.compensationMethod());
+ sagaAnnotationResource.setCompensationMethod(interfaceClass.getMethod(sagaTransactional.compensationMethod(),
+ sagaTransactional.compensationArgsClasses()));
+ // set argsClasses
+ sagaAnnotationResource.setCompensationArgsClasses(sagaTransactional.compensationArgsClasses());
+ // set phase two method's keys
+ sagaAnnotationResource.setPhaseTwoCompensationKeys(DefaultRemotingParser.get().getTwoPhaseArgs(sagaAnnotationResource.getCompensationMethod(),
+ sagaTransactional.compensationArgsClasses()));
+ //registry saga annotation resource
+ sagaAnnotationResource.setResourceGroupId(sagaTransactional.name());
+ DefaultResourceManager.get().registerResource(sagaAnnotationResource);
+ }
+ }
+ } catch (Throwable t) {
+ throw new FrameworkException(t, "parser remoting service error");
+ }
+ }
+ }
+}
diff --git a/saga/seata-saga-annotation-mode/src/main/java/io/seata/saga/annotation/interceptor/SagaActionInterceptor.java b/saga/seata-saga-annotation-mode/src/main/java/io/seata/saga/annotation/interceptor/SagaActionInterceptor.java
new file mode 100644
index 00000000000..3db03d7f138
--- /dev/null
+++ b/saga/seata-saga-annotation-mode/src/main/java/io/seata/saga/annotation/interceptor/SagaActionInterceptor.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.saga.annotation.interceptor;
+
+import io.seata.common.Constants;
+import io.seata.common.DefaultValues;
+import io.seata.config.ConfigurationChangeEvent;
+import io.seata.config.ConfigurationChangeListener;
+import io.seata.config.ConfigurationFactory;
+import io.seata.core.constants.ConfigurationKeys;
+import io.seata.core.context.RootContext;
+import io.seata.core.model.BranchType;
+import io.seata.saga.annotation.api.SagaTransactional;
+import io.seata.spring.interceptor.ActionInterceptorHandler;
+import io.seata.spring.interceptor.TwoPhaseBusinessActionParam;
+import io.seata.spring.remoting.RemotingDesc;
+import org.aopalliance.intercept.MethodInterceptor;
+import org.aopalliance.intercept.MethodInvocation;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.slf4j.MDC;
+import org.springframework.core.Ordered;
+
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Map;
+
+import static io.seata.common.ConfigurationKeys.SAGA_ACTION_INTERCEPTOR_ORDER;
+import static io.seata.common.DefaultValues.DEFAULT_DISABLE_GLOBAL_TRANSACTION;
+
+/**
+ * SAGA Interceptor
+ *
+ * @author ruishansun
+ */
+public class SagaActionInterceptor implements MethodInterceptor, ConfigurationChangeListener, Ordered {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SagaActionInterceptor.class);
+
+ private static final int ORDER_NUM = ConfigurationFactory.getInstance().getInt(SAGA_ACTION_INTERCEPTOR_ORDER,
+ DefaultValues.SAGA_ACTION_INTERCEPTOR_ORDER);
+
+ /**
+ * TODO Singleton?
+ */
+ private ActionInterceptorHandler actionInterceptorHandler = new ActionInterceptorHandler();
+
+ private volatile boolean disable = ConfigurationFactory.getInstance().getBoolean(
+ ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION, DEFAULT_DISABLE_GLOBAL_TRANSACTION);
+
+ /**
+ * remoting bean info
+ */
+ protected RemotingDesc remotingDesc;
+
+ /**
+ * Instantiates a new Saga action interceptor.
+ */
+ public SagaActionInterceptor() {
+ }
+
+ /**
+ * Instantiates a new Saga action interceptor.
+ *
+ * @param remotingDesc the remoting desc
+ */
+ public SagaActionInterceptor(RemotingDesc remotingDesc) {
+ this.remotingDesc = remotingDesc;
+ }
+
+ @Nullable
+ @Override
+ public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
+ if (!RootContext.inGlobalTransaction() || disable || RootContext.inTccBranch()) {
+ //not in transaction, or this interceptor is disabled
+ return invocation.proceed();
+ }
+ Method method = actionInterceptorHandler.getActionInterfaceMethod(invocation, this.remotingDesc);
+ SagaTransactional sagaTransactional = method.getAnnotation(SagaTransactional.class);
+ //commit method
+ if (sagaTransactional != null) {
+ //save the xid
+ String xid = RootContext.getXID();
+ //save the previous branchType
+ BranchType previousBranchType = RootContext.getBranchType();
+ //if not SAGA, bind SAGA branchType
+ if (BranchType.SAGA_ANNOTATION != previousBranchType) {
+ RootContext.bindBranchType(BranchType.SAGA_ANNOTATION);
+ }
+ try {
+ TwoPhaseBusinessActionParam businessActionParam = new TwoPhaseBusinessActionParam();
+ businessActionParam.setActionName(sagaTransactional.name());
+ businessActionParam.setDelayReport(sagaTransactional.isDelayReport());
+ businessActionParam.setUseCommonFence(sagaTransactional.useCommonFence());
+ businessActionParam.setBranchType(BranchType.SAGA_ANNOTATION);
+ Map businessActionContextMap = new HashMap<>(4);
+ //the phase two method name
+ businessActionContextMap.put(Constants.COMPENSATION_METHOD, sagaTransactional.compensationMethod());
+ businessActionContextMap.put(Constants.ACTION_NAME, sagaTransactional.name());
+ businessActionContextMap.put(Constants.USE_COMMON_FENCE, sagaTransactional.useCommonFence());
+ businessActionParam.setBusinessActionContext(businessActionContextMap);
+ //Handler the Saga Aspect, and return the business result
+ return actionInterceptorHandler.proceed(method, invocation.getArguments(), xid, businessActionParam,
+ invocation::proceed);
+ } finally {
+ //if not SAGA, unbind branchType
+ if (BranchType.SAGA_ANNOTATION != previousBranchType) {
+ RootContext.unbindBranchType();
+ }
+ //MDC remove branchId
+ MDC.remove(RootContext.MDC_KEY_BRANCH_ID);
+ }
+ }
+
+ //not SAGA commit method
+ return invocation.proceed();
+ }
+
+ @Override
+ public void onChangeEvent(ConfigurationChangeEvent event) {
+ if (ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION.equals(event.getDataId())) {
+ LOGGER.info("{} config changed, old value:{}, new value:{}", ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
+ disable, event.getNewValue());
+ disable = Boolean.parseBoolean(event.getNewValue().trim());
+ }
+ }
+
+ @Override
+ public int getOrder() {
+ return ORDER_NUM;
+ }
+}
diff --git a/saga/seata-saga-annotation-mode/src/test/java/io/seata/rm/saga/SagaAction.java b/saga/seata-saga-annotation-mode/src/test/java/io/seata/rm/saga/SagaAction.java
new file mode 100644
index 00000000000..8fc5b40da48
--- /dev/null
+++ b/saga/seata-saga-annotation-mode/src/test/java/io/seata/rm/saga/SagaAction.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.rm.saga;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+import io.seata.rm.tcc.api.BusinessActionContextParameter;
+import io.seata.saga.annotation.api.SagaTransactional;
+import io.seata.spring.annotation.LocalService;
+
+/**
+ * The interface Saga action.
+ *
+ * @author ruishansun
+ */
+@LocalService
+public interface SagaAction {
+
+ /**
+ * Commit boolean.
+ *
+ * @param actionContext the action context
+ * @param a the a
+ * @return the boolean
+ */
+ @SagaTransactional(name = "sagaActionForTest", compensationMethod = "compensation")
+ boolean commit(BusinessActionContext actionContext,
+ @BusinessActionContextParameter("a") int a);
+
+ /**
+ * Compensation boolean.
+ *
+ * @param actionContext the action context
+ * @return the boolean
+ */
+ boolean compensation(BusinessActionContext actionContext);
+}
diff --git a/saga/seata-saga-annotation-mode/src/test/java/io/seata/rm/saga/SagaActionImpl.java b/saga/seata-saga-annotation-mode/src/test/java/io/seata/rm/saga/SagaActionImpl.java
new file mode 100644
index 00000000000..3f49382e0df
--- /dev/null
+++ b/saga/seata-saga-annotation-mode/src/test/java/io/seata/rm/saga/SagaActionImpl.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.rm.saga;
+
+import io.seata.rm.tcc.api.BusinessActionContext;
+
+/**
+ * The type Saga action impl.
+ *
+ * @author ruishansun
+ */
+public class SagaActionImpl implements SagaAction {
+
+ @Override
+ public boolean commit(BusinessActionContext actionContext, int a) {
+ return true;
+ }
+
+ @Override
+ public boolean compensation(BusinessActionContext actionContext) {
+ return true;
+ }
+}
diff --git a/saga/seata-saga-annotation-mode/src/test/java/io/seata/rm/saga/remoting/parser/LocalServiceRemotingParserTest.java b/saga/seata-saga-annotation-mode/src/test/java/io/seata/rm/saga/remoting/parser/LocalServiceRemotingParserTest.java
new file mode 100644
index 00000000000..80f99affed0
--- /dev/null
+++ b/saga/seata-saga-annotation-mode/src/test/java/io/seata/rm/saga/remoting/parser/LocalServiceRemotingParserTest.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.rm.saga.remoting.parser;
+
+import io.seata.rm.saga.SagaAction;
+import io.seata.rm.saga.SagaActionImpl;
+import io.seata.spring.remoting.RemotingDesc;
+import io.seata.spring.remoting.parser.LocalServiceRemotingParser;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+/**
+ * The type Local service remoting parser test.
+ *
+ * @author ruishansun
+ */
+public class LocalServiceRemotingParserTest {
+
+ /**
+ * The Local service remoting parser.
+ */
+ LocalServiceRemotingParser localServiceRemotingParser = new LocalServiceRemotingParser();
+
+ /**
+ * Test service parser.
+ */
+ @Test
+ public void testServiceParser() {
+ SagaActionImpl sagaActionImpl = new SagaActionImpl();
+
+ boolean result = localServiceRemotingParser.isService(sagaActionImpl, "a");
+ Assertions.assertTrue(result);
+ }
+
+ /**
+ * Test reference parser.
+ */
+ @Test
+ public void testReferenceParser() {
+ SagaActionImpl sagaActionImpl = new SagaActionImpl();
+
+ boolean result = localServiceRemotingParser.isReference(sagaActionImpl, "b");
+ Assertions.assertTrue(result);
+ }
+
+ /**
+ * Test service desc.
+ */
+ @Test
+ public void testServiceDesc() {
+ SagaActionImpl sagaActionImpl = new SagaActionImpl();
+
+ RemotingDesc remotingDesc = localServiceRemotingParser.getServiceDesc(sagaActionImpl, "c");
+ Assertions.assertNotNull(remotingDesc);
+
+ Assertions.assertEquals("io.seata.rm.saga.SagaAction", remotingDesc.getInterfaceClassName());
+ Assertions.assertEquals(remotingDesc.getInterfaceClass(), SagaAction.class);
+ Assertions.assertEquals(remotingDesc.getTargetBean(), sagaActionImpl);
+ }
+
+}
diff --git a/saga/seata-saga-rm/pom.xml b/saga/seata-saga-rm/pom.xml
index 0956f235865..1e324de4322 100644
--- a/saga/seata-saga-rm/pom.xml
+++ b/saga/seata-saga-rm/pom.xml
@@ -44,6 +44,11 @@
seata-saga-engine
${project.version}
+
+ io.seata
+ seata-spring
+ ${project.version}
+
diff --git a/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/SagaResource.java b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/SagaResource.java
index 8a087c3b8c4..eab48cad704 100644
--- a/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/SagaResource.java
+++ b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/SagaResource.java
@@ -85,4 +85,5 @@ public String getApplicationId() {
public void setApplicationId(String applicationId) {
this.applicationId = applicationId;
}
+
}
\ No newline at end of file
diff --git a/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/SagaResourceManager.java b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/SagaResourceManager.java
index b58120b8d6c..41e29f5f4c5 100644
--- a/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/SagaResourceManager.java
+++ b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/SagaResourceManager.java
@@ -15,9 +15,6 @@
*/
package io.seata.saga.rm;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-
import io.seata.common.exception.FrameworkErrorCode;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
@@ -33,8 +30,11 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
/**
- * Saga resource manager
+ * Saga state machine resource manager
*
* @author lorne.cl
*/
@@ -60,7 +60,7 @@ public SagaResourceManager() {
*/
@Override
public void registerResource(Resource resource) {
- SagaResource sagaResource = (SagaResource)resource;
+ SagaResource sagaResource = (SagaResource) resource;
sagaResourceCache.put(sagaResource.getResourceId(), sagaResource);
super.registerResource(sagaResource);
}
@@ -84,19 +84,21 @@ public Map getManagedResources() {
@Override
public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId,
String applicationData) throws TransactionException {
+
+ // Saga state machine mode
try {
StateMachineInstance machineInstance = StateMachineEngineHolder.getStateMachineEngine().forward(xid, null);
if (ExecutionStatus.SU.equals(machineInstance.getStatus())
- && machineInstance.getCompensationStatus() == null) {
+ && machineInstance.getCompensationStatus() == null) {
return BranchStatus.PhaseTwo_Committed;
} else if (ExecutionStatus.SU.equals(machineInstance.getCompensationStatus())) {
return BranchStatus.PhaseTwo_Rollbacked;
} else if (ExecutionStatus.FA.equals(machineInstance.getCompensationStatus()) || ExecutionStatus.UN.equals(
- machineInstance.getCompensationStatus())) {
+ machineInstance.getCompensationStatus())) {
return BranchStatus.PhaseTwo_RollbackFailed_Retryable;
} else if (ExecutionStatus.FA.equals(machineInstance.getStatus())
- && machineInstance.getCompensationStatus() == null) {
+ && machineInstance.getCompensationStatus() == null) {
return BranchStatus.PhaseOne_Failed;
}
@@ -127,20 +129,22 @@ public BranchStatus branchCommit(BranchType branchType, String xid, long branchI
@Override
public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId,
String applicationData) throws TransactionException {
+
+ // Saga state machine mode
try {
StateMachineInstance stateMachineInstance = StateMachineEngineHolder.getStateMachineEngine().reloadStateMachineInstance(xid);
if (stateMachineInstance == null) {
return BranchStatus.PhaseTwo_Rollbacked;
}
if (RecoverStrategy.Forward.equals(stateMachineInstance.getStateMachine().getRecoverStrategy())
- && (GlobalStatus.TimeoutRollbacking.name().equals(applicationData)
- || GlobalStatus.TimeoutRollbackRetrying.name().equals(applicationData))) {
+ && (GlobalStatus.TimeoutRollbacking.name().equals(applicationData)
+ || GlobalStatus.TimeoutRollbackRetrying.name().equals(applicationData))) {
LOGGER.warn("Retry by custom recover strategy [Forward] on timeout, SAGA global[{}]", xid);
return BranchStatus.PhaseTwo_CommitFailed_Retryable;
}
stateMachineInstance = StateMachineEngineHolder.getStateMachineEngine().compensate(xid,
- null);
+ null);
if (ExecutionStatus.SU.equals(stateMachineInstance.getCompensationStatus())) {
return BranchStatus.PhaseTwo_Rollbacked;
}
diff --git a/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/annotation/RMHandlerSagaAnnotation.java b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/annotation/RMHandlerSagaAnnotation.java
new file mode 100644
index 00000000000..d4af54b561b
--- /dev/null
+++ b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/annotation/RMHandlerSagaAnnotation.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.saga.rm.annotation;
+
+import io.seata.core.model.BranchType;
+import io.seata.core.model.ResourceManager;
+import io.seata.rm.AbstractRMHandler;
+import io.seata.rm.DefaultResourceManager;
+
+/**
+ * The type Rm handler SAGA annotation.
+ *
+ * @author ruishansun
+ */
+public class RMHandlerSagaAnnotation extends AbstractRMHandler {
+
+ @Override
+ protected ResourceManager getResourceManager() {
+ return DefaultResourceManager.get().getResourceManager(BranchType.SAGA_ANNOTATION);
+ }
+
+ @Override
+ public BranchType getBranchType() {
+ return BranchType.SAGA_ANNOTATION;
+ }
+}
diff --git a/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/annotation/SagaAnnotationResource.java b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/annotation/SagaAnnotationResource.java
new file mode 100644
index 00000000000..0e3dcd2fe40
--- /dev/null
+++ b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/annotation/SagaAnnotationResource.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.saga.rm.annotation;
+
+import io.seata.core.model.BranchType;
+import io.seata.core.model.Resource;
+
+import java.lang.reflect.Method;
+
+/**
+ * Saga annotation mode resource (Only register application as a saga annotation mode resource)
+ *
+ * @author ruishansun
+ */
+public class SagaAnnotationResource implements Resource {
+
+ private String resourceGroupId = "DEFAULT";
+
+ private Object targetBean;
+
+ private String actionName;
+
+ private Method commitMethod;
+
+ private Method compensationMethod;
+
+ private String compensationMethodName;
+
+ private Class>[] compensationArgsClasses;
+
+ private String[] phaseTwoCompensationKeys;
+
+ public Object getTargetBean() {
+ return targetBean;
+ }
+
+ public void setTargetBean(Object targetBean) {
+ this.targetBean = targetBean;
+ }
+
+ public String getActionName() {
+ return actionName;
+ }
+
+ public void setActionName(String actionName) {
+ this.actionName = actionName;
+ }
+
+ public Method getCommitMethod() {
+ return commitMethod;
+ }
+
+ public void setCommitMethod(Method commitMethod) {
+ this.commitMethod = commitMethod;
+ }
+
+ public Method getCompensationMethod() {
+ return compensationMethod;
+ }
+
+ public void setCompensationMethod(Method compensationMethod) {
+ this.compensationMethod = compensationMethod;
+ }
+
+ public String getCompensationMethodName() {
+ return compensationMethodName;
+ }
+
+ public void setCompensationMethodName(String compensationMethodName) {
+ this.compensationMethodName = compensationMethodName;
+ }
+
+ public Class>[] getCompensationArgsClasses() {
+ return compensationArgsClasses;
+ }
+
+ public void setCompensationArgsClasses(Class>[] compensationArgsClasses) {
+ this.compensationArgsClasses = compensationArgsClasses;
+ }
+
+ public String[] getPhaseTwoCompensationKeys() {
+ return phaseTwoCompensationKeys;
+ }
+
+ public void setPhaseTwoCompensationKeys(String[] phaseTwoCompensationKeys) {
+ this.phaseTwoCompensationKeys = phaseTwoCompensationKeys;
+ }
+
+ @Override
+ public String getResourceGroupId() {
+ return resourceGroupId;
+ }
+
+ /**
+ * Sets resource group id.
+ *
+ * @param resourceGroupId the resource group id
+ */
+ public void setResourceGroupId(String resourceGroupId) {
+ this.resourceGroupId = resourceGroupId;
+ }
+
+
+ @Override
+ public String getResourceId() {
+ return actionName;
+ }
+
+ /**
+ * Gets get branch type.
+ *
+ * @return the get branch type
+ */
+ @Override
+ public BranchType getBranchType() {
+ return BranchType.SAGA_ANNOTATION;
+ }
+}
diff --git a/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/annotation/SagaAnnotationResourceManager.java b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/annotation/SagaAnnotationResourceManager.java
new file mode 100644
index 00000000000..0513cb8b2fc
--- /dev/null
+++ b/saga/seata-saga-rm/src/main/java/io/seata/saga/rm/annotation/SagaAnnotationResourceManager.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.saga.rm.annotation;
+
+import io.seata.common.Constants;
+import io.seata.common.exception.ShouldNeverHappenException;
+import io.seata.common.exception.SkipCallbackWrapperException;
+import io.seata.core.model.BranchStatus;
+import io.seata.core.model.BranchType;
+import io.seata.core.model.Resource;
+import io.seata.rm.AbstractResourceManager;
+import io.seata.rm.tcc.api.BusinessActionContext;
+import io.seata.rm.tcc.api.BusinessActionContextUtil;
+import io.seata.spring.fence.CommonFenceHandler;
+import io.seata.spring.remoting.TwoPhaseResult;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Saga annotation resource manager
+ *
+ * @author ruishansun
+ */
+public class SagaAnnotationResourceManager extends AbstractResourceManager {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(SagaAnnotationResourceManager.class);
+
+ /**
+ * Saga annotation resource cache
+ */
+ private final Map sagaAnnotationResourceCache = new ConcurrentHashMap<>();
+
+ /**
+ * Instantiates a new saga annotation resource manager.
+ */
+ public SagaAnnotationResourceManager() {
+ }
+
+ /**
+ * registry saga annotation resource
+ *
+ * @param resource The resource to be managed.
+ */
+ @Override
+ public void registerResource(Resource resource) {
+ SagaAnnotationResource sagaAnnotationResource = (SagaAnnotationResource) resource;
+ sagaAnnotationResourceCache.put(sagaAnnotationResource.getResourceId(), sagaAnnotationResource);
+ super.registerResource(sagaAnnotationResource);
+ }
+
+ @Override
+ public Map getManagedResources() {
+ return sagaAnnotationResourceCache;
+ }
+
+ @Override
+ public BranchStatus branchCommit(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) {
+
+ // Saga annotation mode
+ return BranchStatus.PhaseTwo_Committed;
+ }
+
+ @Override
+ public BranchStatus branchRollback(BranchType branchType, String xid, long branchId, String resourceId, String applicationData) {
+ // Saga annotation mode
+ SagaAnnotationResource sagaAnnotationResource = (SagaAnnotationResource) sagaAnnotationResourceCache.get(resourceId);
+ if (sagaAnnotationResource == null) {
+ throw new ShouldNeverHappenException(String.format("Saga annotation resource is not exist, resourceId: %s", resourceId));
+ }
+ Object targetSagaBean = sagaAnnotationResource.getTargetBean();
+ Method compensationMethod = sagaAnnotationResource.getCompensationMethod();
+ if (targetSagaBean == null || compensationMethod == null) {
+ throw new ShouldNeverHappenException(String.format("Saga target Bean or compensationMethod is not available, resourceId: %s", resourceId));
+ }
+ try {
+ //BusinessActionContext
+ BusinessActionContext businessActionContext = BusinessActionContextUtil.getBusinessActionContext(xid, branchId, resourceId, applicationData);
+ Object[] args = this.getTwoPhaseCompensationArgs(sagaAnnotationResource, businessActionContext);
+
+ Object ret;
+ boolean result;
+ // add idempotent and anti hanging
+ if (Boolean.TRUE.equals(businessActionContext.getActionContext(Constants.USE_COMMON_FENCE))) {
+ try {
+ result = CommonFenceHandler.rollbackFence(compensationMethod, targetSagaBean, xid, branchId, args, sagaAnnotationResource.getActionName());
+ } catch (SkipCallbackWrapperException | UndeclaredThrowableException e) {
+ throw e.getCause();
+ }
+ } else {
+ ret = compensationMethod.invoke(targetSagaBean, args);
+ if (ret != null) {
+ if (ret instanceof TwoPhaseResult) {
+ result = ((TwoPhaseResult) ret).isSuccess();
+ } else {
+ result = (boolean) ret;
+ }
+ } else {
+ result = true;
+ }
+ }
+ LOGGER.info("Saga resource compensation result : {}, xid: {}, branchId: {}, resourceId: {}", result, xid, branchId, resourceId);
+ return result ? BranchStatus.PhaseTwo_Rollbacked : BranchStatus.PhaseTwo_RollbackFailed_Retryable;
+ } catch (Throwable t) {
+ String msg = String.format("compensation Saga resource error, resourceId: %s, xid: %s.", resourceId, xid);
+ LOGGER.error(msg, t);
+ return BranchStatus.PhaseTwo_RollbackFailed_Retryable;
+ }
+ }
+
+
+ /**
+ * get phase two compensate method's args
+ *
+ * @param sagaAnnotationResource sagaAnnotationResource
+ * @param businessActionContext businessActionContext
+ * @return args
+ */
+ private Object[] getTwoPhaseCompensationArgs(SagaAnnotationResource sagaAnnotationResource, BusinessActionContext businessActionContext) {
+ String[] keys = sagaAnnotationResource.getPhaseTwoCompensationKeys();
+ Class>[] argsCommitClasses = sagaAnnotationResource.getCompensationArgsClasses();
+ return BusinessActionContextUtil.getTwoPhaseMethodParams(keys, argsCommitClasses, businessActionContext);
+ }
+
+ @Override
+ public BranchType getBranchType() {
+ return BranchType.SAGA_ANNOTATION;
+ }
+}
diff --git a/saga/seata-saga-rm/src/main/resources/META-INF/services/io.seata.core.model.ResourceManager b/saga/seata-saga-rm/src/main/resources/META-INF/services/io.seata.core.model.ResourceManager
index 69bc38af6f9..4e157dfa481 100644
--- a/saga/seata-saga-rm/src/main/resources/META-INF/services/io.seata.core.model.ResourceManager
+++ b/saga/seata-saga-rm/src/main/resources/META-INF/services/io.seata.core.model.ResourceManager
@@ -1 +1,2 @@
io.seata.saga.rm.SagaResourceManager
+io.seata.saga.rm.annotation.SagaAnnotationResourceManager
\ No newline at end of file
diff --git a/saga/seata-saga-rm/src/main/resources/META-INF/services/io.seata.rm.AbstractRMHandler b/saga/seata-saga-rm/src/main/resources/META-INF/services/io.seata.rm.AbstractRMHandler
index cd5b9a18bbb..b10cb62b3de 100644
--- a/saga/seata-saga-rm/src/main/resources/META-INF/services/io.seata.rm.AbstractRMHandler
+++ b/saga/seata-saga-rm/src/main/resources/META-INF/services/io.seata.rm.AbstractRMHandler
@@ -1 +1,2 @@
-io.seata.saga.rm.RMHandlerSaga
\ No newline at end of file
+io.seata.saga.rm.RMHandlerSaga
+io.seata.saga.rm.annotation.RMHandlerSagaAnnotation
\ No newline at end of file
diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataClientEnvironmentPostProcessor.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataClientEnvironmentPostProcessor.java
index 5939248427a..48145b3ca73 100644
--- a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataClientEnvironmentPostProcessor.java
+++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataClientEnvironmentPostProcessor.java
@@ -16,7 +16,7 @@
package io.seata.spring.boot.autoconfigure;
import io.seata.common.holder.ObjectHolder;
-import io.seata.rm.tcc.config.TCCFenceConfig;
+import io.seata.spring.fence.config.CommonFenceConfig;
import io.seata.saga.engine.StateMachineConfig;
import io.seata.spring.boot.autoconfigure.properties.SagaAsyncThreadPoolProperties;
import io.seata.spring.boot.autoconfigure.properties.SeataProperties;
@@ -64,7 +64,7 @@ public void postProcessEnvironment(ConfigurableEnvironment environment, SpringAp
PROPERTY_BEAN_MAP.put(UNDO_PREFIX, UndoProperties.class);
PROPERTY_BEAN_MAP.put(COMPRESS_PREFIX, UndoCompressProperties.class);
PROPERTY_BEAN_MAP.put(LOAD_BALANCE_PREFIX, LoadBalanceProperties.class);
- PROPERTY_BEAN_MAP.put(TCC_FENCE_PREFIX, TCCFenceConfig.class);
+ PROPERTY_BEAN_MAP.put(TCC_FENCE_PREFIX, CommonFenceConfig.class);
PROPERTY_BEAN_MAP.put(SAGA_STATE_MACHINE_PREFIX, StateMachineConfig.class);
PROPERTY_BEAN_MAP.put(SAGA_ASYNC_THREAD_POOL_PREFIX, SagaAsyncThreadPoolProperties.class);
}
diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataTCCFenceAutoConfiguration.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataTCCFenceAutoConfiguration.java
index 3f4cd97a87f..a772ae66c4b 100644
--- a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataTCCFenceAutoConfiguration.java
+++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/SeataTCCFenceAutoConfiguration.java
@@ -15,7 +15,7 @@
*/
package io.seata.spring.boot.autoconfigure;
-import io.seata.rm.tcc.config.TCCFenceConfig;
+import io.seata.spring.fence.config.CommonFenceConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
@@ -37,7 +37,7 @@
*/
@ConditionalOnExpression("${seata.enabled:true}")
@ConditionalOnBean(type = {"javax.sql.DataSource", "org.springframework.transaction.PlatformTransactionManager"})
-@ConditionalOnMissingBean(TCCFenceConfig.class)
+@ConditionalOnMissingBean(CommonFenceConfig.class)
@AutoConfigureAfter({SeataCoreAutoConfiguration.class, DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class})
public class SeataTCCFenceAutoConfiguration {
@@ -46,12 +46,12 @@ public class SeataTCCFenceAutoConfiguration {
@Bean
@ConfigurationProperties(StarterConstants.TCC_FENCE_PREFIX)
- public TCCFenceConfig tccFenceConfig(
+ public CommonFenceConfig tccFenceConfig(
DataSource dataSource,
PlatformTransactionManager transactionManager,
@Qualifier(TCC_FENCE_DATA_SOURCE_BEAN_NAME) @Autowired(required = false) DataSource tccFenceDataSource,
@Qualifier(TCC_FENCE_TRANSACTION_MANAGER_BEAN_NAME) @Autowired(required = false) PlatformTransactionManager tccFenceTransactionManager) {
- return new TCCFenceConfig(tccFenceDataSource != null ? tccFenceDataSource : dataSource,
+ return new CommonFenceConfig(tccFenceDataSource != null ? tccFenceDataSource : dataSource,
tccFenceTransactionManager != null ? tccFenceTransactionManager : transactionManager);
}
diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/properties/client/RmProperties.java b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/properties/client/RmProperties.java
index 705e05ff1bd..b01c860eb52 100644
--- a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/properties/client/RmProperties.java
+++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/java/io/seata/spring/boot/autoconfigure/properties/client/RmProperties.java
@@ -29,6 +29,7 @@
import static io.seata.common.DefaultValues.DEFAULT_SAGA_JSON_PARSER;
import static io.seata.common.DefaultValues.DEFAULT_TABLE_META_CHECKER_INTERVAL;
import static io.seata.common.DefaultValues.TCC_ACTION_INTERCEPTOR_ORDER;
+import static io.seata.common.DefaultValues.SAGA_ACTION_INTERCEPTOR_ORDER;
import static io.seata.common.DefaultValues.DEFAULT_XA_BRANCH_EXECUTION_TIMEOUT;
import static io.seata.common.DefaultValues.DEFAULT_XA_CONNECTION_TWO_PHASE_HOLD_TIMEOUT;
import static io.seata.spring.boot.autoconfigure.StarterConstants.CLIENT_RM_PREFIX;
@@ -49,6 +50,7 @@ public class RmProperties {
private boolean sagaRetryPersistModeUpdate = DEFAULT_CLIENT_SAGA_RETRY_PERSIST_MODE_UPDATE;
private boolean sagaCompensatePersistModeUpdate = DEFAULT_CLIENT_SAGA_COMPENSATE_PERSIST_MODE_UPDATE;
private int tccActionInterceptorOrder = TCC_ACTION_INTERCEPTOR_ORDER;
+ private int sagaActionInterceptorOrder = SAGA_ACTION_INTERCEPTOR_ORDER;
private int branchExecutionTimeoutXA = DEFAULT_XA_BRANCH_EXECUTION_TIMEOUT;
private int connectionTwoPhaseHoldTimeoutXA = DEFAULT_XA_CONNECTION_TWO_PHASE_HOLD_TIMEOUT;
private String sqlParserType = SqlParserType.SQL_PARSER_TYPE_DRUID;
@@ -164,4 +166,11 @@ public void setConnectionTwoPhaseHoldTimeoutXA(int connectionTwoPhaseHoldTimeout
this.connectionTwoPhaseHoldTimeoutXA = connectionTwoPhaseHoldTimeoutXA;
}
+ public int getSagaActionInterceptorOrder() {
+ return sagaActionInterceptorOrder;
+ }
+
+ public void setSagaActionInterceptorOrder(int sagaActionInterceptorOrder) {
+ this.sagaActionInterceptorOrder = sagaActionInterceptorOrder;
+ }
}
diff --git a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index 5ca2a8de55c..0e16befe433 100644
--- a/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/seata-spring-autoconfigure/seata-spring-autoconfigure-client/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -248,14 +248,14 @@
"name": "seata.tcc.fence.log-table-name",
"type": "java.lang.String",
"description": "TCC fence log table name.",
- "sourceType": "io.seata.rm.tcc.config.TCCFenceConfig",
+ "sourceType": "io.seata.spring.fence.config.CommonFenceConfig",
"defaultValue": "tcc_fence_log"
},
{
"name": "seata.tcc.fence.clean-period",
"type": "java.time.Duration",
"description": "TCC fence log clean period. only duration type format are supported.",
- "sourceType": "io.seata.rm.tcc.config.TCCFenceConfig",
+ "sourceType": "io.seata.spring.fence.config.CommonFenceConfig",
"defaultValue": "1d"
},
{
diff --git a/spring/pom.xml b/spring/pom.xml
index c67e64c763a..540c348265a 100644
--- a/spring/pom.xml
+++ b/spring/pom.xml
@@ -39,11 +39,6 @@
seata-rm-datasource
${project.version}
-
- ${project.groupId}
- seata-tcc
- ${project.version}
-
${project.groupId}
seata-rm
@@ -64,6 +59,10 @@
org.springframework
spring-context
+
+ org.springframework
+ spring-jdbc
+
org.jetbrains.kotlin
kotlin-stdlib-jdk8
@@ -78,8 +77,10 @@
org.jetbrains.kotlinx
kotlinx-coroutines-core
-
-
+
+ com.alibaba
+ fastjson
+
diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java b/spring/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java
similarity index 93%
rename from tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java
rename to spring/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java
index f11b3249940..0676adddcf0 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java
+++ b/spring/src/main/java/io/seata/rm/tcc/api/BusinessActionContext.java
@@ -20,7 +20,8 @@
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
-import io.seata.rm.tcc.interceptor.ActionContextUtil;
+import io.seata.core.model.BranchType;
+import io.seata.spring.interceptor.ActionContextUtil;
/**
* The type Business action context.
@@ -50,6 +51,11 @@ public class BusinessActionContext implements Serializable {
*/
private Boolean isUpdated;
+ /**
+ * branch Type
+ */
+ private BranchType branchType;
+
/**
* action context
*/
@@ -219,6 +225,14 @@ public void setUpdated(Boolean updated) {
isUpdated = updated;
}
+ public BranchType getBranchType() {
+ return branchType;
+ }
+
+ public void setBranchType(BranchType branchType) {
+ this.branchType = branchType;
+ }
+
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
@@ -227,6 +241,7 @@ public String toString() {
.append(",action_name:").append(actionName)
.append(",is_delay_report:").append(isDelayReport)
.append(",is_updated:").append(isDelayReport)
+ .append(",branch_type:").append(branchType)
.append(",action_context:")
.append(actionContext).append("]");
return sb.toString();
diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java b/spring/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java
similarity index 85%
rename from tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java
rename to spring/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java
index 518a7c5dec4..786b8a8549b 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java
+++ b/spring/src/main/java/io/seata/rm/tcc/api/BusinessActionContextParameter.java
@@ -26,7 +26,7 @@
* add this annotation on the parameters of the try method, and the parameters will be passed to the action context
*
* @author zhangsen
- * @see io.seata.rm.tcc.interceptor.ActionContextUtil
+ * @see io.seata.spring.interceptor.ActionContextUtil
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
@@ -36,7 +36,7 @@
* parameter's name. Synonym for {@link #paramName()}.
*
* @return the name of the param or field
- * @see io.seata.rm.tcc.interceptor.ActionContextUtil#getParamNameFromAnnotation
+ * @see io.seata.spring.interceptor.ActionContextUtil#getParamNameFromAnnotation
*/
String value() default "";
@@ -44,7 +44,7 @@
* parameter's name. Synonym for {@link #value()}.
*
* @return the name of the param or field
- * @see io.seata.rm.tcc.interceptor.ActionContextUtil#getParamNameFromAnnotation
+ * @see io.seata.spring.interceptor.ActionContextUtil#getParamNameFromAnnotation
*/
String paramName() default "";
@@ -61,7 +61,7 @@
* Specify the index of the parameter in the List
*
* @return the index of the List
- * @see io.seata.rm.tcc.interceptor.ActionContextUtil#getByIndex
+ * @see io.seata.spring.interceptor.ActionContextUtil#getByIndex
*/
int index() default -1;
@@ -70,8 +70,8 @@
* if {@code index >= 0}, the object get from the List and then do get the parameter from the property of the object
*
* @return the boolean
- * @see io.seata.rm.tcc.interceptor.ActionContextUtil#loadParamByAnnotationAndPutToContext
- * @see io.seata.rm.tcc.interceptor.ActionContextUtil#fetchContextFromObject
+ * @see io.seata.spring.interceptor.ActionContextUtil#loadParamByAnnotationAndPutToContext
+ * @see io.seata.spring.interceptor.ActionContextUtil#fetchContextFromObject
*/
boolean isParamInProperty() default false;
}
diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java b/spring/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java
similarity index 68%
rename from tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java
rename to spring/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java
index 30a52ae0500..ac5cb348eb6 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java
+++ b/spring/src/main/java/io/seata/rm/tcc/api/BusinessActionContextUtil.java
@@ -16,17 +16,18 @@
package io.seata.rm.tcc.api;
import java.util.Collections;
+import java.util.HashMap;
import java.util.Map;
import com.alibaba.fastjson.JSON;
import io.seata.common.Constants;
import io.seata.common.exception.FrameworkException;
import io.seata.common.util.CollectionUtils;
+import io.seata.common.util.StringUtils;
import io.seata.core.exception.TransactionException;
import io.seata.core.model.BranchStatus;
-import io.seata.core.model.BranchType;
import io.seata.rm.DefaultResourceManager;
-import io.seata.rm.tcc.interceptor.ActionContextUtil;
+import io.seata.spring.interceptor.ActionContextUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -106,11 +107,11 @@ public static boolean reportContext(BusinessActionContext actionContext) {
try {
// branch report
DefaultResourceManager.get().branchReport(
- BranchType.TCC,
+ actionContext.getBranchType(),
actionContext.getXid(),
actionContext.getBranchId(),
BranchStatus.Registered,
- JSON.toJSONString(Collections.singletonMap(Constants.TCC_ACTION_CONTEXT, actionContext.getActionContext()))
+ JSON.toJSONString(Collections.singletonMap(Constants.TX_ACTION_CONTEXT, actionContext.getActionContext()))
);
// reset to un_updated
@@ -134,4 +135,43 @@ public static void setContext(BusinessActionContext context) {
public static void clear() {
CONTEXT_HOLDER.remove();
}
+
+ /**
+ * transfer tcc applicationData to BusinessActionContext
+ *
+ * @param xid the xid
+ * @param branchId the branch id
+ * @param resourceId the resource id
+ * @param applicationData the application data
+ * @return business action context
+ */
+ public static BusinessActionContext getBusinessActionContext(String xid, long branchId, String resourceId,
+ String applicationData) {
+ Map actionContextMap = null;
+ if (StringUtils.isNotBlank(applicationData)) {
+ Map tccContext = JSON.parseObject(applicationData, Map.class);
+ actionContextMap = (Map) tccContext.get(Constants.TX_ACTION_CONTEXT);
+ }
+ if (actionContextMap == null) {
+ actionContextMap = new HashMap<>(2);
+ }
+
+ //instance the action context
+ BusinessActionContext businessActionContext = new BusinessActionContext(
+ xid, String.valueOf(branchId), actionContextMap);
+ businessActionContext.setActionName(resourceId);
+ return businessActionContext;
+ }
+
+ public static Object[] getTwoPhaseMethodParams(String[] keys, Class>[] argsClasses, BusinessActionContext businessActionContext) {
+ Object[] args = new Object[argsClasses.length];
+ for (int i = 0; i < argsClasses.length; i++) {
+ if (argsClasses[i].equals(BusinessActionContext.class)) {
+ args[i] = businessActionContext;
+ } else {
+ args[i] = businessActionContext.getActionContext(keys[i], argsClasses[i]);
+ }
+ }
+ return args;
+ }
}
diff --git a/tcc/src/main/java/io/seata/rm/tcc/api/ParamType.java b/spring/src/main/java/io/seata/rm/tcc/api/ParamType.java
similarity index 100%
rename from tcc/src/main/java/io/seata/rm/tcc/api/ParamType.java
rename to spring/src/main/java/io/seata/rm/tcc/api/ParamType.java
diff --git a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java b/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java
index 852d959e55e..aaf85fa2e49 100644
--- a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java
+++ b/spring/src/main/java/io/seata/spring/annotation/GlobalTransactionScanner.java
@@ -37,10 +37,12 @@
import io.seata.core.rpc.netty.TmNettyRemotingClient;
import io.seata.rm.RMClient;
import io.seata.spring.annotation.scannercheckers.PackageScannerChecker;
-import io.seata.spring.tcc.TccActionInterceptor;
+import io.seata.spring.autoproxy.DefaultTransactionAutoProxy;
+import io.seata.spring.autoproxy.IsTransactionProxyResult;
+import io.seata.spring.interceptor.TxBeanParserUtils;
+import io.seata.spring.remoting.parser.LocalServiceRemotingParser;
import io.seata.spring.util.OrderUtil;
import io.seata.spring.util.SpringProxyUtils;
-import io.seata.spring.util.TCCBeanParserUtils;
import io.seata.tm.TMClient;
import io.seata.tm.api.FailureHandler;
import org.aopalliance.aop.Advice;
@@ -259,9 +261,18 @@ private void registerSpringShutdownHook() {
* TCC mode:
* @see io.seata.rm.tcc.api.LocalTCC // TCC annotation on interface
* @see io.seata.rm.tcc.api.TwoPhaseBusinessAction // TCC annotation on try method
- * @see io.seata.rm.tcc.remoting.RemotingParser // Remote TCC service parser
+ * @see io.seata.spring.remoting.RemotingParser // Remote TCC service parser
* Corresponding interceptor:
- * @see io.seata.spring.tcc.TccActionInterceptor // the interceptor of TCC mode
+ * @see io.seata.rm.tcc.interceptor.TccActionInterceptor // the interceptor of TCC mode
+ *
+ * SAGA annotation mode:
+ * @see LocalService // local service annotation on interface
+ * @see io.seata.saga.api.SagaTransactional // SAGA annotation on commit method
+ * @see LocalServiceRemotingParser // Remote SAGA service parser
+ * @see io.seata.saga.autoproxy.SagaTransactionAutoProxy // SAGA transaction auto proxy
+ * Corresponding interceptor:
+ * @see io.seata.saga.interceptor.SagaActionInterceptor // the interceptor of SAGA annotation mode
+ *
*/
@Override
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
@@ -276,14 +287,15 @@ protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey)
return bean;
}
interceptor = null;
- //check TCC proxy
- if (TCCBeanParserUtils.isTccAutoProxy(bean, beanName, applicationContext)) {
+ //check Transaction proxy
+ if (TxBeanParserUtils.isTxAutoProxy(bean, beanName, applicationContext)) {
+ IsTransactionProxyResult isTransactionProxyResult = DefaultTransactionAutoProxy.get().getIsProxyTargetBeanResult(beanName);
// init tcc fence clean task if enable useTccFence
- TCCBeanParserUtils.initTccFenceCleanTask(TCCBeanParserUtils.getRemotingDesc(beanName), applicationContext);
- //TCC interceptor, proxy bean of sofa:reference/dubbo:reference, and LocalTCC
- interceptor = new TccActionInterceptor(TCCBeanParserUtils.getRemotingDesc(beanName));
+ TxBeanParserUtils.initCommonFenceCleanTask(TxBeanParserUtils.getRemotingDesc(beanName), applicationContext, isTransactionProxyResult.isUseCommonFence());
+ //transaction interceptor(TCC/SAGA), proxy bean of sofa:reference/dubbo:reference, LocalTCC and LocalService
+ interceptor = isTransactionProxyResult.getMethodInterceptor();
ConfigurationCache.addConfigListener(ConfigurationKeys.DISABLE_GLOBAL_TRANSACTION,
- (ConfigurationChangeListener)interceptor);
+ (ConfigurationChangeListener) interceptor);
} else {
Class> serviceInterface = SpringProxyUtils.findTargetClass(bean);
Class>[] interfacesIfJdk = SpringProxyUtils.findInterfaces(bean);
diff --git a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactional.java b/spring/src/main/java/io/seata/spring/annotation/GlobalTransactional.java
index 2fb478073e9..40ff30dad0c 100644
--- a/spring/src/main/java/io/seata/spring/annotation/GlobalTransactional.java
+++ b/spring/src/main/java/io/seata/spring/annotation/GlobalTransactional.java
@@ -40,7 +40,7 @@
* @see io.seata.spring.annotation.datasource.SeataAutoDataSourceProxyAdvice#invoke(MethodInvocation) io.seata.spring
* .annotation.datasource.SeataAutoDataSourceProxyAdvice#invoke(MethodInvocation)// RM: the interceptor of
* GlobalLockLogic and AT/XA mode
- * @see io.seata.spring.tcc.TccActionInterceptor#invoke(MethodInvocation) io.seata.spring.tcc
+ * @see io.seata.rm.tcc.interceptor.TccActionInterceptor#invoke(MethodInvocation) io.seata.spring.tcc
* .TccActionInterceptor#invoke(MethodInvocation)// RM: the interceptor of TCC mode
*/
@Retention(RetentionPolicy.RUNTIME)
diff --git a/spring/src/main/java/io/seata/spring/annotation/LocalService.java b/spring/src/main/java/io/seata/spring/annotation/LocalService.java
new file mode 100644
index 00000000000..d2ac7db7897
--- /dev/null
+++ b/spring/src/main/java/io/seata/spring/annotation/LocalService.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.spring.annotation;
+
+import io.seata.spring.remoting.parser.LocalServiceRemotingParser;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
+
+/**
+ * Local transaction bean annotation, add on the TCC/SAGA interface
+ *
+ * @author ruishansun
+ * @see io.seata.spring.annotation.GlobalTransactionScanner#wrapIfNecessary(Object, String, Object) // the scanner for TM, GlobalLock, TCC mode and SAGA mode
+ * @see LocalServiceRemotingParser // the RemotingParser impl for Local transaction
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Inherited
+public @interface LocalService {
+}
diff --git a/spring/src/main/java/io/seata/spring/autoproxy/DefaultTransactionAutoProxy.java b/spring/src/main/java/io/seata/spring/autoproxy/DefaultTransactionAutoProxy.java
new file mode 100644
index 00000000000..60e3bab6fc1
--- /dev/null
+++ b/spring/src/main/java/io/seata/spring/autoproxy/DefaultTransactionAutoProxy.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.spring.autoproxy;
+
+import io.seata.common.loader.EnhancedServiceLoader;
+import io.seata.common.util.CollectionUtils;
+import io.seata.spring.remoting.RemotingDesc;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * the default transaction auto proxy
+ *
+ * @author ruishansun
+ */
+public class DefaultTransactionAutoProxy {
+
+ /**
+ * all the transaction auto proxy
+ */
+ protected static final List ALL_TRANSACTION_AUTO_PROXIES = new ArrayList<>();
+ /**
+ * method interceptor map, beanName -> IsTransactionProxyResult
+ */
+ private static final Map METHOD_INTERCEPTOR_MAP = new ConcurrentHashMap<>();
+
+ private static class SingletonHolder {
+ private static final DefaultTransactionAutoProxy INSTANCE = new DefaultTransactionAutoProxy();
+ }
+
+ /**
+ * Get the default transaction auto proxy
+ *
+ * @return the default transaction auto proxy
+ */
+ public static DefaultTransactionAutoProxy get() {
+ return SingletonHolder.INSTANCE;
+ }
+
+ /**
+ * Instantiates a new default transaction auto proxy
+ */
+ protected DefaultTransactionAutoProxy() {
+ initTransactionAutoProxy();
+ }
+
+ /**
+ * init transaction auto proxy
+ */
+ private void initTransactionAutoProxy() {
+ List proxies = EnhancedServiceLoader.loadAll(TransactionAutoProxy.class);
+ if (CollectionUtils.isNotEmpty(proxies)) {
+ ALL_TRANSACTION_AUTO_PROXIES.addAll(proxies);
+ }
+ }
+
+ /**
+ * whether is transaction auto proxy
+ *
+ * @param beanName the beanName
+ * @param remotingDesc the remotingDesc
+ * @return true or false
+ */
+ public boolean isTransactionAutoProxy(String beanName, RemotingDesc remotingDesc) {
+ for (TransactionAutoProxy proxy : ALL_TRANSACTION_AUTO_PROXIES) {
+ IsTransactionProxyResult result = proxy.isTransactionProxyTargetBean(remotingDesc);
+ if (result.isProxyTargetBean()) {
+ METHOD_INTERCEPTOR_MAP.put(beanName, result);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * get the IsTransactionProxyResult
+ *
+ * @param beanName the beanName
+ * @return the IsTransactionProxyResult
+ */
+ public IsTransactionProxyResult getIsProxyTargetBeanResult(String beanName) {
+ IsTransactionProxyResult result = METHOD_INTERCEPTOR_MAP.get(beanName);
+ return result != null ? result : new IsTransactionProxyResult();
+ }
+}
diff --git a/spring/src/main/java/io/seata/spring/autoproxy/IsTransactionProxyResult.java b/spring/src/main/java/io/seata/spring/autoproxy/IsTransactionProxyResult.java
new file mode 100644
index 00000000000..07d831a749e
--- /dev/null
+++ b/spring/src/main/java/io/seata/spring/autoproxy/IsTransactionProxyResult.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.spring.autoproxy;
+
+import org.aopalliance.intercept.MethodInterceptor;
+
+/**
+ * whether is the transaction proxy result
+ *
+ * @author ruishansun
+ */
+public class IsTransactionProxyResult {
+
+ /**
+ * whether proxied by transaction bean
+ */
+ private boolean isProxyTargetBean;
+
+ /**
+ * whether used common fence
+ */
+ private boolean useCommonFence;
+
+ /**
+ * transaction proxy method
+ */
+ private MethodInterceptor methodInterceptor;
+
+ public boolean isProxyTargetBean() {
+ return isProxyTargetBean;
+ }
+
+ public void setProxyTargetBean(boolean proxyTargetBean) {
+ isProxyTargetBean = proxyTargetBean;
+ }
+
+ public boolean isUseCommonFence() {
+ return useCommonFence;
+ }
+
+ public void setUseCommonFence(boolean useCommonFence) {
+ this.useCommonFence = useCommonFence;
+ }
+
+ public MethodInterceptor getMethodInterceptor() {
+ return methodInterceptor;
+ }
+
+ public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
+ this.methodInterceptor = methodInterceptor;
+ }
+}
diff --git a/spring/src/main/java/io/seata/spring/autoproxy/TransactionAutoProxy.java b/spring/src/main/java/io/seata/spring/autoproxy/TransactionAutoProxy.java
new file mode 100644
index 00000000000..1f54cf24992
--- /dev/null
+++ b/spring/src/main/java/io/seata/spring/autoproxy/TransactionAutoProxy.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright 1999-2019 Seata.io Group.
+ *
+ * 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 io.seata.spring.autoproxy;
+
+import io.seata.spring.remoting.RemotingDesc;
+
+/**
+ * The interface Transaction Auto Proxy. Proxied by tcc/saga with SPI.
+ *
+ * @author ruishansun
+ */
+public interface TransactionAutoProxy {
+
+ /**
+ * Whether it is transaction auto proxy? (tcc or saga)
+ *
+ * @param remotingDesc the remotingDesc
+ * @return the IsTransactionProxyResult
+ */
+ IsTransactionProxyResult isTransactionProxyTargetBean(RemotingDesc remotingDesc);
+}
diff --git a/tcc/src/main/java/io/seata/rm/tcc/TCCFenceHandler.java b/spring/src/main/java/io/seata/spring/fence/CommonFenceHandler.java
similarity index 73%
rename from tcc/src/main/java/io/seata/rm/tcc/TCCFenceHandler.java
rename to spring/src/main/java/io/seata/spring/fence/CommonFenceHandler.java
index f5a4dee1021..e9240e98c4d 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/TCCFenceHandler.java
+++ b/spring/src/main/java/io/seata/spring/fence/CommonFenceHandler.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc;
+package io.seata.spring.fence;
import java.lang.reflect.Method;
import java.sql.Connection;
@@ -28,11 +28,12 @@
import io.seata.common.exception.SkipCallbackWrapperException;
import io.seata.common.executor.Callback;
import io.seata.common.thread.NamedThreadFactory;
-import io.seata.rm.tcc.constant.TCCFenceConstant;
-import io.seata.rm.tcc.exception.TCCFenceException;
-import io.seata.rm.tcc.store.TCCFenceDO;
-import io.seata.rm.tcc.store.TCCFenceStore;
-import io.seata.rm.tcc.store.db.TCCFenceStoreDataBaseDAO;
+import io.seata.spring.fence.constant.CommonFenceConstant;
+import io.seata.spring.fence.exception.CommonFenceException;
+import io.seata.spring.fence.store.CommonFenceDO;
+import io.seata.spring.fence.store.CommonFenceStore;
+import io.seata.spring.fence.store.db.CommonFenceStoreDataBaseDAO;
+import io.seata.spring.remoting.TwoPhaseResult;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.jdbc.datasource.DataSourceUtils;
@@ -40,19 +41,19 @@
import org.springframework.transaction.support.TransactionTemplate;
/**
- * TCC Fence Handler(idempotent, non_rollback, suspend)
+ * Common Fence Handler(idempotent, non_rollback, suspend)
*
* @author kaka2code
*/
-public class TCCFenceHandler {
+public class CommonFenceHandler {
- private TCCFenceHandler() {
+ private CommonFenceHandler() {
throw new IllegalStateException("Utility class");
}
- private static final Logger LOGGER = LoggerFactory.getLogger(TCCFenceHandler.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(CommonFenceHandler.class);
- private static final TCCFenceStore TCC_FENCE_DAO = TCCFenceStoreDataBaseDAO.getInstance();
+ private static final CommonFenceStore COMMON_FENCE_DAO = CommonFenceStoreDataBaseDAO.getInstance();
private static DataSource dataSource;
@@ -77,15 +78,15 @@ private TCCFenceHandler() {
}
public static void setDataSource(DataSource dataSource) {
- TCCFenceHandler.dataSource = dataSource;
+ CommonFenceHandler.dataSource = dataSource;
}
public static void setTransactionTemplate(TransactionTemplate transactionTemplate) {
- TCCFenceHandler.transactionTemplate = transactionTemplate;
+ CommonFenceHandler.transactionTemplate = transactionTemplate;
}
/**
- * tcc prepare method enhanced
+ * common prepare method enhanced
*
* @param xid the global transaction id
* @param branchId the branch transaction id
@@ -97,15 +98,15 @@ public static Object prepareFence(String xid, Long branchId, String actionName,
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
- boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_TRIED);
- LOGGER.info("TCC fence prepare result: {}. xid: {}, branchId: {}", result, xid, branchId);
+ boolean result = insertCommonFenceLog(conn, xid, branchId, actionName, CommonFenceConstant.STATUS_TRIED);
+ LOGGER.info("Common fence prepare result: {}. xid: {}, branchId: {}", result, xid, branchId);
if (result) {
return targetCallback.execute();
} else {
- throw new TCCFenceException(String.format("Insert tcc fence record error, prepare fence failed. xid= %s, branchId= %s", xid, branchId),
+ throw new CommonFenceException(String.format("Insert common fence record error, prepare fence failed. xid= %s, branchId= %s", xid, branchId),
FrameworkErrorCode.InsertRecordError);
}
- } catch (TCCFenceException e) {
+ } catch (CommonFenceException e) {
if (e.getErrcode() == FrameworkErrorCode.DuplicateKeyException) {
LOGGER.error("Branch transaction has already rollbacked before,prepare fence failed. xid= {},branchId = {}", xid, branchId);
addToLogCleanQueue(xid, branchId);
@@ -120,10 +121,10 @@ public static Object prepareFence(String xid, Long branchId, String actionName,
}
/**
- * tcc commit method enhanced
+ * common commit method enhanced
*
* @param commitMethod commit method
- * @param targetTCCBean target tcc bean
+ * @param targetTCCBean target common bean
* @param xid the global transaction id
* @param branchId the branch transaction id
* @param args commit method's parameters
@@ -134,22 +135,22 @@ public static boolean commitFence(Method commitMethod, Object targetTCCBean,
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
- TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId);
- if (tccFenceDO == null) {
- throw new TCCFenceException(String.format("TCC fence record not exists, commit fence method failed. xid= %s, branchId= %s", xid, branchId),
+ CommonFenceDO commonFenceDO = COMMON_FENCE_DAO.queryCommonFenceDO(conn, xid, branchId);
+ if (commonFenceDO == null) {
+ throw new CommonFenceException(String.format("Common fence record not exists, commit fence method failed. xid= %s, branchId= %s", xid, branchId),
FrameworkErrorCode.RecordAlreadyExists);
}
- if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) {
- LOGGER.info("Branch transaction has already committed before. idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
+ if (CommonFenceConstant.STATUS_COMMITTED == commonFenceDO.getStatus()) {
+ LOGGER.info("Branch transaction has already committed before. idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus());
return true;
}
- if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) {
+ if (CommonFenceConstant.STATUS_ROLLBACKED == commonFenceDO.getStatus() || CommonFenceConstant.STATUS_SUSPENDED == commonFenceDO.getStatus()) {
if (LOGGER.isWarnEnabled()) {
- LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
+ LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus());
}
return false;
}
- return updateStatusAndInvokeTargetMethod(conn, commitMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_COMMITTED, status, args);
+ return updateStatusAndInvokeTargetMethod(conn, commitMethod, targetTCCBean, xid, branchId, CommonFenceConstant.STATUS_COMMITTED, status, args);
} catch (Throwable t) {
status.setRollbackOnly();
throw new SkipCallbackWrapperException(t);
@@ -158,7 +159,7 @@ public static boolean commitFence(Method commitMethod, Object targetTCCBean,
}
/**
- * tcc rollback method enhanced
+ * Common rollback method enhanced
*
* @param rollbackMethod rollback method
* @param targetTCCBean target tcc bean
@@ -173,29 +174,29 @@ public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
- TCCFenceDO tccFenceDO = TCC_FENCE_DAO.queryTCCFenceDO(conn, xid, branchId);
+ CommonFenceDO commonFenceDO = COMMON_FENCE_DAO.queryCommonFenceDO(conn, xid, branchId);
// non_rollback
- if (tccFenceDO == null) {
- boolean result = insertTCCFenceLog(conn, xid, branchId, actionName, TCCFenceConstant.STATUS_SUSPENDED);
- LOGGER.info("Insert tcc fence record result: {}. xid: {}, branchId: {}", result, xid, branchId);
+ if (commonFenceDO == null) {
+ boolean result = insertCommonFenceLog(conn, xid, branchId, actionName, CommonFenceConstant.STATUS_SUSPENDED);
+ LOGGER.info("Insert common fence record result: {}. xid: {}, branchId: {}", result, xid, branchId);
if (!result) {
- throw new TCCFenceException(String.format("Insert tcc fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId),
+ throw new CommonFenceException(String.format("Insert common fence record error, rollback fence method failed. xid= %s, branchId= %s", xid, branchId),
FrameworkErrorCode.InsertRecordError);
}
return true;
} else {
- if (TCCFenceConstant.STATUS_ROLLBACKED == tccFenceDO.getStatus() || TCCFenceConstant.STATUS_SUSPENDED == tccFenceDO.getStatus()) {
- LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
+ if (CommonFenceConstant.STATUS_ROLLBACKED == commonFenceDO.getStatus() || CommonFenceConstant.STATUS_SUSPENDED == commonFenceDO.getStatus()) {
+ LOGGER.info("Branch transaction had already rollbacked before, idempotency rejected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus());
return true;
}
- if (TCCFenceConstant.STATUS_COMMITTED == tccFenceDO.getStatus()) {
+ if (CommonFenceConstant.STATUS_COMMITTED == commonFenceDO.getStatus()) {
if (LOGGER.isWarnEnabled()) {
- LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, tccFenceDO.getStatus());
+ LOGGER.warn("Branch transaction status is unexpected. xid: {}, branchId: {}, status: {}", xid, branchId, commonFenceDO.getStatus());
}
return false;
}
}
- return updateStatusAndInvokeTargetMethod(conn, rollbackMethod, targetTCCBean, xid, branchId, TCCFenceConstant.STATUS_ROLLBACKED, status, args);
+ return updateStatusAndInvokeTargetMethod(conn, rollbackMethod, targetTCCBean, xid, branchId, CommonFenceConstant.STATUS_ROLLBACKED, status, args);
} catch (Throwable t) {
status.setRollbackOnly();
throw new SkipCallbackWrapperException(t);
@@ -204,7 +205,7 @@ public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
}
/**
- * Insert TCC fence log
+ * Insert Common fence log
*
* @param conn the db connection
* @param xid the xid
@@ -212,13 +213,13 @@ public static boolean rollbackFence(Method rollbackMethod, Object targetTCCBean,
* @param status the status
* @return the boolean
*/
- private static boolean insertTCCFenceLog(Connection conn, String xid, Long branchId, String actionName, Integer status) {
- TCCFenceDO tccFenceDO = new TCCFenceDO();
- tccFenceDO.setXid(xid);
- tccFenceDO.setBranchId(branchId);
- tccFenceDO.setActionName(actionName);
- tccFenceDO.setStatus(status);
- return TCC_FENCE_DAO.insertTCCFenceDO(conn, tccFenceDO);
+ private static boolean insertCommonFenceLog(Connection conn, String xid, Long branchId, String actionName, Integer status) {
+ CommonFenceDO commonFenceDO = new CommonFenceDO();
+ commonFenceDO.setXid(xid);
+ commonFenceDO.setBranchId(branchId);
+ commonFenceDO.setActionName(actionName);
+ commonFenceDO.setStatus(status);
+ return COMMON_FENCE_DAO.insertCommonFenceDO(conn, commonFenceDO);
}
/**
@@ -235,7 +236,7 @@ private static boolean updateStatusAndInvokeTargetMethod(Connection conn, Method
String xid, Long branchId, int status,
TransactionStatus transactionStatus,
Object[] args) throws Exception {
- boolean result = TCC_FENCE_DAO.updateTCCFenceDO(conn, xid, branchId, status, TCCFenceConstant.STATUS_TRIED);
+ boolean result = COMMON_FENCE_DAO.updateCommonFenceDO(conn, xid, branchId, status, CommonFenceConstant.STATUS_TRIED);
if (result) {
// invoke two phase method
Object ret = method.invoke(targetTCCBean, args);
@@ -255,7 +256,7 @@ private static boolean updateStatusAndInvokeTargetMethod(Connection conn, Method
}
/**
- * Delete TCC Fence
+ * Delete Common Fence
*
* @param xid the global transaction id
* @param branchId the branch transaction id
@@ -266,7 +267,7 @@ public static boolean deleteFence(String xid, Long branchId) {
boolean ret = false;
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
- ret = TCC_FENCE_DAO.deleteTCCFenceDO(conn, xid, branchId);
+ ret = COMMON_FENCE_DAO.deleteCommonFenceDO(conn, xid, branchId);
} catch (RuntimeException e) {
status.setRollbackOnly();
LOGGER.error("delete fence log failed, xid: {}, branchId: {}", xid, branchId, e);
@@ -276,7 +277,7 @@ public static boolean deleteFence(String xid, Long branchId) {
}
/**
- * Delete TCC Fence By Datetime
+ * Delete Common Fence By Datetime
*
* @param datetime datetime
* @return the deleted row count
@@ -285,7 +286,7 @@ public static int deleteFenceByDate(Date datetime) {
return transactionTemplate.execute(status -> {
try {
Connection conn = DataSourceUtils.getConnection(dataSource);
- return TCC_FENCE_DAO.deleteTCCFenceDOByDate(conn, datetime);
+ return COMMON_FENCE_DAO.deleteCommonFenceDOByDate(conn, datetime);
} catch (RuntimeException e) {
status.setRollbackOnly();
throw e;
@@ -316,7 +317,7 @@ private static void addToLogCleanQueue(final String xid, final long branchId) {
/**
* clean fence log that has the final status runnable.
*
- * @see TCCFenceConstant
+ * @see CommonFenceConstant
*/
private static class FenceLogCleanRunnable implements Runnable {
@Override
@@ -325,7 +326,7 @@ public void run() {
try {
FenceLogIdentity logIdentity = LOG_QUEUE.take();
- boolean ret = TCCFenceHandler.deleteFence(logIdentity.getXid(), logIdentity.getBranchId());
+ boolean ret = CommonFenceHandler.deleteFence(logIdentity.getXid(), logIdentity.getBranchId());
if (!ret) {
LOGGER.error("delete fence log failed, xid: {}, branchId: {}", logIdentity.getXid(), logIdentity.getBranchId());
}
diff --git a/tcc/src/main/java/io/seata/rm/tcc/config/TCCFenceConfig.java b/spring/src/main/java/io/seata/spring/fence/config/CommonFenceConfig.java
similarity index 75%
rename from tcc/src/main/java/io/seata/rm/tcc/config/TCCFenceConfig.java
rename to spring/src/main/java/io/seata/spring/fence/config/CommonFenceConfig.java
index fffc08ab993..5b8037e9d69 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/config/TCCFenceConfig.java
+++ b/spring/src/main/java/io/seata/spring/fence/config/CommonFenceConfig.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.config;
+package io.seata.spring.fence.config;
import java.time.Duration;
import java.util.Date;
@@ -27,9 +27,9 @@
import io.seata.common.exception.FrameworkErrorCode;
import io.seata.common.thread.NamedThreadFactory;
import io.seata.core.rpc.Disposable;
-import io.seata.rm.tcc.TCCFenceHandler;
-import io.seata.rm.tcc.exception.TCCFenceException;
-import io.seata.rm.tcc.store.db.TCCFenceStoreDataBaseDAO;
+import io.seata.spring.fence.CommonFenceHandler;
+import io.seata.spring.fence.exception.CommonFenceException;
+import io.seata.spring.fence.store.db.CommonFenceStoreDataBaseDAO;
import org.apache.commons.lang.time.DateUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -38,48 +38,48 @@
import org.springframework.transaction.support.TransactionTemplate;
/**
- * TCC Fence Config
+ * Common Fence Config
*
* @author kaka2code
*/
-public class TCCFenceConfig implements InitializingBean, Disposable {
+public class CommonFenceConfig implements InitializingBean, Disposable {
- private static final Logger LOGGER = LoggerFactory.getLogger(TCCFenceConfig.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(CommonFenceConfig.class);
private final AtomicBoolean initialized = new AtomicBoolean(false);
/**
- * TCC fence clean period max value. maximum interval is 68 years
+ * Common fence clean period max value. maximum interval is 68 years
*/
private static final Duration MAX_PERIOD = Duration.ofSeconds(Integer.MAX_VALUE);
/**
- * TCC fence clean period. only duration type format are supported
+ * Common fence clean period. only duration type format are supported
*/
- private Duration cleanPeriod = Duration.ofDays(DefaultValues.DEFAULT_TCC_FENCE_CLEAN_PERIOD);
+ private Duration cleanPeriod = Duration.ofDays(DefaultValues.DEFAULT_COMMON_FENCE_CLEAN_PERIOD);
/**
- * TCC fence log table name
+ * Common fence log table name
*/
- private String logTableName = DefaultValues.DEFAULT_TCC_FENCE_LOG_TABLE_NAME;
+ private String logTableName = DefaultValues.DEFAULT_COMMON_FENCE_LOG_TABLE_NAME;
/**
- * TCC fence datasource
+ * Common fence datasource
*/
private final DataSource dataSource;
/**
- * TCC fence transactionManager
+ * Common fence transactionManager
*/
private final PlatformTransactionManager transactionManager;
/**
- * TCC fence clean scheduled thread pool
+ * Common fence clean scheduled thread pool
*/
private final ScheduledThreadPoolExecutor tccFenceClean = new ScheduledThreadPoolExecutor(1,
new NamedThreadFactory("tccFenceClean", 1));
- public TCCFenceConfig(final DataSource dataSource, final PlatformTransactionManager transactionManager) {
+ public CommonFenceConfig(final DataSource dataSource, final PlatformTransactionManager transactionManager) {
this.dataSource = dataSource;
this.transactionManager = transactionManager;
}
@@ -116,7 +116,7 @@ public void initCleanTask() {
Date timeBefore = null;
try {
timeBefore = DateUtils.addSeconds(new Date(), -(int)periodSeconds);
- int deletedRowCount = TCCFenceHandler.deleteFenceByDate(timeBefore);
+ int deletedRowCount = CommonFenceHandler.deleteFenceByDate(timeBefore);
if (deletedRowCount > 0) {
LOGGER.info("TCC fence clean task executed success, timeBefore: {}, deleted row count: {}",
timeBefore, deletedRowCount);
@@ -141,19 +141,19 @@ public void destroy() {
public void afterPropertiesSet() {
// set log table name
if (logTableName != null) {
- TCCFenceStoreDataBaseDAO.getInstance().setLogTableName(logTableName);
+ CommonFenceStoreDataBaseDAO.getInstance().setLogTableName(logTableName);
}
if (dataSource != null) {
// set dataSource
- TCCFenceHandler.setDataSource(dataSource);
+ CommonFenceHandler.setDataSource(dataSource);
} else {
- throw new TCCFenceException(FrameworkErrorCode.DateSourceNeedInjected);
+ throw new CommonFenceException(FrameworkErrorCode.DateSourceNeedInjected);
}
if (transactionManager != null) {
// set transaction template
- TCCFenceHandler.setTransactionTemplate(new TransactionTemplate(transactionManager));
+ CommonFenceHandler.setTransactionTemplate(new TransactionTemplate(transactionManager));
} else {
- throw new TCCFenceException(FrameworkErrorCode.TransactionManagerNeedInjected);
+ throw new CommonFenceException(FrameworkErrorCode.TransactionManagerNeedInjected);
}
}
}
diff --git a/tcc/src/main/java/io/seata/rm/tcc/constant/TCCFenceConstant.java b/spring/src/main/java/io/seata/spring/fence/constant/CommonFenceConstant.java
similarity index 89%
rename from tcc/src/main/java/io/seata/rm/tcc/constant/TCCFenceConstant.java
rename to spring/src/main/java/io/seata/spring/fence/constant/CommonFenceConstant.java
index 7c9a2b416cf..bc56d841559 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/constant/TCCFenceConstant.java
+++ b/spring/src/main/java/io/seata/spring/fence/constant/CommonFenceConstant.java
@@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.constant;
+package io.seata.spring.fence.constant;
/**
- * TCC Fence Constant
+ * Common Fence Constant
*
* @author kaka2code
*/
-public class TCCFenceConstant {
+public class CommonFenceConstant {
- private TCCFenceConstant() {
+ private CommonFenceConstant() {
throw new IllegalStateException("Utility class");
}
diff --git a/tcc/src/main/java/io/seata/rm/tcc/exception/TCCFenceException.java b/spring/src/main/java/io/seata/spring/fence/exception/CommonFenceException.java
similarity index 65%
rename from tcc/src/main/java/io/seata/rm/tcc/exception/TCCFenceException.java
rename to spring/src/main/java/io/seata/spring/fence/exception/CommonFenceException.java
index 716da66ed1f..0200dd993c5 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/exception/TCCFenceException.java
+++ b/spring/src/main/java/io/seata/spring/fence/exception/CommonFenceException.java
@@ -13,39 +13,39 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.exception;
+package io.seata.spring.fence.exception;
import io.seata.common.exception.FrameworkErrorCode;
import io.seata.common.exception.FrameworkException;
/**
- * TCC Fence Exception
+ * Common Fence Exception
*
* @author kaka2code
*/
-public class TCCFenceException extends FrameworkException {
+public class CommonFenceException extends FrameworkException {
- public TCCFenceException(FrameworkErrorCode err) {
+ public CommonFenceException(FrameworkErrorCode err) {
super(err);
}
- public TCCFenceException(String msg) {
+ public CommonFenceException(String msg) {
super(msg);
}
- public TCCFenceException(String msg, FrameworkErrorCode errCode) {
+ public CommonFenceException(String msg, FrameworkErrorCode errCode) {
super(msg, errCode);
}
- public TCCFenceException(Throwable cause, String msg, FrameworkErrorCode errCode) {
+ public CommonFenceException(Throwable cause, String msg, FrameworkErrorCode errCode) {
super(cause, msg, errCode);
}
- public TCCFenceException(Throwable th) {
+ public CommonFenceException(Throwable th) {
super(th);
}
- public TCCFenceException(Throwable th, String msg) {
+ public CommonFenceException(Throwable th, String msg) {
super(th, msg);
}
diff --git a/tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceDO.java b/spring/src/main/java/io/seata/spring/fence/store/CommonFenceDO.java
similarity index 94%
rename from tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceDO.java
rename to spring/src/main/java/io/seata/spring/fence/store/CommonFenceDO.java
index 6878a9a0c74..ba079cb955c 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceDO.java
+++ b/spring/src/main/java/io/seata/spring/fence/store/CommonFenceDO.java
@@ -13,16 +13,16 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.store;
+package io.seata.spring.fence.store;
import java.util.Date;
/**
- * TCC Fence Domain
+ * Common Fence Domain
*
* @author kaka2code
*/
-public class TCCFenceDO {
+public class CommonFenceDO {
/**
* the global transaction id
@@ -40,7 +40,7 @@ public class TCCFenceDO {
private String actionName;
/**
- * the tcc fence status
+ * the common fence status
* tried: 1; committed: 2; rollbacked: 3; suspended: 4
*/
private Integer status;
diff --git a/tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceStore.java b/spring/src/main/java/io/seata/spring/fence/store/CommonFenceStore.java
similarity index 63%
rename from tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceStore.java
rename to spring/src/main/java/io/seata/spring/fence/store/CommonFenceStore.java
index 9cbd6ba255e..ff9d04b3434 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/store/TCCFenceStore.java
+++ b/spring/src/main/java/io/seata/spring/fence/store/CommonFenceStore.java
@@ -13,56 +13,56 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.store;
+package io.seata.spring.fence.store;
import java.sql.Connection;
import java.util.Date;
/**
- * The TCC Fence Store
+ * The common Fence Store
*
* @author kaka2code
*/
-public interface TCCFenceStore {
+public interface CommonFenceStore {
/**
- * Query tcc fence do.
+ * Query common fence do.
* @param xid the global transaction id
* @param branchId the branch transaction id
- * @return the tcc fence do
+ * @return the common fence do
*/
- TCCFenceDO queryTCCFenceDO(Connection conn, String xid, Long branchId);
+ CommonFenceDO queryCommonFenceDO(Connection conn, String xid, Long branchId);
/**
- * Insert tcc fence do boolean.
- * @param tccFenceDO the tcc fence do
+ * Insert common fence do boolean.
+ * @param commonFenceDO the common fence do
* @return the boolean
*/
- boolean insertTCCFenceDO(Connection conn, TCCFenceDO tccFenceDO);
+ boolean insertCommonFenceDO(Connection conn, CommonFenceDO commonFenceDO);
/**
- * Update tcc fence do boolean.
+ * Update common fence do boolean.
* @param xid the global transaction id
* @param branchId the branch transaction id
* @param newStatus the new status
* @return the boolean
*/
- boolean updateTCCFenceDO(Connection conn, String xid, Long branchId, int newStatus, int oldStatus);
+ boolean updateCommonFenceDO(Connection conn, String xid, Long branchId, int newStatus, int oldStatus);
/**
- * Delete tcc fence do boolean.
+ * Delete common fence do boolean.
* @param xid the global transaction id
* @param branchId the branch transaction id
* @return the boolean
*/
- boolean deleteTCCFenceDO(Connection conn, String xid, Long branchId);
+ boolean deleteCommonFenceDO(Connection conn, String xid, Long branchId);
/**
- * Delete tcc fence by datetime.
+ * Delete common fence by datetime.
* @param datetime datetime
* @return the deleted row count
*/
- int deleteTCCFenceDOByDate(Connection conn, Date datetime);
+ int deleteCommonFenceDOByDate(Connection conn, Date datetime);
/**
* Set LogTable Name
diff --git a/tcc/src/main/java/io/seata/rm/tcc/store/db/TCCFenceStoreDataBaseDAO.java b/spring/src/main/java/io/seata/spring/fence/store/db/CommonFenceStoreDataBaseDAO.java
similarity index 60%
rename from tcc/src/main/java/io/seata/rm/tcc/store/db/TCCFenceStoreDataBaseDAO.java
rename to spring/src/main/java/io/seata/spring/fence/store/db/CommonFenceStoreDataBaseDAO.java
index 10e6c2979fd..5da3f2393ae 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/store/db/TCCFenceStoreDataBaseDAO.java
+++ b/spring/src/main/java/io/seata/spring/fence/store/db/CommonFenceStoreDataBaseDAO.java
@@ -13,17 +13,17 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.store.db;
+package io.seata.spring.fence.store.db;
import io.seata.common.DefaultValues;
import io.seata.common.exception.DataAccessException;
import io.seata.common.exception.FrameworkErrorCode;
import io.seata.common.exception.StoreException;
import io.seata.common.util.IOUtil;
-import io.seata.rm.tcc.exception.TCCFenceException;
-import io.seata.rm.tcc.store.TCCFenceDO;
-import io.seata.rm.tcc.store.TCCFenceStore;
-import io.seata.rm.tcc.store.db.sql.TCCFenceStoreSqls;
+import io.seata.spring.fence.exception.CommonFenceException;
+import io.seata.spring.fence.store.CommonFenceDO;
+import io.seata.spring.fence.store.CommonFenceStore;
+import io.seata.spring.fence.store.db.sql.CommonFenceStoreSqls;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -34,26 +34,26 @@
import java.util.Date;
/**
- * The type TCC Fence store data base dao
+ * The type Common Fence store data base dao
*
* @author kaka2code
*/
-public class TCCFenceStoreDataBaseDAO implements TCCFenceStore {
+public class CommonFenceStoreDataBaseDAO implements CommonFenceStore {
/**
- * TCC fence log table name
+ * Common fence log table name
*/
- private String logTableName = DefaultValues.DEFAULT_TCC_FENCE_LOG_TABLE_NAME;
+ private String logTableName = DefaultValues.DEFAULT_COMMON_FENCE_LOG_TABLE_NAME;
- private static volatile TCCFenceStoreDataBaseDAO instance = null;
+ private static volatile CommonFenceStoreDataBaseDAO instance = null;
- private TCCFenceStoreDataBaseDAO() {}
+ private CommonFenceStoreDataBaseDAO() {}
- public static TCCFenceStore getInstance() {
+ public static CommonFenceStore getInstance() {
if (instance == null) {
- synchronized (TCCFenceStore.class) {
+ synchronized (CommonFenceStore.class) {
if (instance == null) {
- instance = new TCCFenceStoreDataBaseDAO();
+ instance = new CommonFenceStoreDataBaseDAO();
}
}
}
@@ -61,21 +61,21 @@ public static TCCFenceStore getInstance() {
}
@Override
- public TCCFenceDO queryTCCFenceDO(Connection conn, String xid, Long branchId) {
+ public CommonFenceDO queryCommonFenceDO(Connection conn, String xid, Long branchId) {
PreparedStatement ps = null;
ResultSet rs = null;
try {
- String sql = TCCFenceStoreSqls.getQuerySQLByBranchIdAndXid(logTableName);
+ String sql = CommonFenceStoreSqls.getQuerySQLByBranchIdAndXid(logTableName);
ps = conn.prepareStatement(sql);
ps.setString(1, xid);
ps.setLong(2, branchId);
rs = ps.executeQuery();
if (rs.next()) {
- TCCFenceDO tccFenceDO = new TCCFenceDO();
- tccFenceDO.setXid(rs.getString("xid"));
- tccFenceDO.setBranchId(rs.getLong("branch_id"));
- tccFenceDO.setStatus(rs.getInt("status"));
- return tccFenceDO;
+ CommonFenceDO commonFenceDO = new CommonFenceDO();
+ commonFenceDO.setXid(rs.getString("xid"));
+ commonFenceDO.setBranchId(rs.getLong("branch_id"));
+ commonFenceDO.setStatus(rs.getInt("status"));
+ return commonFenceDO;
} else {
return null;
}
@@ -87,22 +87,22 @@ public TCCFenceDO queryTCCFenceDO(Connection conn, String xid, Long branchId) {
}
@Override
- public boolean insertTCCFenceDO(Connection conn, TCCFenceDO tccFenceDO) {
+ public boolean insertCommonFenceDO(Connection conn, CommonFenceDO commonFenceDO) {
PreparedStatement ps = null;
try {
Timestamp now = new Timestamp(System.currentTimeMillis());
- String sql = TCCFenceStoreSqls.getInsertLocalTCCLogSQL(logTableName);
+ String sql = CommonFenceStoreSqls.getInsertLocalTCCLogSQL(logTableName);
ps = conn.prepareStatement(sql);
- ps.setString(1, tccFenceDO.getXid());
- ps.setLong(2, tccFenceDO.getBranchId());
- ps.setString(3, tccFenceDO.getActionName());
- ps.setInt(4, tccFenceDO.getStatus());
+ ps.setString(1, commonFenceDO.getXid());
+ ps.setLong(2, commonFenceDO.getBranchId());
+ ps.setString(3, commonFenceDO.getActionName());
+ ps.setInt(4, commonFenceDO.getStatus());
ps.setTimestamp(5, now);
ps.setTimestamp(6, now);
return ps.executeUpdate() > 0;
} catch (SQLIntegrityConstraintViolationException e) {
- throw new TCCFenceException(String.format("Insert tcc fence record duplicate key exception. xid= %s, branchId= %s", tccFenceDO.getXid(), tccFenceDO.getBranchId()),
+ throw new CommonFenceException(String.format("Insert tcc fence record duplicate key exception. xid= %s, branchId= %s", commonFenceDO.getXid(), commonFenceDO.getBranchId()),
FrameworkErrorCode.DuplicateKeyException);
} catch (SQLException e) {
throw new StoreException(e);
@@ -112,10 +112,10 @@ public boolean insertTCCFenceDO(Connection conn, TCCFenceDO tccFenceDO) {
}
@Override
- public boolean updateTCCFenceDO(Connection conn, String xid, Long branchId, int newStatus, int oldStatus) {
+ public boolean updateCommonFenceDO(Connection conn, String xid, Long branchId, int newStatus, int oldStatus) {
PreparedStatement ps = null;
try {
- String sql = TCCFenceStoreSqls.getUpdateStatusSQLByBranchIdAndXid(logTableName);
+ String sql = CommonFenceStoreSqls.getUpdateStatusSQLByBranchIdAndXid(logTableName);
ps = conn.prepareStatement(sql);
ps.setInt(1, newStatus);
// gmt_modified
@@ -132,10 +132,10 @@ public boolean updateTCCFenceDO(Connection conn, String xid, Long branchId, int
}
@Override
- public boolean deleteTCCFenceDO(Connection conn, String xid, Long branchId) {
+ public boolean deleteCommonFenceDO(Connection conn, String xid, Long branchId) {
PreparedStatement ps = null;
try {
- String sql = TCCFenceStoreSqls.getDeleteSQLByBranchIdAndXid(logTableName);
+ String sql = CommonFenceStoreSqls.getDeleteSQLByBranchIdAndXid(logTableName);
ps = conn.prepareStatement(sql);
ps.setString(1, xid);
ps.setLong(2, branchId);
@@ -149,10 +149,10 @@ public boolean deleteTCCFenceDO(Connection conn, String xid, Long branchId) {
}
@Override
- public int deleteTCCFenceDOByDate(Connection conn, Date datetime) {
+ public int deleteCommonFenceDOByDate(Connection conn, Date datetime) {
PreparedStatement ps = null;
try {
- String sql = TCCFenceStoreSqls.getDeleteSQLByDateAndStatus(logTableName);
+ String sql = CommonFenceStoreSqls.getDeleteSQLByDateAndStatus(logTableName);
ps = conn.prepareStatement(sql);
ps.setTimestamp(1, new Timestamp(datetime.getTime()));
return ps.executeUpdate();
diff --git a/tcc/src/main/java/io/seata/rm/tcc/store/db/sql/TCCFenceStoreSqls.java b/spring/src/main/java/io/seata/spring/fence/store/db/sql/CommonFenceStoreSqls.java
similarity index 89%
rename from tcc/src/main/java/io/seata/rm/tcc/store/db/sql/TCCFenceStoreSqls.java
rename to spring/src/main/java/io/seata/spring/fence/store/db/sql/CommonFenceStoreSqls.java
index 130cb132c46..4fc94e91c84 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/store/db/sql/TCCFenceStoreSqls.java
+++ b/spring/src/main/java/io/seata/spring/fence/store/db/sql/CommonFenceStoreSqls.java
@@ -13,18 +13,18 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.store.db.sql;
+package io.seata.spring.fence.store.db.sql;
-import io.seata.rm.tcc.constant.TCCFenceConstant;
+import io.seata.spring.fence.constant.CommonFenceConstant;
/**
* TCC Fence Store Sqls
*
* @author kaka2code
*/
-public class TCCFenceStoreSqls {
+public class CommonFenceStoreSqls {
- private TCCFenceStoreSqls() {
+ private CommonFenceStoreSqls() {
throw new IllegalStateException("Utility class");
}
@@ -63,7 +63,7 @@ private TCCFenceStoreSqls() {
*/
protected static final String DELETE_BY_DATE_AND_STATUS = "delete from " + LOCAL_TCC_LOG_PLACEHOLD
+ " where gmt_modified < ? "
- + " and status in (" + TCCFenceConstant.STATUS_COMMITTED + " , " + TCCFenceConstant.STATUS_ROLLBACKED + " , " + TCCFenceConstant.STATUS_SUSPENDED + ")";
+ + " and status in (" + CommonFenceConstant.STATUS_COMMITTED + " , " + CommonFenceConstant.STATUS_ROLLBACKED + " , " + CommonFenceConstant.STATUS_SUSPENDED + ")";
public static String getInsertLocalTCCLogSQL(String localTccTable) {
return INSERT_LOCAL_TCC_LOG.replace(LOCAL_TCC_LOG_PLACEHOLD, localTccTable);
diff --git a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextFilter.java b/spring/src/main/java/io/seata/spring/interceptor/ActionContextFilter.java
similarity index 96%
rename from tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextFilter.java
rename to spring/src/main/java/io/seata/spring/interceptor/ActionContextFilter.java
index 32a6f492161..e0f86ad70a9 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextFilter.java
+++ b/spring/src/main/java/io/seata/spring/interceptor/ActionContextFilter.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.interceptor;
+package io.seata.spring.interceptor;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
diff --git a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextUtil.java b/spring/src/main/java/io/seata/spring/interceptor/ActionContextUtil.java
similarity index 99%
rename from tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextUtil.java
rename to spring/src/main/java/io/seata/spring/interceptor/ActionContextUtil.java
index 8b50b52bce9..4cc9ee1085c 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionContextUtil.java
+++ b/spring/src/main/java/io/seata/spring/interceptor/ActionContextUtil.java
@@ -13,7 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.interceptor;
+package io.seata.spring.interceptor;
import java.lang.reflect.Field;
import java.util.Collections;
diff --git a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandler.java b/spring/src/main/java/io/seata/spring/interceptor/ActionInterceptorHandler.java
similarity index 63%
rename from tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandler.java
rename to spring/src/main/java/io/seata/spring/interceptor/ActionInterceptorHandler.java
index ea80e3e4d62..23a4c662ed4 100644
--- a/tcc/src/main/java/io/seata/rm/tcc/interceptor/ActionInterceptorHandler.java
+++ b/spring/src/main/java/io/seata/spring/interceptor/ActionInterceptorHandler.java
@@ -13,15 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-package io.seata.rm.tcc.interceptor;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.lang.reflect.UndeclaredThrowableException;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-import javax.annotation.Nonnull;
+package io.seata.spring.interceptor;
import com.alibaba.fastjson.JSON;
import io.seata.common.Constants;
@@ -31,20 +23,31 @@
import io.seata.common.util.CollectionUtils;
import io.seata.common.util.NetUtil;
import io.seata.core.context.RootContext;
-import io.seata.core.model.BranchType;
import io.seata.rm.DefaultResourceManager;
-import io.seata.rm.tcc.TCCFenceHandler;
import io.seata.rm.tcc.api.BusinessActionContext;
import io.seata.rm.tcc.api.BusinessActionContextParameter;
import io.seata.rm.tcc.api.BusinessActionContextUtil;
import io.seata.rm.tcc.api.ParamType;
-import io.seata.rm.tcc.api.TwoPhaseBusinessAction;
+import io.seata.spring.fence.CommonFenceHandler;
+import io.seata.spring.remoting.RemotingDesc;
+import io.seata.spring.util.DubboUtil;
+import io.seata.spring.util.SpringProxyUtils;
+import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+
/**
- * Handler the TCC Participant Aspect : Setting Context, Creating Branch Record
+ * Handler the Tx Participant Aspect : Setting Context, Creating Branch Record
*
* @author zhangsen
*/
@@ -53,30 +56,32 @@ public class ActionInterceptorHandler {
private static final Logger LOGGER = LoggerFactory.getLogger(ActionInterceptorHandler.class);
/**
- * Handler the TCC Aspect
+ * Handler the Tx Aspect
*
- * @param method the method
- * @param arguments the arguments
- * @param businessAction the business action
- * @param targetCallback the target callback
+ * @param method the method
+ * @param arguments the arguments
+ * @param businessActionParam the business action params
+ * @param targetCallback the target callback
* @return the business result
* @throws Throwable the throwable
*/
- public Object proceed(Method method, Object[] arguments, String xid, TwoPhaseBusinessAction businessAction,
- Callback