callback) {
try (Jedis jedis = jedisPool.getResource()) {
return callback.doInJedis(jedis);
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return null;
}
/**
* SCAN cursor [MATCH pattern] [COUNT count]
* $redis-cli scan 0 match key99* count 1000
* 1) "13912"
* 2) 1) "key997"
* 2) "key9906"
* 3) "key9957"
* 4) "key9902"
* 5) "key9971"
* 6) "key9935"
* 7) "key9958"
* 8) "key9928"
* 9) "key9931"
* 10) "key9961"
* 11) "key9948"
* 12) "key9965"
* 13) "key9937"
*
* $redis-cli scan 13912 match key99* count 1000
* 1) "5292"
* 2) 1) "key996"
* 2) "key9960"
* 3) "key9973"
* 4) "key9978"
* 5) "key9927"
* 6) "key995"
* 7) "key9992"
* 8) "key9993"
* 9) "key9964"
* 10) "key9934"
* ؽΪ֣һּ 1) һεα꣬ڶּ 2) DZε
*
* @param pattern
* @param count
* @return
*/
public static ScanParams buildDefaultScanParams(String pattern, int count) {
return new ScanParams().match(pattern).count(count);
}
/**
* Ƿ֧scanJedis
*
* @param jedis
* @return
*/
public static Boolean isSupportScanCommand(Jedis jedis) {
try {
ScanParams scanParams = buildDefaultScanParams(SCAN_TEST_PATTERN, SCAN_COUNT);
jedis.scan(SCAN_INIT_CURSOR, scanParams);
} catch (JedisDataException e) {
logger.error(e.getMessage(), e);
logger.info("Redis **NOT** support scan command");
return false;
}
logger.info("Redis support scan command");
return true;
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/repository/helper/TransactionSerializer.java
================================================
package io.tyloo.repository.helper;
import io.tyloo.Transaction;
import io.tyloo.serializer.ObjectSerializer;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
/*
*
* л
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:08 2019/5/8
*
*/
public class TransactionSerializer {
public static byte[] serialize(ObjectSerializer serializer, Transaction transaction) throws CloneNotSupportedException {
Map map = new HashMap<>();
map.put("GLOBAL_TX_ID", transaction.getXid().getGlobalTransactionId());
map.put("BRANCH_QUALIFIER", transaction.getXid().getBranchQualifier());
map.put("STATUS", transaction.getStatus().getId());
map.put("TRANSACTION_TYPE", transaction.getTransactionType().getId());
map.put("RETRIED_COUNT", transaction.getRetriedCount());
map.put("CREATE_TIME", transaction.getCreateTime());
map.put("LAST_UPDATE_TIME", transaction.getLastUpdateTime());
map.put("VERSION", transaction.getVersion());
map.put("CONTENT", serializer.serialize(transaction));
return serializer.serialize(map);
}
public static Transaction deserialize(ObjectSerializer serializer, byte[] value) {
Map map = (Map) serializer.deserialize(value);
byte[] content = (byte[]) map.get("CONTENT");
Transaction transaction = (Transaction) serializer.deserialize(content);
transaction.resetRetriedCount((Integer) map.get("RETRIED_COUNT"));
transaction.setLastUpdateTime((Date) map.get("LAST_UPDATE_TIME"));
transaction.setVersion((Long) map.get("VERSION"));
return transaction;
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/serializer/JacksonJsonSerializer.java
================================================
package io.tyloo.serializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import io.tyloo.SystemException;
import io.tyloo.Transaction;
import java.io.IOException;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:29 2019/12/4
*
*/
public class JacksonJsonSerializer implements ObjectSerializer {
private static ObjectMapper objectMapper = new ObjectMapper();
static {
// objectMapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL,"@class");
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
objectMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
}
@Override
public byte[] serialize(Transaction transaction) {
try {
return objectMapper.writeValueAsBytes(transaction);
} catch (JsonProcessingException e) {
throw new SystemException(String.format("serialize object failed. object:%s", transaction), e);
}
}
@Override
public Transaction deserialize(byte[] bytes) {
try {
return objectMapper.readValue(bytes, Transaction.class);
} catch (IOException e) {
throw new SystemException(String.format("deserialize object failed."), e);
}
}
@Override
public Transaction clone(Transaction object) {
return deserialize(serialize(object));
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/serializer/JdkSerializationSerializer.java
================================================
package io.tyloo.serializer;
import io.tyloo.Transaction;
import org.apache.commons.lang3.SerializationUtils;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:29 2019/5/22
*
*/
public class JdkSerializationSerializer implements ObjectSerializer {
@Override
public byte[] serialize(Transaction object) {
return SerializationUtils.serialize(object);
}
@Override
public Transaction deserialize(byte[] bytes) {
if (bytes == null) {
return null;
} else {
return (Transaction) SerializationUtils.deserialize(bytes);
}
}
@Override
public Transaction clone(Transaction object) {
return SerializationUtils.clone(object);
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/serializer/KryoPoolSerializer.java
================================================
package io.tyloo.serializer;
import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import com.esotericsoftware.kryo.pool.KryoCallback;
import com.esotericsoftware.kryo.pool.KryoFactory;
import com.esotericsoftware.kryo.pool.KryoPool;
import io.tyloo.Transaction;
import org.objenesis.strategy.StdInstantiatorStrategy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:29 2019/5/22
*
*/
public class KryoPoolSerializer implements ObjectSerializer {
static KryoFactory factory = () -> {
Kryo kryo = new Kryo();
kryo.setReferences(true);
kryo.setRegistrationRequired(false);
//Fix the NPE bug when deserializing Collections.
((Kryo.DefaultInstantiatorStrategy) kryo.getInstantiatorStrategy())
.setFallbackInstantiatorStrategy(new StdInstantiatorStrategy());
return kryo;
};
KryoPool pool = new KryoPool.Builder(factory).softReferences().build();
private int initPoolSize = 300;
public KryoPoolSerializer() {
init();
}
public KryoPoolSerializer(int initPoolSize) {
this.initPoolSize = initPoolSize;
init();
}
private void init() {
for (int i = 0; i < initPoolSize; i++) {
Kryo kryo = pool.borrow();
pool.release(kryo);
}
}
@Override
public byte[] serialize(final Transaction object) {
return pool.run(kryo -> {
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
Output output = new Output(byteArrayOutputStream);
kryo.writeClassAndObject(output, object);
output.flush();
return byteArrayOutputStream.toByteArray();
});
}
@Override
public Transaction deserialize(final byte[] bytes) {
return pool.run(kryo -> {
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
Input input = new Input(byteArrayInputStream);
return (Transaction) kryo.readClassAndObject(input);
});
}
@Override
public Transaction clone(final Transaction object) {
return pool.run(kryo -> kryo.copy(object));
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/serializer/ObjectSerializer.java
================================================
package io.tyloo.serializer;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:29 2019/5/22
*
*/
public interface ObjectSerializer {
/**
* Serialize the given object to binary data.
*
* @param t object to serialize
* @return the equivalent binary data
*/
byte[] serialize(T t);
/**
* Deserialize an object from the given binary data.
*
* @param bytes object binary representation
* @return the equivalent object instance
*/
T deserialize(byte[] bytes);
T clone(T object);
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/support/BeanFactory.java
================================================
package io.tyloo.support;
/*
*
* Bean
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:34 2019/5/27
*
*/
public interface BeanFactory {
T getBean(Class var1);
boolean isFactoryOf(Class clazz);
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/support/FactoryBuilder.java
================================================
package io.tyloo.support;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/*
*
* Bean
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:35 2019/5/27
*
*/
public final class FactoryBuilder {
private FactoryBuilder() {
}
/**
* Bean
*/
private static List beanFactories = new ArrayList();
/**
* Bean ӳ
*/
private static ConcurrentHashMap classFactoryMap = new ConcurrentHashMap<>();
/**
* ָ
*
* @param
* @param clazz ָ
* @return
*/
public static SingeltonFactory factoryOf(Class clazz) {
if (!classFactoryMap.containsKey(clazz)) {
for (BeanFactory beanFactory : beanFactories) {
if (beanFactory.isFactoryOf(clazz)) {
classFactoryMap.putIfAbsent(clazz, new SingeltonFactory(clazz, beanFactory.getBean(clazz)));
}
}
if (!classFactoryMap.containsKey(clazz)) {
classFactoryMap.putIfAbsent(clazz, new SingeltonFactory(clazz));
}
}
return classFactoryMap.get(clazz);
}
/**
* BeanעᵽǰBuilder
*
* @param beanFactory Bean
*/
public static void registerBeanFactory(BeanFactory beanFactory) {
beanFactories.add(beanFactory);
}
/**
*
*
* @param
*/
public static class SingeltonFactory {
private volatile T instance = null;
private String className;
public SingeltonFactory(Class clazz, T instance) {
this.className = clazz.getName();
this.instance = instance;
}
public SingeltonFactory(Class clazz) {
this.className = clazz.getName();
}
/**
* õ
*
* @return
*/
public T getInstance() {
if (instance == null) {
synchronized (SingeltonFactory.class) {
if (instance == null) {
try {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class> clazz = loader.loadClass(className);
instance = (T) clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException("Failed to create an instance of " + className, e);
}
}
}
}
return instance;
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (other == null || getClass() != other.getClass()) {
return false;
}
SingeltonFactory that = (SingeltonFactory) other;
return className.equals(that.className);
}
@Override
public int hashCode() {
return className.hashCode();
}
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/support/TransactionConfigurator.java
================================================
package io.tyloo.support;
import io.tyloo.TransactionManager;
import io.tyloo.TransactionRepository;
import io.tyloo.recover.TylooRecoverConfiguration;
/*
*
*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:34 2019/5/28
*
*/
public interface TransactionConfigurator {
/**
* ȡ.
*
* @return
*/
TransactionManager getTransactionManager();
/**
* ȡ.
*
* @return
*/
TransactionRepository getTransactionRepository();
/**
* ȡָ.
*
* @return
*/
TylooRecoverConfiguration getTylooRecoverConfiguration();
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/utils/ByteUtils.java
================================================
package io.tyloo.utils;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:49 2019/5/30
*
*/
public class ByteUtils {
public static byte[] longToBytes(long num) {
return String.valueOf(num).getBytes();
}
public static long bytesToLong(byte[] bytes) {
return Long.valueOf(new String(bytes));
}
public static byte[] intToBytes(int num) {
return String.valueOf(num).getBytes();
}
public static int bytesToInt(byte[] bytes) {
return Integer.valueOf(new String(bytes));
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/utils/CollectionUtils.java
================================================
package io.tyloo.utils;
import java.util.Collection;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:49 2019/5/30
*
*/
public final class CollectionUtils {
private CollectionUtils() {
}
public static boolean isEmpty(Collection collection) {
return (collection == null || collection.isEmpty());
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/utils/ReflectionUtils.java
================================================
package io.tyloo.utils;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;
import java.util.Map;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 18:07 2019/5/19
*
*/
public class ReflectionUtils {
public static void makeAccessible(Method method) {
if ((!Modifier.isPublic(method.getModifiers()) || !Modifier.isPublic(method.getDeclaringClass().getModifiers())) && !method.isAccessible()) {
method.setAccessible(true);
}
}
public static Object changeAnnotationValue(Annotation annotation, String key, Object newValue) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException {
Object handler = Proxy.getInvocationHandler(annotation);
Field f;
f = handler.getClass().getDeclaredField("memberValues");
f.setAccessible(true);
Map memberValues;
memberValues = (Map) f.get(handler);
Object oldValue = memberValues.get(key);
if (oldValue == null || oldValue.getClass() != newValue.getClass()) {
throw new IllegalArgumentException();
}
memberValues.put(key, newValue);
return oldValue;
}
public static Class getDeclaringType(Class aClass, String methodName, Class>[] parameterTypes) {
Method method = null;
Class findClass = aClass;
do {
Class[] clazzes = findClass.getInterfaces();
for (Class clazz : clazzes) {
try {
method = clazz.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException e) {
method = null;
}
if (method != null) {
return clazz;
}
}
findClass = findClass.getSuperclass();
} while (!findClass.equals(Object.class));
return aClass;
}
public static Object getNullValue(Class type) {
if (boolean.class.equals(type)) {
return false;
} else if (byte.class.equals(type)) {
return 0;
} else if (short.class.equals(type)) {
return 0;
} else if (int.class.equals(type)) {
return 0;
} else if (long.class.equals(type)) {
return 0;
} else if (float.class.equals(type)) {
return 0;
} else if (double.class.equals(type)) {
return 0;
} else if (char.class.equals(type)) {
return ' ';
}
return null;
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/utils/StringUtils.java
================================================
package io.tyloo.utils;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:49 2019/5/30
*
*/
public class StringUtils {
public static boolean isNotEmpty(String value) {
if(value == null) {
return false;
}
if(value.equals("")) {
return false;
}
return true;
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/utils/TransactionUtils.java
================================================
package io.tyloo.utils;
import io.tyloo.api.Propagation;
import io.tyloo.interceptor.TylooMethodContext;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:49 2019/5/30
*
*/
public class TransactionUtils {
public static boolean isLegalTransactionContext(boolean isTransactionActive, TylooMethodContext tylooMethodContext) {
return !tylooMethodContext.getPropagation().equals(Propagation.MANDATORY) ||
isTransactionActive ||
tylooMethodContext.getTransactionContext() != null;
}
}
================================================
FILE: tyloo-core/src/main/java/io/tyloo/utils/TylooMethodUtils.java
================================================
package io.tyloo.utils;
import io.tyloo.api.Propagation;
import io.tyloo.api.TransactionContext;
import io.tyloo.api.Tyloo;
import io.tyloo.common.MethodRole;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import java.lang.reflect.Method;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 19:49 2019/5/30
*
*/
public class TylooMethodUtils {
public static Method getTylooMethod(ProceedingJoinPoint pjp) {
Method method = ((MethodSignature) (pjp.getSignature())).getMethod();
if (method.getAnnotation(Tyloo.class) == null) {
try {
method = pjp.getTarget().getClass().getMethod(method.getName(), method.getParameterTypes());
} catch (NoSuchMethodException e) {
return null;
}
}
return method;
}
public static MethodRole calculateMethodType(Propagation propagation, boolean isTransactionActive, TransactionContext transactionContext) {
if ((propagation.equals(Propagation.REQUIRED) && !isTransactionActive && transactionContext == null) ||
propagation.equals(Propagation.REQUIRES_NEW)) {
return MethodRole.ROOT;
} else if ((propagation.equals(Propagation.REQUIRED) || propagation.equals(Propagation.MANDATORY)) && !isTransactionActive && transactionContext != null) {
return MethodRole.PROVIDER;
} else {
return MethodRole.NORMAL;
}
}
public static int getTransactionContextParamPosition(Class>[] parameterTypes) {
int position = -1;
for (int i = 0; i < parameterTypes.length; i++) {
if (parameterTypes[i].equals(TransactionContext.class)) {
position = i;
break;
}
}
return position;
}
}
================================================
FILE: tyloo-dubbo/pom.xml
================================================
tyloo
io.tyloo
1.1.0
4.0.0
tyloo-dubbo
io.tyloo
tyloo-api
io.tyloo
tyloo-core
org.apache.dubbo
dubbo
org.apache.dubbo
dubbo-dependencies-zookeeper
pom
org.aspectj
aspectjweaver
com.alibaba
fastjson
================================================
FILE: tyloo-dubbo/src/main/java/io/tyloo/dubbo/constants/TransactionContextConstants.java
================================================
package io.tyloo.dubbo.constants;
/*
*
* ij
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 9:33 2019/7/5
*
*/
public class TransactionContextConstants {
public static final String TRANSACTION_CONTEXT = "TRANSACTION_CONTEXT";
}
================================================
FILE: tyloo-dubbo/src/main/java/io/tyloo/dubbo/context/DubboTransactionContextEditor.java
================================================
package io.tyloo.dubbo.context;
import com.alibaba.fastjson.JSON;
import io.tyloo.api.TransactionContext;
import io.tyloo.api.TransactionContextEditor;
import io.tyloo.dubbo.constants.TransactionContextConstants;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.RpcContext;
import java.lang.reflect.Method;
/*
* Dubbo ı༭
*
* ײʹõdubboTransactionContextEditorΪDubboTransactionContextEditor.classʹdubboʽηʽ
* ͨ Dubbo ʽεķʽ Dubbo Service ӿ TransactionContext Խӿڲһ
* tyloo ͨ Dubbo Proxy Ļƣʵ `@Tyloo` Զ
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 15:32 2019/7/17
*
*/
public class DubboTransactionContextEditor implements TransactionContextEditor {
@Override
public TransactionContext get(Object target, Method method, Object[] args) {
String context = RpcContext.getContext().getAttachment(TransactionContextConstants.TRANSACTION_CONTEXT);
if (StringUtils.isNotEmpty(context)) {
return JSON.parseObject(context, TransactionContext.class);
}
return null;
}
@Override
public void set(TransactionContext transactionContext, Object target, Method method, Object[] args) {
RpcContext.getContext().setAttachment(TransactionContextConstants.TRANSACTION_CONTEXT, JSON.toJSONString(transactionContext));
}
}
================================================
FILE: tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/javassist/TylooClassGenerator.java
================================================
package io.tyloo.dubbo.proxy.javassist;
import javassist.*;
import javassist.bytecode.AnnotationsAttribute;
import javassist.bytecode.ConstPool;
import javassist.bytecode.annotation.Annotation;
import javassist.bytecode.annotation.ClassMemberValue;
import javassist.bytecode.annotation.EnumMemberValue;
import javassist.bytecode.annotation.StringMemberValue;
import org.apache.dubbo.common.utils.ReflectUtils;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
/*
*
* TCC Javassist ʵ֡
* Javassist һԴķ༭ʹ Java ֽ⡣
* һ Dubbo ServiceTccProxy ᶯ̬ࣺ
- Dubbo Service Proxy
- Dubbo Service ProxyFactoryɶӦ Dubbo Service Proxy
*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 9:33 2019/8/1
*
*/
public final class TylooClassGenerator {
private static final AtomicLong CLASS_NAME_COUNTER = new AtomicLong(0);
private static final String SIMPLE_NAME_TAG = "";
private static final Map POOL_MAP = new ConcurrentHashMap(); //ClassLoader - ClassPool
private final Set tylooMethods = new HashSet();
private ClassPool mPool;
private CtClass mCtc;
private String mClassName, mSuperClass;
private Set mInterfaces;
private List mFields, mConstructors, mMethods;
private Map mCopyMethods; //
private Map> mCopyConstructors; //
private boolean mDefaultConstructor = false;
private TylooClassGenerator() {
}
private TylooClassGenerator(ClassPool pool) {
mPool = pool;
}
public static TylooClassGenerator newInstance() {
return new TylooClassGenerator(getClassPool(Thread.currentThread().getContextClassLoader()));
}
public static TylooClassGenerator newInstance(ClassLoader loader) {
return new TylooClassGenerator(getClassPool(loader));
}
public static boolean isDynamicClass(Class> cl) {
return TylooClassGenerator.DC.class.isAssignableFrom(cl);
}
public static ClassPool getClassPool(ClassLoader loader) {
if (loader == null) {
return ClassPool.getDefault();
}
ClassPool pool = POOL_MAP.get(loader);
if (pool == null) {
pool = new ClassPool(true);
pool.appendClassPath(new LoaderClassPath(loader));
POOL_MAP.put(loader, pool);
}
return pool;
}
private static String modifier(int mod) {
if (java.lang.reflect.Modifier.isPublic(mod)) {
return "public";
}
if (java.lang.reflect.Modifier.isProtected(mod)) {
return "protected";
}
if (java.lang.reflect.Modifier.isPrivate(mod)) {
return "private";
}
return "";
}
public String getClassName() {
return mClassName;
}
public TylooClassGenerator setClassName(String name) {
mClassName = name;
return this;
}
public TylooClassGenerator addInterface(String cn) {
if (mInterfaces == null) {
mInterfaces = new HashSet();
}
mInterfaces.add(cn);
return this;
}
public TylooClassGenerator addInterface(Class> cl) {
return addInterface(cl.getName());
}
public TylooClassGenerator setSuperClass(String cn) {
mSuperClass = cn;
return this;
}
public TylooClassGenerator setSuperClass(Class> cl) {
mSuperClass = cl.getName();
return this;
}
public TylooClassGenerator addField(String code) {
if (mFields == null) {
mFields = new ArrayList();
}
mFields.add(code);
return this;
}
public TylooClassGenerator addField(String name, int mod, Class> type) {
return addField(name, mod, type, null);
}
public TylooClassGenerator addField(String name, int mod, Class> type, String def) {
StringBuilder sb = new StringBuilder();
sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(type)).append(' ');
sb.append(name);
if (def != null && def.length() > 0) {
sb.append('=');
sb.append(def);
}
sb.append(';');
return addField(sb.toString());
}
public TylooClassGenerator addMethod(String code) {
if (mMethods == null) {
mMethods = new ArrayList();
}
mMethods.add(code);
return this;
}
public TylooClassGenerator addMethod(String name, int mod, Class> rt, Class>[] pts, String body) {
return addMethod(false, name, mod, rt, pts, null, body);
}
public TylooClassGenerator addMethod(boolean isTylooMethod, String name, int mod, Class> rt, Class>[] pts, Class>[] ets, String body) {
StringBuilder sb = new StringBuilder();
sb.append(modifier(mod)).append(' ').append(ReflectUtils.getName(rt)).append(' ').append(name);
sb.append('(');
for (int i = 0; i < pts.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(ReflectUtils.getName(pts[i]));
sb.append(" arg").append(i);
}
sb.append(')');
if (ets != null && ets.length > 0) {
sb.append(" throws ");
for (int i = 0; i < ets.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(ReflectUtils.getName(ets[i]));
}
}
sb.append('{').append(body).append('}');
if (isTylooMethod) {
tylooMethods.add(sb.toString());
}
return addMethod(sb.toString());
}
public TylooClassGenerator addMethod(Method m) {
addMethod(m.getName(), m);
return this;
}
public TylooClassGenerator addMethod(String name, Method m) {
String desc = name + ReflectUtils.getDescWithoutMethodName(m);
addMethod(':' + desc);
if (mCopyMethods == null) {
mCopyMethods = new ConcurrentHashMap(8);
}
mCopyMethods.put(desc, m);
return this;
}
public TylooClassGenerator addConstructor(String code) {
if (mConstructors == null) {
mConstructors = new LinkedList();
}
mConstructors.add(code);
return this;
}
public TylooClassGenerator addConstructor(int mod, Class>[] pts, String body) {
return addConstructor(mod, pts, null, body);
}
public TylooClassGenerator addConstructor(int mod, Class>[] pts, Class>[] ets, String body) {
StringBuilder sb = new StringBuilder();
sb.append(modifier(mod)).append(' ').append(SIMPLE_NAME_TAG);
sb.append('(');
for (int i = 0; i < pts.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(ReflectUtils.getName(pts[i]));
sb.append(" arg").append(i);
}
sb.append(')');
if (ets != null && ets.length > 0) {
sb.append(" throws ");
for (int i = 0; i < ets.length; i++) {
if (i > 0) {
sb.append(',');
}
sb.append(ReflectUtils.getName(ets[i]));
}
}
sb.append('{').append(body).append('}');
return addConstructor(sb.toString());
}
public TylooClassGenerator addConstructor(Constructor> c) {
String desc = ReflectUtils.getDesc(c);
addConstructor(":" + desc);
if (mCopyConstructors == null) {
mCopyConstructors = new ConcurrentHashMap>(4);
}
mCopyConstructors.put(desc, c);
return this;
}
public TylooClassGenerator addDefaultConstructor() {
mDefaultConstructor = true;
return this;
}
public ClassPool getClassPool() {
return mPool;
}
public Class> toClass() {
if (mCtc != null) {
mCtc.detach();
}
long id = CLASS_NAME_COUNTER.getAndIncrement();
try {
CtClass ctcs = mSuperClass == null ? null : mPool.get(mSuperClass);
if (mClassName == null) {
mClassName = (mSuperClass == null || Modifier.isPublic(ctcs.getModifiers())
? TylooClassGenerator.class.getName() : mSuperClass + "$sc") + id;
}
mCtc = mPool.makeClass(mClassName);
if (mSuperClass != null) {
mCtc.setSuperclass(ctcs);
}
mCtc.addInterface(mPool.get(DC.class.getName())); // add dynamic class tag.
if (mInterfaces != null) {
for (String cl : mInterfaces) {
mCtc.addInterface(mPool.get(cl));
}
}
if (mFields != null) {
for (String code : mFields) {
mCtc.addField(CtField.make(code, mCtc));
}
}
if (mMethods != null) {
for (String code : mMethods) {
if (code.charAt(0) == ':') {
mCtc.addMethod(CtNewMethod.copy(getCtMethod(mCopyMethods.get(code.substring(1))), code.substring(1, code.indexOf('(')), mCtc, null));
} else {
CtMethod ctMethod = CtNewMethod.make(code, mCtc);
// עⷽ
if (tylooMethods.contains(code)) {
ConstPool constpool = mCtc.getClassFile().getConstPool();
AnnotationsAttribute attr = new AnnotationsAttribute(constpool, AnnotationsAttribute.visibleTag);
Annotation annot = new Annotation("Tyloo", constpool);
EnumMemberValue enumMemberValue = new EnumMemberValue(constpool);
enumMemberValue.setType("Propagation");
enumMemberValue.setValue("SUPPORTS");
annot.addMemberValue("propagation", enumMemberValue);
annot.addMemberValue("confirmMethod", new StringMemberValue(ctMethod.getName(), constpool));
annot.addMemberValue("cancelMethod", new StringMemberValue(ctMethod.getName(), constpool));
ClassMemberValue classMemberValue = new ClassMemberValue("DubboTransactionContextEditor", constpool);
annot.addMemberValue("transactionContextEditor", classMemberValue);
attr.addAnnotation(annot);
ctMethod.getMethodInfo().addAttribute(attr);
}
mCtc.addMethod(ctMethod);
}
}
}
if (mDefaultConstructor) {
mCtc.addConstructor(CtNewConstructor.defaultConstructor(mCtc));
}
if (mConstructors != null) {
for (String code : mConstructors) {
if (code.charAt(0) == ':') {
mCtc.addConstructor(CtNewConstructor.copy(getCtConstructor(mCopyConstructors.get(code.substring(1))), mCtc, null));
} else {
String[] sn = mCtc.getSimpleName().split("\\$+"); // inner class name include $.
mCtc.addConstructor(CtNewConstructor.make(code.replaceFirst(SIMPLE_NAME_TAG, sn[sn.length - 1]), mCtc));
}
}
}
return mCtc.toClass();
} catch (RuntimeException e) {
throw e;
} catch (NotFoundException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (CannotCompileException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
public void release() {
if (mCtc != null) {
mCtc.detach();
}
if (mInterfaces != null) {
mInterfaces.clear();
}
if (mFields != null) {
mFields.clear();
}
if (mMethods != null) {
mMethods.clear();
}
if (mConstructors != null) {
mConstructors.clear();
}
if (mCopyMethods != null) {
mCopyMethods.clear();
}
if (mCopyConstructors != null) {
mCopyConstructors.clear();
}
}
private CtClass getCtClass(Class> c) throws NotFoundException {
return mPool.get(c.getName());
}
private CtMethod getCtMethod(Method m) throws NotFoundException {
return getCtClass(m.getDeclaringClass()).getMethod(m.getName(), ReflectUtils.getDescWithoutMethodName(m));
}
private CtConstructor getCtConstructor(Constructor> c) throws NotFoundException {
return getCtClass(c.getDeclaringClass()).getConstructor(ReflectUtils.getDesc(c));
}
public interface DC {
} // dynamic class tag interface.
}
================================================
FILE: tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/javassist/TylooJavassistProxyFactory.java
================================================
package io.tyloo.dubbo.proxy.javassist;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;
import org.apache.dubbo.rpc.proxy.javassist.JavassistProxyFactory;
/*
*
*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 9:33 2019/8/16
*
*/
public class TylooJavassistProxyFactory extends JavassistProxyFactory {
/**
* ĿʱgetProxy(...)` Dubbo Service Proxy
* com.alibaba.dubbo.rpc.proxy.InvokerInvocationHandler`Dubbo ô
*
* @param invoker
* @param interfaces
* @param
* @return
*/
@Override
@SuppressWarnings("unchecked")
public T getProxy(Invoker invoker, Class>[] interfaces) {
return (T) TylooProxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
}
}
================================================
FILE: tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/javassist/TylooProxy.java
================================================
/*
* Copyright 1999-2011 Alibaba 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.tyloo.dubbo.proxy.javassist;
import io.tyloo.api.Tyloo;
import org.apache.dubbo.common.utils.ClassHelper;
import org.apache.dubbo.common.utils.ReflectUtils;
import java.lang.ref.Reference;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.*;
import java.util.concurrent.atomic.AtomicLong;
/*
*
* TCC Proxy Dubbo Service Proxy
*
* ο Dubbo Դʵ֣
* com.alibaba.dubbo.Enums.bytecode.Proxy
* com.alibaba.dubbo.Enums.bytecode.ClassGenerator
* com.alibaba.dubbo.Enums.bytecode.Wrapper
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 9:34 2019/8/27
*
*/
public abstract class TylooProxy {
public static final InvocationHandler RETURN_NULL_INVOKER = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) {
return null;
}
};
public static final InvocationHandler THROW_UNSUPPORTED_INVOKER = (proxy, method, args) -> {
throw new UnsupportedOperationException("Method [" + ReflectUtils.getName(method) + "] unimplemented.");
};
private static final AtomicLong PROXY_CLASS_COUNTER = new AtomicLong(0);
private static final String PACKAGE_NAME = TylooProxy.class.getPackage().getName();
private static final Map> ProxyCacheMap = new WeakHashMap>();
private static final Object PendingGenerationMarker = new Object();
protected TylooProxy() {
}
/**
* TCC Proxy
*
* @param ics interface class array.
* @return TccProxy instance.
*/
public static TylooProxy getProxy(Class>... ics) {
return getProxy(ClassHelper.getCallerClassLoader(TylooProxy.class), ics);
}
/**
* 1. Уӿ
* 2. ʹýӿڼ `;` ָƴӣΪ Proxy Ψһʶ
* 3. Proxy Ӧ ClassLoader
* 4. һֱ Tyloo Proxy ֱɹ
* 5. Dubbo Service ProxyFactoryProxy Ĵ
* 6. Dubbo Service Proxy Ĵ롣
*
* @param cl
* @param ics
* @return
*/
public static TylooProxy getProxy(ClassLoader cl, Class>... ics) {
if (ics.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
StringBuilder sb = new StringBuilder();
for (int i = 0; i < ics.length; i++) {
String itf = ics[i].getName();
if (!ics[i].isInterface()) {
throw new RuntimeException(itf + " is not a interface.");
}
Class> tmp = null;
try {
tmp = Class.forName(itf, false, cl);
} catch (ClassNotFoundException e) {
}
if (tmp != ics[i]) {
throw new IllegalArgumentException(ics[i] + " is not visible from class loader");
}
sb.append(itf).append(';');
}
// use interface class name list as key.
String key = sb.toString();
// get cache by class loader.
Map cache;
synchronized (ProxyCacheMap) {
cache = ProxyCacheMap.get(cl);
if (cache == null) {
cache = new HashMap();
ProxyCacheMap.put(cl, cache);
}
}
TylooProxy proxy = null;
synchronized (cache) {
do {
Object value = cache.get(key);
if (value instanceof Reference>) {
proxy = (TylooProxy) ((Reference>) value).get();
if (proxy != null) {
return proxy;
}
}
if (value == PendingGenerationMarker) {
try {
cache.wait();
} catch (InterruptedException e) {
}
} else {
cache.put(key, PendingGenerationMarker);
break;
}
}
while (true);
}
/**
* Dubbo Service Proxy Ĵ
*/
long id = PROXY_CLASS_COUNTER.getAndIncrement();
String pkg = null;
TylooClassGenerator ccp = null, ccm = null;
try {
ccp = TylooClassGenerator.newInstance(cl);
Set worked = new HashSet();
List methods = new ArrayList();
for (int i = 0; i < ics.length; i++) {
if (!Modifier.isPublic(ics[i].getModifiers())) {
String npkg = ics[i].getPackage().getName();
if (pkg == null) {
pkg = npkg;
} else {
if (pkg.equals(npkg)) {
} else {
throw new IllegalArgumentException("non-public interfaces from different packages");
}
}
}
ccp.addInterface(ics[i]);
for (Method method : ics[i].getMethods()) {
String desc = ReflectUtils.getDesc(method);
if (worked.contains(desc)) {
continue;
}
worked.add(desc);
int ix = methods.size();
Class> rt = method.getReturnType();
Class>[] pts = method.getParameterTypes();
StringBuilder code = new StringBuilder("Object[] args = new Object[").append(pts.length).append("];");
for (int j = 0; j < pts.length; j++) {
code.append(" args[").append(j).append("] = ($w)$").append(j + 1).append(";");
}
code.append(" Object ret = handler.invoke(this, methods[" + ix + "], args);");
if (!Void.TYPE.equals(rt)) {
code.append(" return ").append(asArgument(rt, "ret")).append(";");
}
methods.add(method);
StringBuilder tylooDesc = new StringBuilder();
Tyloo tyloo = method.getAnnotation(Tyloo.class);
ccp.addMethod(tyloo != null, method.getName(), method.getModifiers(), rt, pts, method.getExceptionTypes(), code.toString());
}
}
if (pkg == null) {
pkg = PACKAGE_NAME;
}
// create ProxyInstance class.
String pcn = pkg + ".proxy" + id;
ccp.setClassName(pcn);
ccp.addField("public static java.lang.reflect.Method[] methods;");
ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
ccp.addConstructor(Modifier.PUBLIC, new Class>[]{InvocationHandler.class}, new Class>[0], "handler=$1;");
ccp.addDefaultConstructor();
Class> clazz = ccp.toClass();
clazz.getField("methods").set(null, methods.toArray(new Method[0]));
// create TccProxy class.
String fcn = TylooProxy.class.getName() + id;
ccm = TylooClassGenerator.newInstance(cl);
ccm.setClassName(fcn);
ccm.addDefaultConstructor();
ccm.setSuperClass(TylooProxy.class);
ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
Class> pc = ccm.toClass();
proxy = (TylooProxy) pc.newInstance();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e.getMessage(), e);
} finally {
// release TccClassGenerator
if (ccp != null) {
ccp.release();
}
if (ccm != null) {
ccm.release();
}
synchronized (cache) {
if (proxy == null) {
cache.remove(key);
} else {
cache.put(key, new WeakReference(proxy));
}
cache.notifyAll();
}
}
return proxy;
}
private static String asArgument(Class> cl, String name) {
if (cl.isPrimitive()) {
if (Boolean.TYPE == cl) {
return name + "==null?false:((Boolean)" + name + ").booleanValue()";
}
if (Byte.TYPE == cl) {
return name + "==null?(byte)0:((Byte)" + name + ").byteValue()";
}
if (Character.TYPE == cl) {
return name + "==null?(char)0:((Character)" + name + ").charValue()";
}
if (Double.TYPE == cl) {
return name + "==null?(double)0:((Double)" + name + ").doubleValue()";
}
if (Float.TYPE == cl) {
return name + "==null?(float)0:((Float)" + name + ").floatValue()";
}
if (Integer.TYPE == cl) {
return name + "==null?(int)0:((Integer)" + name + ").intValue()";
}
if (Long.TYPE == cl) {
return name + "==null?(long)0:((Long)" + name + ").longValue()";
}
if (Short.TYPE == cl) {
return name + "==null?(short)0:((Short)" + name + ").shortValue()";
}
throw new RuntimeException(name + " is unknown primitive type.");
}
return "(" + ReflectUtils.getName(cl) + ")" + name;
}
/**
* get instance with default handler.
*
* @return instance.
*/
public Object newInstance() {
return newInstance(THROW_UNSUPPORTED_INVOKER);
}
/**
* get instance with special handler.
*
* @return instance.
*/
abstract public Object newInstance(InvocationHandler handler);
}
================================================
FILE: tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/jdk/MethodProceedingJoinPoint.java
================================================
package io.tyloo.dubbo.proxy.jdk;
import io.tyloo.SystemException;
import io.tyloo.utils.ReflectionUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.reflect.MethodSignature;
import org.aspectj.lang.reflect.SourceLocation;
import org.aspectj.runtime.internal.AroundClosure;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/*
*
* ɷ
*
* ο [`org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint`](https://github.com/spring-projects/spring-framework/blob/master/spring-aop/src/main/java/org/springframework/aop/aspectj/MethodInvocationProceedingJoinPoint.java) ʵ֡
* 洦ɺ `#proceed(...)` Զ Dubbo Service á
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 10:56 2019/9/12
*
*/
public class MethodProceedingJoinPoint implements ProceedingJoinPoint, JoinPoint.StaticPart {
private final Object proxy;
private final Object target;
private final Method method;
private final Object[] args;
private Signature signature;
/**
* Lazily initialized source location object
*/
private SourceLocation sourceLocation;
/**
* @param proxy
* @param target Ŀ
* @param method
* @param args
*/
public MethodProceedingJoinPoint(Object proxy, Object target, Method method, Object[] args) {
this.proxy = proxy;
this.target = target;
this.method = method;
this.args = args;
}
/**
* Զ Dubbo Service
*
* @return
* @throws Throwable
*/
@Override
public void set$AroundClosure(AroundClosure aroundClosure) {
throw new UnsupportedOperationException();
}
@Override
public Object proceed() throws Throwable {
// Use reflection to invoke the method.
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, args);
} catch (InvocationTargetException ex) {
// Invoked method threw a checked exception.
// We must rethrow it. The client won't see the interceptor.
throw ex.getTargetException();
} catch (IllegalArgumentException ex) {
throw new SystemException("Tried calling method [" +
method + "] on target [" + target + "] failed", ex);
} catch (IllegalAccessException ex) {
throw new SystemException("Could not access method [" + method + "]", ex);
}
}
@Override
public Object proceed(Object[] objects) throws Throwable {
// Use reflection to invoke the method.
try {
ReflectionUtils.makeAccessible(method);
return method.invoke(target, objects);
} catch (InvocationTargetException ex) {
throw ex.getTargetException();
} catch (IllegalArgumentException ex) {
throw new SystemException("Tried calling method [" +
method + "] on target [" + target + "] failed", ex);
} catch (IllegalAccessException ex) {
throw new SystemException("Could not access method [" + method + "]", ex);
}
}
@Override
public String toShortString() {
return "execution(" + getSignature().toShortString() + ")";
}
@Override
public String toLongString() {
return "execution(" + getSignature().toLongString() + ")";
}
@Override
public String toString() {
return "execution(" + getSignature().toString() + ")";
}
@Override
public Object getThis() {
return this.proxy;
}
@Override
public Object getTarget() {
return this.target;
}
@Override
public Object[] getArgs() {
return this.args;
}
@Override
public Signature getSignature() {
if (this.signature == null) {
this.signature = new MethodSignatureImpl();
}
return signature;
}
@Override
public SourceLocation getSourceLocation() {
if (this.sourceLocation == null) {
this.sourceLocation = new SourceLocationImpl();
}
return this.sourceLocation;
}
@Override
public String getKind() {
return ProceedingJoinPoint.METHOD_EXECUTION;
}
@Override
public int getId() {
return 0;
}
@Override
public StaticPart getStaticPart() {
return this;
}
/**
* Lazily initialized MethodSignature.
*/
private class MethodSignatureImpl implements MethodSignature {
private volatile String[] parameterNames;
@Override
public String getName() {
return method.getName();
}
@Override
public int getModifiers() {
return method.getModifiers();
}
@Override
public Class getDeclaringType() {
return method.getDeclaringClass();
}
@Override
public String getDeclaringTypeName() {
return method.getDeclaringClass().getName();
}
@Override
public Class getReturnType() {
return method.getReturnType();
}
@Override
public Method getMethod() {
return method;
}
@Override
public Class[] getParameterTypes() {
return method.getParameterTypes();
}
@Override
public String[] getParameterNames() {
throw new UnsupportedOperationException();
}
@Override
public Class[] getExceptionTypes() {
return method.getExceptionTypes();
}
@Override
public String toShortString() {
return toString(false, false, false, false);
}
@Override
public String toLongString() {
return toString(true, true, true, true);
}
@Override
public String toString() {
return toString(false, true, false, true);
}
private String toString(boolean includeModifier, boolean includeReturnTypeAndArgs,
boolean useLongReturnAndArgumentTypeName, boolean useLongTypeName) {
StringBuilder sb = new StringBuilder();
if (includeModifier) {
sb.append(Modifier.toString(getModifiers()));
sb.append(" ");
}
if (includeReturnTypeAndArgs) {
appendType(sb, getReturnType(), useLongReturnAndArgumentTypeName);
sb.append(" ");
}
appendType(sb, getDeclaringType(), useLongTypeName);
sb.append(".");
sb.append(getMethod().getName());
sb.append("(");
Class[] parametersTypes = getParameterTypes();
appendTypes(sb, parametersTypes, includeReturnTypeAndArgs, useLongReturnAndArgumentTypeName);
sb.append(")");
return sb.toString();
}
private void appendTypes(StringBuilder sb, Class>[] types,
boolean includeArgs, boolean useLongReturnAndArgumentTypeName) {
if (includeArgs) {
for (int size = types.length, i = 0; i < size; i++) {
appendType(sb, types[i], useLongReturnAndArgumentTypeName);
if (i < size - 1) {
sb.append(",");
}
}
} else {
if (types.length != 0) {
sb.append("..");
}
}
}
private void appendType(StringBuilder sb, Class> type, boolean useLongTypeName) {
if (type.isArray()) {
appendType(sb, type.getComponentType(), useLongTypeName);
sb.append("[]");
} else {
sb.append(useLongTypeName ? type.getName() : type.getSimpleName());
}
}
}
/**
* Lazily initialized SourceLocation.
*/
private class SourceLocationImpl implements SourceLocation {
@Override
public Class getWithinType() {
if (proxy == null) {
throw new UnsupportedOperationException("No source location joinpoint available: target is null");
}
return proxy.getClass();
}
@Override
public String getFileName() {
throw new UnsupportedOperationException();
}
@Override
public int getLine() {
throw new UnsupportedOperationException();
}
@Override
public int getColumn() {
throw new UnsupportedOperationException();
}
}
}
================================================
FILE: tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/jdk/TylooInvokerInvocationHandler.java
================================================
package io.tyloo.dubbo.proxy.jdk;
import io.tyloo.api.Propagation;
import io.tyloo.api.Tyloo;
import io.tyloo.dubbo.context.DubboTransactionContextEditor;
import io.tyloo.interceptor.TylooCoordinatorAspect;
import io.tyloo.support.FactoryBuilder;
import io.tyloo.utils.ReflectionUtils;
import org.apache.dubbo.common.utils.StringUtils;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;
import org.aspectj.lang.ProceedingJoinPoint;
import java.lang.reflect.Method;
/*
*
* TCC ô
* ڵ Dubbo Service ʱʹ TylooCoordinatorAspect ش
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 10:55 2019/9/23
*
*/
public class TylooInvokerInvocationHandler extends InvokerInvocationHandler {
private Object target;
public TylooInvokerInvocationHandler(Invoker> handler) {
super(handler);
}
public TylooInvokerInvocationHandler(T target, Invoker invoker) {
super(invoker);
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Tyloo tyloo = method.getAnnotation(Tyloo.class);
if (tyloo != null) {
if (StringUtils.isEmpty(tyloo.confirmMethod())) {
ReflectionUtils.changeAnnotationValue(tyloo, "confirmMethod", method.getName());
ReflectionUtils.changeAnnotationValue(tyloo, "cancelMethod", method.getName());
ReflectionUtils.changeAnnotationValue(tyloo, "transactionContextEditor", DubboTransactionContextEditor.class);
ReflectionUtils.changeAnnotationValue(tyloo, "propagation", Propagation.SUPPORTS);
}
/**
* ɷ
* TylooCoordinatorAspect#interceptTransactionContextMethod Էش
* Ϊʲô TylooTransactionAspect 棿
* ΪΪ Propagation.SUPPORTSᷢ
*/
ProceedingJoinPoint pjp = new MethodProceedingJoinPoint(proxy, target, method, args);
return FactoryBuilder.factoryOf(TylooCoordinatorAspect.class).getInstance().interceptTransactionContextMethod(pjp);
} else {
return super.invoke(target, method, args);
}
}
}
================================================
FILE: tyloo-dubbo/src/main/java/io/tyloo/dubbo/proxy/jdk/TylooJdkProxyFactory.java
================================================
package io.tyloo.dubbo.proxy.jdk;
import org.apache.dubbo.rpc.Invoker;
import org.apache.dubbo.rpc.proxy.InvokerInvocationHandler;
import org.apache.dubbo.rpc.proxy.jdk.JdkProxyFactory;
import java.lang.reflect.Proxy;
/*
*
* TCC JDK
* JDK ̬
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 10:52 2019/9/29
*
*/
public class TylooJdkProxyFactory extends JdkProxyFactory {
/**
* - Ŀʱ `TccJavassistProxyFactory#getProxy(...)` Dubbo Service Proxy
* - һε `Proxy#newProxyInstance(...)` Dubbo Service Proxy
* - ڶε `Proxy#newProxyInstance(...)` Ե Dubbo Service Proxy Proxy
*
* @param invoker
* @param interfaces
* @param
* @return
*/
@Override
@SuppressWarnings("unchecked")
public T getProxy(Invoker invoker, Class>[] interfaces) {
T proxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
T tccProxy = (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new TylooInvokerInvocationHandler(proxy, invoker));
return tccProxy;
}
}
================================================
FILE: tyloo-dubbo/src/main/resources/META-INF/dubbo/org.apache.dubbo.rpc.ProxyFactory
================================================
tylooJavassist=TylooJavassistProxyFactory
tylooJdk=TylooJdkProxyFactory
================================================
FILE: tyloo-dubbo/src/main/resources/tyloo-dubbo.xml
================================================
================================================
FILE: tyloo-spring/pom.xml
================================================
tyloo
io.tyloo
1.1.0
4.0.0
tyloo-spring
io.tyloo
tyloo-core
io.tyloo
tyloo-api
org.springframework
spring-context
org.springframework
spring-jdbc
org.springframework
spring-aop
org.springframework
spring-context-support
org.quartz-scheduler
quartz
org.aspectj
aspectjweaver
log4j
log4j
org.projectlombok
lombok
1.18.12
================================================
FILE: tyloo-spring/src/main/dbscripts/db.sql
================================================
CREATE TABLE `TCC_TRANSACTION` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
ALTER TABLE `TCC_TRANSACTION` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;
================================================
FILE: tyloo-spring/src/main/java/io/tyloo/spring/ConfigurableCoordinatorAspect.java
================================================
package io.tyloo.spring;
import io.tyloo.interceptor.TylooCoordinatorAspect;
import io.tyloo.interceptor.TylooCoordinatorInterceptor;
import io.tyloo.support.TransactionConfigurator;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
/*
*
* õԴЭ
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 20:21 2019/10/4
*
*/
@Aspect
public class ConfigurableCoordinatorAspect extends TylooCoordinatorAspect implements Ordered {
private TransactionConfigurator transactionConfigurator;
/**
* ʼ
* ע TylooTransactionManager
*/
public void init() {
TylooCoordinatorInterceptor tylooCoordinatorInterceptor = new TylooCoordinatorInterceptor();
tylooCoordinatorInterceptor.setTransactionManager(transactionConfigurator.getTransactionManager());
this.setTylooCoordinatorInterceptor(tylooCoordinatorInterceptor);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE + 1;
}
public void setTransactionConfigurator(TransactionConfigurator transactionConfigurator) {
this.transactionConfigurator = transactionConfigurator;
}
}
================================================
FILE: tyloo-spring/src/main/java/io/tyloo/spring/ConfigurableTransactionAspect.java
================================================
package io.tyloo.spring;
import io.tyloo.TransactionManager;
import io.tyloo.interceptor.TylooTransactionAspect;
import io.tyloo.interceptor.TylooTransactionInterceptor;
import io.tyloo.support.TransactionConfigurator;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.core.Ordered;
/*
*
* õĿɲ
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 20:13 2019/10/7
*
*/
@Aspect
public class ConfigurableTransactionAspect extends TylooTransactionAspect implements Ordered {
private TransactionConfigurator transactionConfigurator;
/**
* ʼ
* ע DelayCancelExceptions setTylooTransactionManager
*/
public void init() {
TransactionManager transactionManager = transactionConfigurator.getTransactionManager();
TylooTransactionInterceptor tylooTransactionInterceptor = new TylooTransactionInterceptor();
tylooTransactionInterceptor.setTransactionManager(transactionManager);
tylooTransactionInterceptor.setDelayCancelExceptions(transactionConfigurator.getTylooRecoverConfiguration().getDelayCancelExceptions());
this.setTylooTransactionInterceptor(tylooTransactionInterceptor);
}
@Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
public void setTransactionConfigurator(TransactionConfigurator transactionConfigurator) {
this.transactionConfigurator = transactionConfigurator;
}
}
================================================
FILE: tyloo-spring/src/main/java/io/tyloo/spring/recover/DefaultTylooRecoverConfiguration.java
================================================
package io.tyloo.spring.recover;
import io.tyloo.OptimisticLockException;
import io.tyloo.recover.TylooRecoverConfiguration;
import lombok.*;
import java.net.SocketTimeoutException;
import java.util.HashSet;
import java.util.Set;
/*
*
* Ĭָ
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 20:14 2019/10/10
*
*/
@AllArgsConstructor
@Data
@Builder
public class DefaultTylooRecoverConfiguration implements TylooRecoverConfiguration {
public static final TylooRecoverConfiguration INSTANCE = new DefaultTylooRecoverConfiguration();
/**
* һೢԻָԶָҪ˹ԤĬ30Σ
*/
private int maxRetryCount = 30;
/**
* һ־һʱûи¾ͻᱻΪǷ쳣Ҫָ
* ָJobɨ賬ʱûиµ־Щлָʱ䵥λ룬Ĭ120
*/
private int recoverDuration = 120; //120 seconds
/**
* ָJobãĬ(ÿ)
* cron ʽ
* 0/30 * * * * ?ÿ 30 ִһΡ
*/
private String cronExpression = "0 */1 * * * ?";
private int asyncTerminateThreadCorePoolSize = 512;
private int asyncTerminateThreadMaxPoolSize = 1024;
private int asyncTerminateThreadWorkQueueSize = 512;
/**
* ӳȡ쳣
*/
private Set> delayCancelExceptions = new HashSet>();
public DefaultTylooRecoverConfiguration() {
/**
* SocketTimeoutException ָʱС Socket ʱʱ䣬ʱָԶ̲ȡع
* Զ̲´θʱΪֹʧܣ׳ OptimisticLockException TylooInterceptor ʱȡع
* ܻͶʱȡعͻͳһʱ
*
*/
delayCancelExceptions.add(OptimisticLockException.class);
/**
* try ΣزߵԶ̲( Զ̷ DubboHttp )Զ̲ try εķִʱϳ Socket ȴʱ SocketTimeoutException
* ִعԶ̲ try ķδִɣܵ cancel ķʵδִ( try ķδִɣݿ TCC δύ
* cancel ķȡʱδ·ʵδִУ try ķִύݿ TCC Ϊ )ݲһ¡
* ָʱȡعʱԶ̲ߵ try ķδǿܷݲһ¡
*
*/
delayCancelExceptions.add(SocketTimeoutException.class);
}
@Override
public void setDelayCancelExceptions(Set> delayCancelExceptions) {
this.delayCancelExceptions.addAll(delayCancelExceptions);
}
}
================================================
FILE: tyloo-spring/src/main/java/io/tyloo/spring/recover/RecoverScheduledJob.java
================================================
package io.tyloo.spring.recover;
import io.tyloo.SystemException;
import io.tyloo.recover.TylooTransactionRecovery;
import io.tyloo.support.TransactionConfigurator;
import org.quartz.Scheduler;
import org.springframework.scheduling.quartz.CronTriggerFactoryBean;
import org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean;
/*
*
* ָʱ
* Quartz ʵֵȣִָ
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 20:15 2019/10/29
*
*/
public class RecoverScheduledJob {
/**
* ָ
*/
private TylooTransactionRecovery tylooTransactionRecovery;
/**
* עTCC.
*/
private TransactionConfigurator transactionConfigurator;
/**
* ָ(עorg.springframework.scheduling.quartz.SchedulerFactoryBeanʵ)
*/
private Scheduler scheduler;
/**
* ʼSpringʱִ.
*/
public void init() {
try {
// MethodInvokingJobDetailFactoryBean ɾֻҪָijijڴʱָָ
MethodInvokingJobDetailFactoryBean jobDetail = new MethodInvokingJobDetailFactoryBean();
// ָӦĵöʵκνӿ
jobDetail.setTargetObject(tylooTransactionRecovery);
// ָtargetObjectijķ(˴TransactionRecoveryеstartRecover)
jobDetail.setTargetMethod("startRecover");
//
jobDetail.setName("transactionRecoveryJob");
// ǷִУĬDzִеģʱáconcurrentΪfalseܴܿ⣬ҼʽС,
// Ϊfalseʾһִٿµ
jobDetail.setConcurrent(false);
jobDetail.afterPropertiesSet();
// ࣬ñָĵָĴ
// ฺspringдһIDӦSchedulerFactoryBeanԵListбãܱ֤ijָ
CronTriggerFactoryBean cronTrigger = new CronTriggerFactoryBean();
// ô
cronTrigger.setBeanName("transactionRecoveryCronTrigger");
// ͨȡָʱ
cronTrigger.setCronExpression(transactionConfigurator.getTylooRecoverConfiguration().getCronExpression());
cronTrigger.setJobDetail(jobDetail.getObject());
cronTrigger.afterPropertiesSet();
// õ
scheduler.scheduleJob(jobDetail.getObject(), cronTrigger.getObject());
//
scheduler.start();
} catch (Exception e) {
throw new SystemException(e);
}
}
public void setTylooTransactionRecovery(TylooTransactionRecovery tylooTransactionRecovery) {
this.tylooTransactionRecovery = tylooTransactionRecovery;
}
public Scheduler getScheduler() {
return scheduler;
}
public void setScheduler(Scheduler scheduler) {
this.scheduler = scheduler;
}
public void setTransactionConfigurator(TransactionConfigurator transactionConfigurator) {
this.transactionConfigurator = transactionConfigurator;
}
}
================================================
FILE: tyloo-spring/src/main/java/io/tyloo/spring/repository/SpringJdbcTransactionRepository.java
================================================
package io.tyloo.spring.repository;
import io.tyloo.repository.JdbcTransactionRepository;
import org.springframework.jdbc.datasource.DataSourceUtils;
import java.sql.Connection;
/*
*
* SpringJdbc
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 20:18 2019/10/30
*
*/
public class SpringJdbcTransactionRepository extends JdbcTransactionRepository {
@Override
protected Connection getConnection() {
return DataSourceUtils.getConnection(this.getDataSource());
}
@Override
protected void releaseConnection(Connection con) {
DataSourceUtils.releaseConnection(con, this.getDataSource());
}
}
================================================
FILE: tyloo-spring/src/main/java/io/tyloo/spring/support/SpringBeanFactory.java
================================================
package io.tyloo.spring.support;
import io.tyloo.support.BeanFactory;
import io.tyloo.support.FactoryBuilder;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Map;
/*
*
* Spring Bean
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 20:12 2019/11/11
*
*/
public class SpringBeanFactory implements BeanFactory, ApplicationContextAware {
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
FactoryBuilder.registerBeanFactory(this);
}
@Override
public boolean isFactoryOf(Class clazz) {
Map map = this.applicationContext.getBeansOfType(clazz);
return map.size() > 0;
}
@Override
public T getBean(Class var1) {
return this.applicationContext.getBean(var1);
}
}
================================================
FILE: tyloo-spring/src/main/java/io/tyloo/spring/support/SpringPostProcessor.java
================================================
package io.tyloo.spring.support;
import io.tyloo.support.BeanFactory;
import io.tyloo.support.FactoryBuilder;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
/*
*
* Spring ô
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 20:13 2019/11/18
*
*/
public class SpringPostProcessor implements ApplicationListener {
/**
* Springʱ.
*/
@Override
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
ApplicationContext applicationContext = contextRefreshedEvent.getApplicationContext();
if (applicationContext.getParent() == null) {
FactoryBuilder.registerBeanFactory(applicationContext.getBean(BeanFactory.class));
}
}
}
================================================
FILE: tyloo-spring/src/main/java/io/tyloo/spring/support/SpringTransactionConfigurator.java
================================================
package io.tyloo.spring.support;
import io.tyloo.TransactionManager;
import io.tyloo.TransactionRepository;
import io.tyloo.recover.TylooRecoverConfiguration;
import io.tyloo.repository.CachableTransactionRepository;
import io.tyloo.spring.recover.DefaultTylooRecoverConfiguration;
import io.tyloo.support.TransactionConfigurator;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
/*
*
* Spring
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 20:20 2019/12/4
*
*/
public class SpringTransactionConfigurator implements TransactionConfigurator {
private static volatile ExecutorService executorService = null;
@Autowired
private TransactionRepository transactionRepository;
@Autowired(required = false)
private TylooRecoverConfiguration tylooRecoverConfiguration = DefaultTylooRecoverConfiguration.INSTANCE;
private TransactionManager transactionManager;
public void init() {
transactionManager = new TransactionManager();
transactionManager.setTransactionRepository(transactionRepository);
if (executorService == null) {
Executors.defaultThreadFactory();
synchronized (SpringTransactionConfigurator.class) {
if (executorService == null) {
executorService = new ThreadPoolExecutor(
tylooRecoverConfiguration.getAsyncTerminateThreadCorePoolSize(),
tylooRecoverConfiguration.getAsyncTerminateThreadMaxPoolSize(),
5L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(tylooRecoverConfiguration.getAsyncTerminateThreadWorkQueueSize()),
new ThreadFactory() {
final AtomicInteger poolNumber = new AtomicInteger(1);
final ThreadGroup group;
final AtomicInteger threadNumber = new AtomicInteger(1);
final String namePrefix;
{
SecurityManager securityManager = System.getSecurityManager();
this.group = securityManager != null ? securityManager.getThreadGroup() : Thread.currentThread().getThreadGroup();
this.namePrefix = "tcc-async-terminate-pool-" + poolNumber.getAndIncrement() + "-thread-";
}
@Override
public Thread newThread(Runnable runnable) {
Thread thread = new Thread(this.group, runnable, this.namePrefix + this.threadNumber.getAndIncrement(), 0L);
if (thread.isDaemon()) {
thread.setDaemon(false);
}
if (thread.getPriority() != 5) {
thread.setPriority(5);
}
return thread;
}
},
new ThreadPoolExecutor.CallerRunsPolicy());
}
}
}
transactionManager.setExecutorService(executorService);
if (transactionRepository instanceof CachableTransactionRepository) {
((CachableTransactionRepository) transactionRepository).setExpireDuration(tylooRecoverConfiguration.getRecoverDuration());
}
}
@Override
public TransactionManager getTransactionManager() {
return transactionManager;
}
@Override
public TransactionRepository getTransactionRepository() {
return transactionRepository;
}
@Override
public TylooRecoverConfiguration getTylooRecoverConfiguration() {
return tylooRecoverConfiguration;
}
}
================================================
FILE: tyloo-spring/src/main/resources/tyloo.xml
================================================
================================================
FILE: tyloo-tutorial-sample/pom.xml
================================================
tyloo
io.tyloo
1.1.0
4.0.0
tyloo-tutorial-sample
pom
tyloo-dubbo-sample
tyloo-http-sample
tyloo-server-sample
tyloo-sample-domain
================================================
FILE: tyloo-tutorial-sample/src/tylooSampledb/create_db_cap.sql
================================================
CREATE DATABASE `TCC_CAP` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC_CAP;
CREATE TABLE `CAP_CAPITAL_ACCOUNT` (
`CAPITAL_ACCOUNT_ID` int(11) NOT NULL AUTO_INCREMENT,
`BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,
`USER_ID` int(11) DEFAULT NULL,
PRIMARY KEY (`CAPITAL_ACCOUNT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `CAP_TRADE_ORDER` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`SELF_USER_ID` bigint(11) DEFAULT NULL,
`OPPOSITE_USER_ID` bigint(11) DEFAULT NULL,
`MERCHANT_ORDER_NO` varchar(45) NOT NULL,
`AMOUNT` decimal(10,0) DEFAULT NULL,
`STATUS` varchar(45) DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `UX_MERCHANT_ORDER_NO` (`MERCHANT_ORDER_NO`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (1,10000,1000);
INSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (2,10000,2000);
================================================
FILE: tyloo-tutorial-sample/src/tylooSampledb/create_db_ord.sql
================================================
CREATE DATABASE `TCC_ORD` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC_ORD;
CREATE TABLE `ORD_ORDER` (
`ORDER_ID` int(11) NOT NULL AUTO_INCREMENT,
`PAYER_USER_ID` int(11) DEFAULT NULL,
`PAYEE_USER_ID` int(11) DEFAULT NULL,
`RED_PACKET_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,
`CAPITAL_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,
`STATUS` varchar(45) DEFAULT NULL,
`MERCHANT_ORDER_NO` varchar(45) NOT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`ORDER_ID`),
UNIQUE KEY `MERCHANT_ORDER_NO_UNIQUE` (`MERCHANT_ORDER_NO`)
) ENGINE=InnoDB AUTO_INCREMENT=1188 DEFAULT CHARSET=utf8;
CREATE TABLE `ORD_ORDER_LINE` (
`ORDER_LINE_ID` int(11) NOT NULL AUTO_INCREMENT,
`PRODUCT_ID` int(11) DEFAULT NULL,
`QUANTITY` decimal(10,0) DEFAULT NULL,
`UNIT_PRICE` decimal(10,0) DEFAULT NULL,
PRIMARY KEY (`ORDER_LINE_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `ORD_SHOP` (
`SHOP_ID` int(11) NOT NULL,
`OWNER_USER_ID` int(11) DEFAULT NULL,
PRIMARY KEY (`SHOP_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `ORD_PRODUCT`(
`PRODUCT_ID` int(11) NOT NULL,
`SHOP_ID` int(11) NOT NULL,
`PRODUCT_NAME` VARCHAR(64) DEFAULT NULL ,
`PRICE` DECIMAL(10,0) DEFAULT NULL,
PRIMARY KEY (`PRODUCT_ID`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `ORD_SHOP` (`SHOP_ID`,`OWNER_USER_ID`) VALUES (1,1000);
INSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (1,1,'IPhone6S',5288);
INSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (2,1,'MAC Pro',10288);
INSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (3,1,'IWatch',2288);
================================================
FILE: tyloo-tutorial-sample/src/tylooSampledb/create_db_red.sql
================================================
CREATE DATABASE `TCC_RED` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC_RED;
CREATE TABLE `RED_RED_PACKET_ACCOUNT` (
`RED_PACKET_ACCOUNT_ID` int(11) NOT NULL,
`BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,
`USER_ID` int(11) DEFAULT NULL,
PRIMARY KEY (`RED_PACKET_ACCOUNT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `RED_TRADE_ORDER` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`SELF_USER_ID` bigint(11) DEFAULT NULL,
`OPPOSITE_USER_ID` bigint(11) DEFAULT NULL,
`MERCHANT_ORDER_NO` varchar(45) NOT NULL,
`AMOUNT` decimal(10,0) DEFAULT NULL,
`STATUS` varchar(45) DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`ID`),
UNIQUE KEY `MERCHANT_ORDER_NO_UNIQUE` (`MERCHANT_ORDER_NO`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (1,950,1000);
INSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (2,500,2000);
================================================
FILE: tyloo-tutorial-sample/src/tylooSampledb/create_db_tcc.sql
================================================
CREATE DATABASE `TCC` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC;
CREATE TABLE `TCC_TRANSACTION_CAP` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `TCC_TRANSACTION_CAP` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;
CREATE TABLE `TCC_TRANSACTION_ORD` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `TCC_TRANSACTION_ORD` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;
CREATE TABLE `TCC_TRANSACTION_RED` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `TCC_TRANSACTION_RED` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;
CREATE TABLE `TCC_TRANSACTION_UT` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
ALTER TABLE `TCC_TRANSACTION_UT` ADD `IS_DELETE` tinyint(1) DEFAULT 0 NOT NULL;
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/pom.xml
================================================
tyloo-tutorial-sample
io.tyloo
1.1.0
4.0.0
tyloo-dubbo-sample
pom
tyloo-dubbo-capital
tyloo-dubbo-capital-api
tyloo-dubbo-order
tyloo-dubbo-redpacket
tyloo-dubbo-redpacket-api
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/src/main/tylooSampledb/create_db_cap.sql
================================================
CREATE DATABASE `tcc_cap` /*!40100 DEFAULT CHARACTER SET utf8 */;
use tcc_cap;
CREATE TABLE `CAP_CAPITAL_ACCOUNT` (
`CAPITAL_ACCOUNT_ID` int(11) NOT NULL AUTO_INCREMENT,
`BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,
`USER_ID` int(11) DEFAULT NULL,
PRIMARY KEY (`CAPITAL_ACCOUNT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
CREATE TABLE `CAP_TRADE_ORDER` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`SELF_USER_ID` bigint(11) DEFAULT NULL,
`OPPOSITE_USER_ID` bigint(11) DEFAULT NULL,
`MERCHANT_ORDER_NO` varchar(45) DEFAULT NULL,
`AMOUNT` decimal(10,0) DEFAULT NULL,
`STATUS` varchar(45) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (1,10000,1000);
INSERT INTO `CAP_CAPITAL_ACCOUNT`(CAPITAL_ACCOUNT_ID, BALANCE_AMOUNT, USER_ID) VALUE (2,10000,2000);
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/src/main/tylooSampledb/create_db_ord.sql
================================================
CREATE DATABASE `TCC_ORD` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC_ORD;
CREATE TABLE `ORD_ORDER` (
`ORDER_ID` int(11) NOT NULL AUTO_INCREMENT,
`PAYER_USER_ID` int(11) DEFAULT NULL,
`PAYEE_USER_ID` int(11) DEFAULT NULL,
`RED_PACKET_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,
`CAPITAL_PAY_AMOUNT` decimal(10,0) DEFAULT NULL,
`STATUS` varchar(45) DEFAULT NULL,
`MERCHANT_ORDER_NO` varchar(45) DEFAULT NULL,
PRIMARY KEY (`ORDER_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `ORD_ORDER_LINE` (
`ORDER_LINE_ID` int(11) NOT NULL AUTO_INCREMENT,
`PRODUCT_ID` int(11) DEFAULT NULL,
`QUANTITY` decimal(10,0) DEFAULT NULL,
`UNIT_PRICE` decimal(10,0) DEFAULT NULL,
PRIMARY KEY (`ORDER_LINE_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `ORD_SHOP` (
`SHOP_ID` int(11) NOT NULL,
`OWNER_USER_ID` int(11) DEFAULT NULL,
PRIMARY KEY (`SHOP_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `ORD_PRODUCT`(
`PRODUCT_ID` int(11) NOT NULL,
`SHOP_ID` int(11) NOT NULL,
`PRODUCT_NAME` VARCHAR(64) DEFAULT NULL ,
`PRICE` DECIMAL(10,0) DEFAULT NULL,
PRIMARY KEY (`PRODUCT_ID`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `ORD_SHOP` (`SHOP_ID`,`OWNER_USER_ID`) VALUES (1,1000);
INSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (1,1,'IPhone6S',5288);
INSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (2,1,'MAC Pro',10288);
INSERT INTO `ORD_PRODUCT` (`PRODUCT_ID`,`SHOP_ID`,`PRODUCT_NAME`,`PRICE`) VALUES (3,1,'IWatch',2288);
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/src/main/tylooSampledb/create_db_red.sql
================================================
CREATE DATABASE `tcc_red` /*!40100 DEFAULT CHARACTER SET utf8 */;
use tcc_red;
CREATE TABLE `RED_RED_PACKET_ACCOUNT` (
`RED_PACKET_ACCOUNT_ID` int(11) NOT NULL,
`BALANCE_AMOUNT` decimal(10,0) DEFAULT NULL,
`USER_ID` int(11) DEFAULT NULL,
PRIMARY KEY (`RED_PACKET_ACCOUNT_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
CREATE TABLE `RED_TRADE_ORDER` (
`ID` int(11) NOT NULL AUTO_INCREMENT,
`SELF_USER_ID` BIGINT(11) DEFAULT NULL,
`OPPOSITE_USER_ID` BIGINT(11) DEFAULT NULL,
`MERCHANT_ORDER_NO` varchar(45) DEFAULT NULL,
`AMOUNT` decimal(10,0) DEFAULT NULL,
`STATUS` varchar(45) DEFAULT NULL,
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
INSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (1,950,1000);
INSERT INTO `RED_RED_PACKET_ACCOUNT` (`RED_PACKET_ACCOUNT_ID`,`BALANCE_AMOUNT`,`USER_ID`) VALUES (2,500,2000);
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/src/main/tylooSampledb/create_db_tcc.sql
================================================
CREATE DATABASE `TCC` /*!40100 DEFAULT CHARACTER SET utf8 */;
use TCC;
CREATE TABLE `TCC_TRANSACTION_CAP` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `TCC_TRANSACTION_ORD` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `TCC_TRANSACTION_RED` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
CREATE TABLE `TCC_TRANSACTION_UT` (
`TRANSACTION_ID` int(11) NOT NULL AUTO_INCREMENT,
`DOMAIN` varchar(100) DEFAULT NULL,
`GLOBAL_TX_ID` varbinary(32) NOT NULL,
`BRANCH_QUALIFIER` varbinary(32) NOT NULL,
`CONTENT` varbinary(8000) DEFAULT NULL,
`STATUS` int(11) DEFAULT NULL,
`TRANSACTION_TYPE` int(11) DEFAULT NULL,
`RETRIED_COUNT` int(11) DEFAULT NULL,
`CREATE_TIME` datetime DEFAULT NULL,
`LAST_UPDATE_TIME` datetime DEFAULT NULL,
`VERSION` int(11) DEFAULT NULL,
PRIMARY KEY (`TRANSACTION_ID`),
UNIQUE KEY `UX_TX_BQ` (`GLOBAL_TX_ID`,`BRANCH_QUALIFIER`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/pom.xml
================================================
tyloo-dubbo-sample
io.tyloo
1.1.0
4.0.0
tyloo-dubbo-capital
war
io.tyloo
tyloo-dubbo-capital-api
${project.version}
io.tyloo
tyloo-sample-captial
${project.version}
io.tyloo
tyloo-dubbo
${project.version}
io.tyloo
tyloo-spring
${project.version}
org.springframework
spring-web
org.springframework
spring-webmvc
org.springframework
spring-context
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/java/io/tyloo/sample/dubbo/capital/service/CapitalAccountServiceImpl.java
================================================
package io.tyloo.sample.dubbo.capital.service;
import io.tyloo.sample.capital.domain.repository.CapitalAccountRepository;
import io.tyloo.sample.dubbo.capital.api.CapitalAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Service("capitalAccountService")
public class CapitalAccountServiceImpl implements CapitalAccountService {
@Autowired
CapitalAccountRepository capitalAccountRepository;
@Override
public BigDecimal getCapitalAccountByUserId(long userId) {
return capitalAccountRepository.findByUserId(userId).getBalanceAmount();
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/java/io/tyloo/sample/dubbo/capital/service/CapitalTradeOrderServiceImpl.java
================================================
package io.tyloo.sample.dubbo.capital.service;
import io.tyloo.sample.capital.domain.entity.CapitalAccount;
import io.tyloo.sample.capital.domain.entity.TradeOrder;
import io.tyloo.sample.capital.domain.repository.CapitalAccountRepository;
import io.tyloo.sample.capital.domain.repository.TradeOrderRepository;
import io.tyloo.sample.dubbo.capital.api.CapitalTradeOrderService;
import io.tyloo.sample.dubbo.capital.api.dto.CapitalTradeOrderDto;
import org.apache.commons.lang3.time.DateFormatUtils;
import io.tyloo.api.Tyloo;
import io.tyloo.dubbo.context.DubboTransactionContextEditor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Calendar;
@Service("capitalTradeOrderService")
public class CapitalTradeOrderServiceImpl implements CapitalTradeOrderService {
@Autowired
CapitalAccountRepository capitalAccountRepository;
@Autowired
TradeOrderRepository tradeOrderRepository;
@Override
@Tyloo(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = DubboTransactionContextEditor.class)
@Transactional
public String record(CapitalTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("capital try record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if trade order has been recorded, if yes, return success directly.
if (foundTradeOrder == null) {
TradeOrder tradeOrder = new TradeOrder(
tradeOrderDto.getSelfUserId(),
tradeOrderDto.getOppositeUserId(),
tradeOrderDto.getMerchantOrderNo(),
tradeOrderDto.getAmount()
);
try {
tradeOrderRepository.insert(tradeOrder);
CapitalAccount transferFromAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
transferFromAccount.transferFrom(tradeOrderDto.getAmount());
capitalAccountRepository.save(transferFromAccount);
} catch (DataIntegrityViolationException e) {
//this exception may happen when insert trade order concurrently, if happened, ignore this insert operation.
}
}
return "success";
}
@Transactional
public void confirmRecord(CapitalTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000L);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("capital confirm record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.
if (tradeOrder != null && tradeOrder.getStatus().equals("DRAFT")) {
tradeOrder.confirm();
tradeOrderRepository.update(tradeOrder);
CapitalAccount transferToAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());
transferToAccount.transferTo(tradeOrderDto.getAmount());
capitalAccountRepository.save(transferToAccount);
}
}
@Transactional
public void cancelRecord(CapitalTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("capital cancel record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.
if (null != tradeOrder && "DRAFT".equals(tradeOrder.getStatus())) {
tradeOrder.cancel();
tradeOrderRepository.update(tradeOrder);
CapitalAccount capitalAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
capitalAccount.cancelTransfer(tradeOrderDto.getAmount());
capitalAccountRepository.save(capitalAccount);
}
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/config/spring/local/appcontext-service-provider.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/config/spring/local/appcontext-service-tcc.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/config/spring/local/appcontext-web-servlet.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/log/log4j.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/sample-dubbo-capital.properties
================================================
dubbo.port=2881
zookeeper.address=127.0.0.1:2181
zookeeper.session.timeout=8000
zookeeper.connect.timeout=2000
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/resources/tccjdbc.properties
================================================
jdbc.driverClassName=com.mysql.jdbc.Driver
tcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=30000
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/webapp/WEB-INF/web.xml
================================================
Sample Dubbo Capital
contextConfigLocation
classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml,classpath:tyloo-dubbo.xml
log4jConfigLocation
classpath:log/log4j.xml
log4jRefreshInterval
60000
org.springframework.web.util.Log4jConfigListener
org.springframework.web.context.ContextLoaderListener
characterEncodingFilter
Character Encoding Filter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
characterEncodingFilter
/*
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:config/spring/local/appcontext-web-servlet.xml
1
springmvc
/
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital/src/main/webapp/index.jsp
================================================
hello tcc transacton dubbo sample capital
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital-api/pom.xml
================================================
tyloo-dubbo-sample
io.tyloo
1.1.0
4.0.0
tyloo-dubbo-capital-api
io.tyloo
tyloo-api
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital-api/src/main/java/io/tyloo/sample/dubbo/capital/api/CapitalAccountService.java
================================================
package io.tyloo.sample.dubbo.capital.api;
import java.math.BigDecimal;
public interface CapitalAccountService {
BigDecimal getCapitalAccountByUserId(long userId);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital-api/src/main/java/io/tyloo/sample/dubbo/capital/api/CapitalTradeOrderService.java
================================================
package io.tyloo.sample.dubbo.capital.api;
import io.tyloo.sample.dubbo.capital.api.dto.CapitalTradeOrderDto;
import io.tyloo.api.Tyloo;
public interface CapitalTradeOrderService {
@Tyloo
public String record(CapitalTradeOrderDto tradeOrderDto);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-capital-api/src/main/java/io/tyloo/sample/dubbo/capital/api/dto/CapitalTradeOrderDto.java
================================================
package io.tyloo.sample.dubbo.capital.api.dto;
import java.io.Serializable;
import java.math.BigDecimal;
public class CapitalTradeOrderDto implements Serializable {
private static final long serialVersionUID = 6627401903410124642L;
private long selfUserId;
private long oppositeUserId;
private String orderTitle;
private String merchantOrderNo;
private BigDecimal amount;
public long getSelfUserId() {
return selfUserId;
}
public void setSelfUserId(long selfUserId) {
this.selfUserId = selfUserId;
}
public long getOppositeUserId() {
return oppositeUserId;
}
public void setOppositeUserId(long oppositeUserId) {
this.oppositeUserId = oppositeUserId;
}
public String getOrderTitle() {
return orderTitle;
}
public void setOrderTitle(String orderTitle) {
this.orderTitle = orderTitle;
}
public String getMerchantOrderNo() {
return merchantOrderNo;
}
public void setMerchantOrderNo(String merchantOrderNo) {
this.merchantOrderNo = merchantOrderNo;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/pom.xml
================================================
tyloo-dubbo-sample
io.tyloo
1.1.0
4.0.0
tyloo-dubbo-order
war
log4j
log4j
io.tyloo
tyloo-dubbo
${project.version}
io.tyloo
tyloo-dubbo-capital-api
${project.version}
io.tyloo
tyloo-dubbo-redpacket-api
${project.version}
io.tyloo
tyloo-sample-order
${project.version}
io.tyloo
tyloo-spring
${project.version}
com.mchange
c3p0
mysql
mysql-connector-java
org.mybatis
mybatis-spring
org.mybatis
mybatis
org.aspectj
aspectjrt
org.aspectj
aspectjweaver
org.springframework
spring-web
org.springframework
spring-webmvc
org.springframework
spring-context
org.springframework
spring-beans
org.springframework
spring-jdbc
org.apache.commons
commons-lang3
org.freemarker
freemarker
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/service/AccountServiceImpl.java
================================================
package io.tyloo.sample.dubbo.order.service;
import io.tyloo.sample.dubbo.capital.api.CapitalAccountService;
import io.tyloo.sample.dubbo.redpacket.api.RedPacketAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Service("accountService")
public class AccountServiceImpl {
@Autowired
RedPacketAccountService redPacketAccountService;
@Autowired
CapitalAccountService capitalAccountService;
public BigDecimal getRedPacketAccountByUserId(long userId){
return redPacketAccountService.getRedPacketAccountByUserId(userId);
}
public BigDecimal getCapitalAccountByUserId(long userId){
return capitalAccountService.getCapitalAccountByUserId(userId);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/service/PaymentServiceImpl.java
================================================
package io.tyloo.sample.dubbo.order.service;
import io.tyloo.sample.dubbo.capital.api.CapitalTradeOrderService;
import io.tyloo.sample.dubbo.capital.api.dto.CapitalTradeOrderDto;
import io.tyloo.sample.dubbo.redpacket.api.RedPacketTradeOrderService;
import io.tyloo.sample.dubbo.redpacket.api.dto.RedPacketTradeOrderDto;
import io.tyloo.sample.order.domain.entity.Order;
import io.tyloo.sample.order.domain.repository.OrderRepository;
import org.apache.commons.lang3.time.DateFormatUtils;
import io.tyloo.api.Tyloo;
import io.tyloo.api.UniqueIdentity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.net.SocketTimeoutException;
import java.util.Calendar;
@Service
public class PaymentServiceImpl {
@Autowired
CapitalTradeOrderService capitalTradeOrderService;
@Autowired
RedPacketTradeOrderService redPacketTradeOrderService;
@Autowired
OrderRepository orderRepository;
@Tyloo(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment", asyncConfirm = false, delayCancelExceptions = {SocketTimeoutException.class, org.apache.dubbo.remoting.TimeoutException.class})
public void makePayment(@UniqueIdentity String orderNo, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
System.out.println("order try make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order order = orderRepository.findByMerchantOrderNo(orderNo);
//check if the order status is DRAFT, if no, means that another call makePayment for the same order happened, ignore this call makePayment.
if (order.getStatus().equals("DRAFT")) {
order.pay(redPacketPayAmount, capitalPayAmount);
try {
orderRepository.updateOrder(order);
} catch (OptimisticLockingFailureException e) {
//ignore the concurrently update order exception, ensure idempotency.
}
}
String result = capitalTradeOrderService.record(buildCapitalTradeOrderDto(order));
String result2 = redPacketTradeOrderService.record(buildRedPacketTradeOrderDto(order));
}
public void confirmMakePayment(String orderNo, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("order confirm make payment called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order foundOrder = orderRepository.findByMerchantOrderNo(orderNo);
//check if the trade order status is PAYING, if no, means another call confirmMakePayment happened, return directly, ensure idempotency.
if (foundOrder != null && foundOrder.getStatus().equals("PAYING")) {
foundOrder.confirm();
orderRepository.updateOrder(foundOrder);
}
}
public void cancelMakePayment(String orderNo, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("order cancel make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order foundOrder = orderRepository.findByMerchantOrderNo(orderNo);
//check if the trade order status is PAYING, if no, means another call cancelMakePayment happened, return directly, ensure idempotency.
if (foundOrder != null && foundOrder.getStatus().equals("PAYING")) {
foundOrder.cancelPayment();
orderRepository.updateOrder(foundOrder);
}
}
private CapitalTradeOrderDto buildCapitalTradeOrderDto(Order order) {
CapitalTradeOrderDto tradeOrderDto = new CapitalTradeOrderDto();
tradeOrderDto.setAmount(order.getCapitalPayAmount());
tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());
tradeOrderDto.setSelfUserId(order.getPayerUserId());
tradeOrderDto.setOppositeUserId(order.getPayeeUserId());
tradeOrderDto.setOrderTitle(String.format("order no:%s", order.getMerchantOrderNo()));
return tradeOrderDto;
}
private RedPacketTradeOrderDto buildRedPacketTradeOrderDto(Order order) {
RedPacketTradeOrderDto tradeOrderDto = new RedPacketTradeOrderDto();
tradeOrderDto.setAmount(order.getRedPacketPayAmount());
tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());
tradeOrderDto.setSelfUserId(order.getPayerUserId());
tradeOrderDto.setOppositeUserId(order.getPayeeUserId());
tradeOrderDto.setOrderTitle(String.format("order no:%s", order.getMerchantOrderNo()));
return tradeOrderDto;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/service/PlaceOrderServiceImpl.java
================================================
package io.tyloo.sample.dubbo.order.service;
import io.tyloo.sample.order.domain.entity.Order;
import io.tyloo.sample.order.domain.entity.Shop;
import io.tyloo.sample.order.domain.repository.ShopRepository;
import io.tyloo.sample.order.domain.service.OrderServiceImpl;
import org.apache.commons.lang3.tuple.Pair;
import io.tyloo.CancellingException;
import io.tyloo.ConfirmingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
@Service
public class PlaceOrderServiceImpl {
@Autowired
ShopRepository shopRepository;
@Autowired
OrderServiceImpl orderService;
@Autowired
PaymentServiceImpl paymentService;
public String placeOrder(long payerUserId, long shopId, List> productQuantities, final BigDecimal redPacketPayAmount) {
Shop shop = shopRepository.findById(shopId);
final Order order = orderService.createOrder(payerUserId, shop.getOwnerUserId(), productQuantities);
Boolean result = false;
try {
// ExecutorService executorService = Executors.newFixedThreadPool(2);
// Future future1 = executorService.submit(new Runnable() {
// @Override
// public void run() {
paymentService.makePayment(order.getMerchantOrderNo(), redPacketPayAmount, order.getTotalAmount().subtract(redPacketPayAmount));
// }
// });
// Future future2 = executorService.submit(new Runnable() {
// @Override
// public void run() {
// paymentService.makePayment(order.getMerchantOrderNo(), order, redPacketPayAmount, order.getTotalAmount().subtract(redPacketPayAmount));
// }
// });
//
// future1.get();
// future2.get();
} catch (ConfirmingException confirmingException) {
//exception throws with the tcc transaction status is CONFIRMING,
//when tcc transaction is confirming status,
// the tcc transaction recovery will try to confirm the whole transaction to ensure eventually consistent.
result = true;
} catch (CancellingException cancellingException) {
//exception throws with the tcc transaction status is CANCELLING,
//when tcc transaction is under CANCELLING status,
// the tcc transaction recovery will try to cancel the whole transaction to ensure eventually consistent.
} catch (Throwable e) {
//other exceptions throws at TRYING stage.
//you can retry or cancel the operation.
e.printStackTrace();
}
return order.getMerchantOrderNo();
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/web/controller/OrderController.java
================================================
package io.tyloo.sample.dubbo.order.web.controller;
import io.tyloo.sample.dubbo.order.service.AccountServiceImpl;
import io.tyloo.sample.dubbo.order.service.PlaceOrderServiceImpl;
import io.tyloo.sample.dubbo.order.web.controller.vo.PlaceOrderRequest;
import io.tyloo.sample.order.domain.entity.Order;
import io.tyloo.sample.order.domain.entity.Product;
import io.tyloo.sample.order.domain.repository.ProductRepository;
import io.tyloo.sample.order.domain.service.OrderServiceImpl;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import java.math.BigDecimal;
import java.security.InvalidParameterException;
import java.util.List;
@Controller
@RequestMapping("")
public class OrderController {
@Autowired
PlaceOrderServiceImpl placeOrderService;
@Autowired
ProductRepository productRepository;
@Autowired
AccountServiceImpl accountService;
@Autowired
OrderServiceImpl orderService;
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index() {
ModelAndView mv = new ModelAndView("/index");
return mv;
}
@RequestMapping(value = "/user/{userId}/shop/{shopId}", method = RequestMethod.GET)
public ModelAndView getProductsInShop(@PathVariable long userId,
@PathVariable long shopId) {
List products = productRepository.findByShopId(shopId);
ModelAndView mv = new ModelAndView("/shop");
mv.addObject("products", products);
mv.addObject("userId", userId);
mv.addObject("shopId", shopId);
return mv;
}
@RequestMapping(value = "/user/{userId}/shop/{shopId}/product/{productId}/confirm", method = RequestMethod.GET)
public ModelAndView productDetail(@PathVariable long userId,
@PathVariable long shopId,
@PathVariable long productId) {
ModelAndView mv = new ModelAndView("product_detail");
mv.addObject("capitalAmount", accountService.getCapitalAccountByUserId(userId));
mv.addObject("redPacketAmount", accountService.getRedPacketAccountByUserId(userId));
mv.addObject("product", productRepository.findById(productId));
mv.addObject("userId", userId);
mv.addObject("shopId", shopId);
return mv;
}
@RequestMapping(value = "/placeorder", method = RequestMethod.POST)
public RedirectView placeOrder(@RequestParam String redPacketPayAmount,
@RequestParam long shopId,
@RequestParam long payerUserId,
@RequestParam long productId) {
PlaceOrderRequest request = buildRequest(redPacketPayAmount, shopId, payerUserId, productId);
String merchantOrderNo = placeOrderService.placeOrder(request.getPayerUserId(), request.getShopId(),
request.getProductQuantities(), request.getRedPacketPayAmount());
return new RedirectView("/payresult/" + merchantOrderNo);
}
@RequestMapping(value = "/payresult/{merchantOrderNo}", method = RequestMethod.GET)
public ModelAndView getPayResult(@PathVariable String merchantOrderNo) {
ModelAndView mv = new ModelAndView("pay_success");
String payResultTip = null;
Order foundOrder = orderService.findOrderByMerchantOrderNo(merchantOrderNo);
payResultTip = foundOrder.getStatus();
mv.addObject("payResult", payResultTip);
mv.addObject("capitalAmount", accountService.getCapitalAccountByUserId(foundOrder.getPayerUserId()));
mv.addObject("redPacketAmount", accountService.getRedPacketAccountByUserId(foundOrder.getPayerUserId()));
return mv;
}
private PlaceOrderRequest buildRequest(String redPacketPayAmount, long shopId, long payerUserId, long productId) {
BigDecimal redPacketPayAmountInBigDecimal = new BigDecimal(redPacketPayAmount);
if (redPacketPayAmountInBigDecimal.compareTo(BigDecimal.ZERO) < 0) {
throw new InvalidParameterException("invalid red packet amount :" + redPacketPayAmount);
}
PlaceOrderRequest request = new PlaceOrderRequest();
request.setPayerUserId(payerUserId);
request.setShopId(shopId);
request.setRedPacketPayAmount(new BigDecimal(redPacketPayAmount));
request.getProductQuantities().add(new ImmutablePair(productId, 1));
return request;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/java/io/tyloo/sample/dubbo/order/web/controller/vo/PlaceOrderRequest.java
================================================
package io.tyloo.sample.dubbo.order.web.controller.vo;
import org.apache.commons.lang3.tuple.Pair;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class PlaceOrderRequest {
private long payerUserId;
private long shopId;
private BigDecimal redPacketPayAmount;
private List> productQuantities = new ArrayList>();
public long getPayerUserId() {
return payerUserId;
}
public void setPayerUserId(long payerUserId) {
this.payerUserId = payerUserId;
}
public long getShopId() {
return shopId;
}
public void setShopId(long shopId) {
this.shopId = shopId;
}
public BigDecimal getRedPacketPayAmount() {
return redPacketPayAmount;
}
public void setRedPacketPayAmount(BigDecimal redPacketPayAmount) {
this.redPacketPayAmount = redPacketPayAmount;
}
public List> getProductQuantities() {
return productQuantities;
}
public void setProductQuantities(List> productQuantities) {
this.productQuantities = productQuantities;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/config/spring/local/appcontext-service-dubbo.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/config/spring/local/appcontext-service-tcc.xml
================================================
org.apache.dubbo.remoting.TimeoutException
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/config/spring/local/appcontext-web-servlet.xml
================================================
json=application/json
html=text/html
org.springframework.web.servlet.view.freemarker.FreeMarkerView
/WEB-INF/ftl/
5
UTF-8
UTF-8
yyyy-MM-dd HH:mm:ss
HH:mm:ss
0.####
true,false
true
auto_detect
UTF-8
true
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/log/log4j.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/sample-dubbo-order.properties
================================================
zookeeper.address=127.0.0.1:2181
zookeeper.session.timeout=8000
zookeeper.connect.timeout=2000
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/resources/tccjdbc.properties
================================================
jdbc.driverClassName=com.mysql.jdbc.Driver
tcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=2000
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/ftl/index.ftl
================================================
[#ftl ]
tcc transacton dubbo sample order
sample说明:
打开下面商品列表链接,选择一个商品购买,输入红包支付金额,进行支付,系统将使用红包+资金账户转账支付。
支付成功后,各个project会打印如下日志:
sample-dubbo-order:
order try make payment called
order confirm make payment called
sample-dubbo-capital:
capital try record called
capital confirm record called
sample-dubbo-redpacket:
red packet try record called
red packet confirm record called
商品列表链接
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/ftl/pay_success.ftl
================================================
[#ftl ]
支付结果
支付状态:${payResult}
剩余可用账户余额: ${capitalAmount?string("0.00")}元
剩余可用红包余额: ${redPacketAmount?string("0.00")}元
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/ftl/product_detail.ftl
================================================
[#ftl ]
订单详情
名称: ${product.productName}
价格: ${product.price?string("0.00")}元
可用账户余额: ${capitalAmount?string("0.00")}元
可用红包余额: ${redPacketAmount?string("0.00")}元
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/ftl/shop.ftl
================================================
[#ftl ]
商品列表
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-order/src/main/webapp/WEB-INF/web.xml
================================================
Sample Dubbo Order
contextConfigLocation
classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml,classpath:tyloo-dubbo.xml
log4jConfigLocation
classpath:log/log4j.xml
log4jRefreshInterval
60000
org.springframework.web.util.Log4jConfigListener
org.springframework.web.context.ContextLoaderListener
characterEncodingFilter
Character Encoding Filter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
characterEncodingFilter
/*
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:config/spring/local/appcontext-web-servlet.xml
1
springmvc
/*
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/pom.xml
================================================
tyloo-dubbo-sample
io.tyloo
1.1.0
4.0.0
tyloo-dubbo-redpacket
war
io.tyloo
tyloo-dubbo-redpacket-api
${project.version}
io.tyloo
tyloo-dubbo
${project.version}
io.tyloo
tyloo-sample-redpacket
${project.version}
io.tyloo
tyloo-spring
${project.version}
org.springframework
spring-context
log4j
log4j
com.mchange
c3p0
mysql
mysql-connector-java
org.mybatis
mybatis-spring
org.mybatis
mybatis
org.aspectj
aspectjrt
org.aspectj
aspectjweaver
org.springframework
spring-web
org.springframework
spring-webmvc
org.springframework
spring-context
org.springframework
spring-beans
org.springframework
spring-jdbc
org.apache.commons
commons-lang3
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/java/io/tyloo/sample/dubbo/redpacket/service/RedPacketAccountServiceImpl.java
================================================
package io.tyloo.sample.dubbo.redpacket.service;
import io.tyloo.sample.dubbo.redpacket.api.RedPacketAccountService;
import io.tyloo.sample.redpacket.domain.repository.RedPacketAccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Service("redPacketAccountService")
public class RedPacketAccountServiceImpl implements RedPacketAccountService {
@Autowired
RedPacketAccountRepository redPacketAccountRepository;
@Override
public BigDecimal getRedPacketAccountByUserId(long userId) {
return redPacketAccountRepository.findByUserId(userId).getBalanceAmount();
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/java/io/tyloo/sample/dubbo/redpacket/service/RedPacketTradeOrderServiceImpl.java
================================================
package io.tyloo.sample.dubbo.redpacket.service;
import io.tyloo.sample.dubbo.redpacket.api.RedPacketTradeOrderService;
import io.tyloo.sample.dubbo.redpacket.api.dto.RedPacketTradeOrderDto;
import io.tyloo.sample.redpacket.domain.entity.RedPacketAccount;
import io.tyloo.sample.redpacket.domain.entity.TradeOrder;
import io.tyloo.sample.redpacket.domain.repository.RedPacketAccountRepository;
import io.tyloo.sample.redpacket.domain.repository.TradeOrderRepository;
import org.apache.commons.lang3.time.DateFormatUtils;
import io.tyloo.api.Tyloo;
import io.tyloo.dubbo.context.DubboTransactionContextEditor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Calendar;
@Service("redPacketTradeOrderService")
public class RedPacketTradeOrderServiceImpl implements RedPacketTradeOrderService {
@Autowired
RedPacketAccountRepository redPacketAccountRepository;
@Autowired
TradeOrderRepository tradeOrderRepository;
@Override
@Tyloo(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = DubboTransactionContextEditor.class)
@Transactional
public String record(RedPacketTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("red packet try record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if trade order has been recorded, if yes, return success directly.
if (foundTradeOrder == null) {
TradeOrder tradeOrder = new TradeOrder(
tradeOrderDto.getSelfUserId(),
tradeOrderDto.getOppositeUserId(),
tradeOrderDto.getMerchantOrderNo(),
tradeOrderDto.getAmount()
);
try {
tradeOrderRepository.insert(tradeOrder);
RedPacketAccount transferFromAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
transferFromAccount.transferFrom(tradeOrderDto.getAmount());
redPacketAccountRepository.save(transferFromAccount);
} catch (DataIntegrityViolationException e) {
//this exception may happen when insert trade order concurrently, if happened, ignore this insert operation.
}
}
return "success";
}
@Transactional
public void confirmRecord(RedPacketTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("red packet confirm record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.
if (tradeOrder != null && tradeOrder.getStatus().equals("DRAFT")) {
tradeOrder.confirm();
tradeOrderRepository.update(tradeOrder);
RedPacketAccount transferToAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());
transferToAccount.transferTo(tradeOrderDto.getAmount());
redPacketAccountRepository.save(transferToAccount);
}
}
@Transactional
public void cancelRecord(RedPacketTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("red packet cancel record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.
if (null != tradeOrder && "DRAFT".equals(tradeOrder.getStatus())) {
tradeOrder.cancel();
tradeOrderRepository.update(tradeOrder);
RedPacketAccount capitalAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
capitalAccount.cancelTransfer(tradeOrderDto.getAmount());
redPacketAccountRepository.save(capitalAccount);
}
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/config/spring/local/appcontext-service-provider.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/config/spring/local/appcontext-service-tcc.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/config/spring/local/appcontext-web-servlet.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/log/log4j.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/sample-dubbo-redpacket.properties
================================================
dubbo.port=2880
zookeeper.address=127.0.0.1:2181
zookeeper.session.timeout=30000
zookeeper.connect.timeout=2000
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/resources/tccjdbc.properties
================================================
jdbc.driverClassName=com.mysql.jdbc.Driver
tcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=30000
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/webapp/WEB-INF/web.xml
================================================
Sample Dubbo Redpacket
contextConfigLocation
classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml,classpath:tyloo-dubbo.xml
log4jConfigLocation
classpath:log/log4j.xml
log4jRefreshInterval
60000
org.springframework.web.util.Log4jConfigListener
org.springframework.web.context.ContextLoaderListener
characterEncodingFilter
Character Encoding Filter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
characterEncodingFilter
/*
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:config/spring/local/appcontext-web-servlet.xml
1
springmvc
/
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket/src/main/webapp/index.jsp
================================================
hello tcc transacton dubbo sample red packet
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket-api/pom.xml
================================================
tyloo-dubbo-sample
io.tyloo
1.1.0
4.0.0
tyloo-dubbo-redpacket-api
io.tyloo
tyloo-api
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket-api/src/main/java/io/tyloo/sample/dubbo/redpacket/api/RedPacketAccountService.java
================================================
package io.tyloo.sample.dubbo.redpacket.api;
import java.math.BigDecimal;
public interface RedPacketAccountService {
BigDecimal getRedPacketAccountByUserId(long userId);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket-api/src/main/java/io/tyloo/sample/dubbo/redpacket/api/RedPacketTradeOrderService.java
================================================
package io.tyloo.sample.dubbo.redpacket.api;
import io.tyloo.sample.dubbo.redpacket.api.dto.RedPacketTradeOrderDto;
import io.tyloo.api.Tyloo;
public interface RedPacketTradeOrderService {
@Tyloo
public String record(RedPacketTradeOrderDto tradeOrderDto);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-dubbo-sample/tyloo-dubbo-redpacket-api/src/main/java/io/tyloo/sample/dubbo/redpacket/api/dto/RedPacketTradeOrderDto.java
================================================
package io.tyloo.sample.dubbo.redpacket.api.dto;
import java.io.Serializable;
import java.math.BigDecimal;
public class RedPacketTradeOrderDto implements Serializable {
private static final long serialVersionUID = 4747014387277477558L;
private long selfUserId;
private long oppositeUserId;
private String orderTitle;
private String merchantOrderNo;
private BigDecimal amount;
public long getSelfUserId() {
return selfUserId;
}
public void setSelfUserId(long selfUserId) {
this.selfUserId = selfUserId;
}
public long getOppositeUserId() {
return oppositeUserId;
}
public void setOppositeUserId(long oppositeUserId) {
this.oppositeUserId = oppositeUserId;
}
public String getOrderTitle() {
return orderTitle;
}
public void setOrderTitle(String orderTitle) {
this.orderTitle = orderTitle;
}
public String getMerchantOrderNo() {
return merchantOrderNo;
}
public void setMerchantOrderNo(String merchantOrderNo) {
this.merchantOrderNo = merchantOrderNo;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/pom.xml
================================================
tyloo-tutorial-sample
io.tyloo
1.1.0
4.0.0
tyloo-http-sample
pom
tyloo-http-capital
tyloo-http-capital-api
tyloo-http-order
tyloo-http-redpacket
tyloo-http-redpacket-api
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/pom.xml
================================================
tyloo-http-sample
io.tyloo
1.1.0
4.0.0
tyloo-http-capital
war
io.tyloo
tyloo-http-capital-api
${project.version}
io.tyloo
tyloo-spring
${project.version}
io.tyloo
tyloo-sample-captial
${project.version}
org.springframework
spring-web
org.springframework
spring-webmvc
org.springframework
spring-context
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/java/io/tyloo/sample/http/capital/service/CapitalAccountServiceImpl.java
================================================
package io.tyloo.sample.http.capital.service;
import io.tyloo.sample.capital.domain.repository.CapitalAccountRepository;
import io.tyloo.sample.http.capital.api.CapitalAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
public class CapitalAccountServiceImpl implements CapitalAccountService {
@Autowired
CapitalAccountRepository capitalAccountRepository;
@Override
public BigDecimal getCapitalAccountByUserId(long userId) {
return capitalAccountRepository.findByUserId(userId).getBalanceAmount();
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/java/io/tyloo/sample/http/capital/service/CapitalTradeOrderServiceImpl.java
================================================
package io.tyloo.sample.http.capital.service;
import io.tyloo.sample.capital.domain.entity.CapitalAccount;
import io.tyloo.sample.capital.domain.entity.TradeOrder;
import io.tyloo.sample.capital.domain.repository.CapitalAccountRepository;
import io.tyloo.sample.capital.domain.repository.TradeOrderRepository;
import io.tyloo.sample.http.capital.api.CapitalTradeOrderService;
import io.tyloo.sample.http.capital.api.dto.CapitalTradeOrderDto;
import org.apache.commons.lang3.time.DateFormatUtils;
import io.tyloo.api.Tyloo;
import io.tyloo.api.TransactionContext;
import io.tyloo.context.MethodTransactionContextEditor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.transaction.annotation.Transactional;
import java.util.Calendar;
public class CapitalTradeOrderServiceImpl implements CapitalTradeOrderService {
@Autowired
CapitalAccountRepository capitalAccountRepository;
@Autowired
TradeOrderRepository tradeOrderRepository;
@Override
@Tyloo(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = MethodTransactionContextEditor.class)
@Transactional
public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("capital try record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if trade order has been recorded, if yes, return success directly.
if (foundTradeOrder == null) {
TradeOrder tradeOrder = new TradeOrder(
tradeOrderDto.getSelfUserId(),
tradeOrderDto.getOppositeUserId(),
tradeOrderDto.getMerchantOrderNo(),
tradeOrderDto.getAmount()
);
try {
tradeOrderRepository.insert(tradeOrder);
CapitalAccount transferFromAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
transferFromAccount.transferFrom(tradeOrderDto.getAmount());
capitalAccountRepository.save(transferFromAccount);
} catch (DataIntegrityViolationException e) {
//this exception may happen when insert trade order concurrently, if happened, ignore this insert operation.
}
}
return "success";
}
@Transactional
public void confirmRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("capital confirm record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.
if (null != tradeOrder && "DRAFT".equals(tradeOrder.getStatus())) {
tradeOrder.confirm();
tradeOrderRepository.update(tradeOrder);
CapitalAccount transferToAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());
transferToAccount.transferTo(tradeOrderDto.getAmount());
capitalAccountRepository.save(transferToAccount);
}
}
@Transactional
public void cancelRecord(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("capital cancel record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if the trade order status is DRAFT, if yes, return directly, ensure idempotency.
if (null != tradeOrder && "DRAFT".equals(tradeOrder.getStatus())) {
tradeOrder.cancel();
tradeOrderRepository.update(tradeOrder);
CapitalAccount capitalAccount = capitalAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
capitalAccount.cancelTransfer(tradeOrderDto.getAmount());
capitalAccountRepository.save(capitalAccount);
}
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/config/spring/local/appcontext-service-provider.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/config/spring/local/appcontext-service-tcc.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/config/spring/local/appcontext-web-servlet.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/log/log4j.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/resources/tccjdbc.properties
================================================
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_CAP?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
tcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=30000
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/webapp/WEB-INF/web.xml
================================================
Sample Http Capital
webAppRootKey
tyloo-http-capital
contextConfigLocation
classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml
log4jConfigLocation
classpath:log/log4j.xml
log4jRefreshInterval
60000
org.springframework.web.util.Log4jConfigListener
org.springframework.web.context.ContextLoaderListener
characterEncodingFilter
Character Encoding Filter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
characterEncodingFilter
/*
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:config/spring/local/appcontext-web-servlet.xml
1
springmvc
/
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital/src/main/webapp/index.jsp
================================================
hello tcc transacton http sample capital
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital-api/pom.xml
================================================
tyloo-http-sample
io.tyloo
1.1.0
4.0.0
tyloo-http-capital-api
io.tyloo
tyloo-api
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital-api/src/main/java/io/tyloo/sample/http/capital/api/CapitalAccountService.java
================================================
package io.tyloo.sample.http.capital.api;
import java.math.BigDecimal;
public interface CapitalAccountService {
BigDecimal getCapitalAccountByUserId(long userId);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital-api/src/main/java/io/tyloo/sample/http/capital/api/CapitalTradeOrderService.java
================================================
package io.tyloo.sample.http.capital.api;
import io.tyloo.sample.http.capital.api.dto.CapitalTradeOrderDto;
import io.tyloo.api.TransactionContext;
public interface CapitalTradeOrderService {
public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-capital-api/src/main/java/io/tyloo/sample/http/capital/api/dto/CapitalTradeOrderDto.java
================================================
package io.tyloo.sample.http.capital.api.dto;
import java.io.Serializable;
import java.math.BigDecimal;
public class CapitalTradeOrderDto implements Serializable {
private static final long serialVersionUID = 6627401903410124642L;
private long selfUserId;
private long oppositeUserId;
private String orderTitle;
private String merchantOrderNo;
private BigDecimal amount;
public long getSelfUserId() {
return selfUserId;
}
public void setSelfUserId(long selfUserId) {
this.selfUserId = selfUserId;
}
public long getOppositeUserId() {
return oppositeUserId;
}
public void setOppositeUserId(long oppositeUserId) {
this.oppositeUserId = oppositeUserId;
}
public String getOrderTitle() {
return orderTitle;
}
public void setOrderTitle(String orderTitle) {
this.orderTitle = orderTitle;
}
public String getMerchantOrderNo() {
return merchantOrderNo;
}
public void setMerchantOrderNo(String merchantOrderNo) {
this.merchantOrderNo = merchantOrderNo;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/pom.xml
================================================
tyloo-http-sample
io.tyloo
1.1.0
4.0.0
tyloo-http-order
war
log4j
log4j
io.tyloo
tyloo-http-capital-api
${project.version}
io.tyloo
tyloo-http-redpacket-api
${project.version}
io.tyloo
tyloo-sample-order
${project.version}
io.tyloo
tyloo-spring
${project.version}
org.apache.httpcomponents
httpclient
com.mchange
c3p0
org.apache.httpcomponents
httpclient
commons-codec
commons-codec
mysql
mysql-connector-java
org.mybatis
mybatis-spring
org.mybatis
mybatis
org.aspectj
aspectjrt
org.aspectj
aspectjweaver
org.springframework
spring-web
org.springframework
spring-webmvc
org.springframework
spring-context
org.springframework
spring-beans
org.springframework
spring-jdbc
org.apache.commons
commons-lang3
org.freemarker
freemarker
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/service/AccountServiceImpl.java
================================================
package io.tyloo.sample.http.order.service;
import io.tyloo.sample.http.capital.api.CapitalAccountService;
import io.tyloo.sample.http.redpacket.api.RedPacketAccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
@Service("accountService")
public class AccountServiceImpl {
@Autowired
RedPacketAccountService redPacketAccountService;
@Autowired
CapitalAccountService capitalAccountService;
public BigDecimal getRedPacketAccountByUserId(long userId){
return redPacketAccountService.getRedPacketAccountByUserId(userId);
}
public BigDecimal getCapitalAccountByUserId(long userId){
return capitalAccountService.getCapitalAccountByUserId(userId);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/service/PaymentServiceImpl.java
================================================
package io.tyloo.sample.http.order.service;
import io.tyloo.sample.http.capital.api.dto.CapitalTradeOrderDto;
import io.tyloo.sample.http.redpacket.api.dto.RedPacketTradeOrderDto;
import io.tyloo.sample.order.domain.entity.Order;
import io.tyloo.sample.order.domain.repository.OrderRepository;
import org.apache.commons.lang3.time.DateFormatUtils;
import io.tyloo.api.Tyloo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Calendar;
@Service
public class PaymentServiceImpl {
@Autowired
TradeOrderServiceProxy tradeOrderServiceProxy;
@Autowired
OrderRepository orderRepository;
@Tyloo(confirmMethod = "confirmMakePayment", cancelMethod = "cancelMakePayment", asyncConfirm = true)
@Transactional
public void makePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
System.out.println("order try make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
//check if the order status is DRAFT, if no, means that another call makePayment for the same order happened, ignore this call makePayment.
if (order.getStatus().equals("DRAFT")) {
order.pay(redPacketPayAmount, capitalPayAmount);
try {
orderRepository.updateOrder(order);
} catch (OptimisticLockingFailureException e) {
//ignore the concurrently update order exception, ensure idempotency.
}
}
String result = tradeOrderServiceProxy.record(null, buildCapitalTradeOrderDto(order));
String result2 = tradeOrderServiceProxy.record(null, buildRedPacketTradeOrderDto(order));
}
public void confirmMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("order confirm make payment called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order foundOrder = orderRepository.findByMerchantOrderNo(order.getMerchantOrderNo());
//check order status, only if the status equals DRAFT, then confirm order
if (foundOrder != null && foundOrder.getStatus().equals("PAYING")) {
order.confirm();
orderRepository.updateOrder(order);
}
}
public void cancelMakePayment(Order order, BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("order cancel make payment called.time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
Order foundOrder = orderRepository.findByMerchantOrderNo(order.getMerchantOrderNo());
if (foundOrder != null && foundOrder.getStatus().equals("PAYING")) {
order.cancelPayment();
orderRepository.updateOrder(order);
}
}
private CapitalTradeOrderDto buildCapitalTradeOrderDto(Order order) {
CapitalTradeOrderDto tradeOrderDto = new CapitalTradeOrderDto();
tradeOrderDto.setAmount(order.getCapitalPayAmount());
tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());
tradeOrderDto.setSelfUserId(order.getPayerUserId());
tradeOrderDto.setOppositeUserId(order.getPayeeUserId());
tradeOrderDto.setOrderTitle(String.format("order no:%s", order.getMerchantOrderNo()));
return tradeOrderDto;
}
private RedPacketTradeOrderDto buildRedPacketTradeOrderDto(Order order) {
RedPacketTradeOrderDto tradeOrderDto = new RedPacketTradeOrderDto();
tradeOrderDto.setAmount(order.getRedPacketPayAmount());
tradeOrderDto.setMerchantOrderNo(order.getMerchantOrderNo());
tradeOrderDto.setSelfUserId(order.getPayerUserId());
tradeOrderDto.setOppositeUserId(order.getPayeeUserId());
tradeOrderDto.setOrderTitle(String.format("order no:%s", order.getMerchantOrderNo()));
return tradeOrderDto;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/service/PlaceOrderServiceImpl.java
================================================
package io.tyloo.sample.http.order.service;
import io.tyloo.sample.order.domain.entity.Order;
import io.tyloo.sample.order.domain.entity.Shop;
import io.tyloo.sample.order.domain.repository.ShopRepository;
import io.tyloo.sample.order.domain.service.OrderServiceImpl;
import org.apache.commons.lang3.tuple.Pair;
import io.tyloo.CancellingException;
import io.tyloo.ConfirmingException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
@Service
public class PlaceOrderServiceImpl {
@Autowired
ShopRepository shopRepository;
@Autowired
OrderServiceImpl orderService;
@Autowired
PaymentServiceImpl paymentService;
public String placeOrder(long payerUserId, long shopId, List> productQuantities, BigDecimal redPacketPayAmount) {
Shop shop = shopRepository.findById(shopId);
Order order = orderService.createOrder(payerUserId, shop.getOwnerUserId(), productQuantities);
Boolean result = false;
try {
paymentService.makePayment(order, redPacketPayAmount, order.getTotalAmount().subtract(redPacketPayAmount));
} catch (ConfirmingException confirmingException) {
//exception throws with the tcc transaction status is CONFIRMING,
//when tcc transaction is confirming status,
// the tcc transaction recovery will try to confirm the whole transaction to ensure eventually consistent.
result = true;
} catch (CancellingException cancellingException) {
//exception throws with the tcc transaction status is CANCELLING,
//when tcc transaction is under CANCELLING status,
// the tcc transaction recovery will try to cancel the whole transaction to ensure eventually consistent.
} catch (Throwable e) {
//other exceptions throws at TRYING stage.
//you can retry or cancel the operation.
e.printStackTrace();
}
return order.getMerchantOrderNo();
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/service/TradeOrderServiceProxy.java
================================================
package io.tyloo.sample.http.order.service;
import io.tyloo.sample.http.capital.api.CapitalTradeOrderService;
import io.tyloo.sample.http.capital.api.dto.CapitalTradeOrderDto;
import io.tyloo.sample.http.redpacket.api.RedPacketTradeOrderService;
import io.tyloo.sample.http.redpacket.api.dto.RedPacketTradeOrderDto;
import io.tyloo.api.Tyloo;
import io.tyloo.api.Propagation;
import io.tyloo.api.TransactionContext;
import io.tyloo.context.MethodTransactionContextEditor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
public class TradeOrderServiceProxy {
@Autowired
CapitalTradeOrderService capitalTradeOrderService;
@Autowired
RedPacketTradeOrderService redPacketTradeOrderService;
/*the propagation need set Propagation.SUPPORTS,otherwise the recover doesn't work,
The default value is Propagation.REQUIRED, which means will begin new transaction when recover.
*/
@Tyloo(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = MethodTransactionContextEditor.class)
public String record(TransactionContext transactionContext, CapitalTradeOrderDto tradeOrderDto) {
return capitalTradeOrderService.record(transactionContext, tradeOrderDto);
}
@Tyloo(propagation = Propagation.SUPPORTS, confirmMethod = "record", cancelMethod = "record", transactionContextEditor = MethodTransactionContextEditor.class)
public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {
return redPacketTradeOrderService.record(transactionContext, tradeOrderDto);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/web/controller/OrderController.java
================================================
package io.tyloo.sample.http.order.web.controller;
import io.tyloo.sample.http.order.service.AccountServiceImpl;
import io.tyloo.sample.http.order.service.PlaceOrderServiceImpl;
import io.tyloo.sample.http.order.web.controller.vo.PlaceOrderRequest;
import io.tyloo.sample.order.domain.entity.Order;
import io.tyloo.sample.order.domain.entity.Product;
import io.tyloo.sample.order.domain.repository.ProductRepository;
import io.tyloo.sample.order.domain.service.OrderServiceImpl;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.view.RedirectView;
import java.math.BigDecimal;
import java.security.InvalidParameterException;
import java.util.List;
@Controller
@RequestMapping("")
public class OrderController {
@Autowired
PlaceOrderServiceImpl placeOrderService;
@Autowired
ProductRepository productRepository;
@Autowired
AccountServiceImpl accountService;
@Autowired
OrderServiceImpl orderService;
@RequestMapping(value = "/", method = RequestMethod.GET)
public ModelAndView index() {
ModelAndView mv = new ModelAndView("/index");
return mv;
}
@RequestMapping(value = "/user/{userId}/shop/{shopId}", method = RequestMethod.GET)
public ModelAndView getProductsInShop(@PathVariable long userId,
@PathVariable long shopId) {
List products = productRepository.findByShopId(shopId);
ModelAndView mv = new ModelAndView("/shop");
mv.addObject("products", products);
mv.addObject("userId", userId);
mv.addObject("shopId", shopId);
return mv;
}
@RequestMapping(value = "/user/{userId}/shop/{shopId}/product/{productId}/confirm", method = RequestMethod.GET)
public ModelAndView productDetail(@PathVariable long userId,
@PathVariable long shopId,
@PathVariable long productId) {
ModelAndView mv = new ModelAndView("product_detail");
mv.addObject("capitalAmount", accountService.getCapitalAccountByUserId(userId));
mv.addObject("redPacketAmount", accountService.getRedPacketAccountByUserId(userId));
mv.addObject("product", productRepository.findById(productId));
mv.addObject("userId", userId);
mv.addObject("shopId", shopId);
return mv;
}
@RequestMapping(value = "/placeorder", method = RequestMethod.POST)
public RedirectView placeOrder(@RequestParam String redPacketPayAmount,
@RequestParam long shopId,
@RequestParam long payerUserId,
@RequestParam long productId) {
PlaceOrderRequest request = buildRequest(redPacketPayAmount, shopId, payerUserId, productId);
String merchantOrderNo = placeOrderService.placeOrder(request.getPayerUserId(), request.getShopId(),
request.getProductQuantities(), request.getRedPacketPayAmount());
return new RedirectView("payresult/" + merchantOrderNo);
}
@RequestMapping(value = "/payresult/{merchantOrderNo}", method = RequestMethod.GET)
public ModelAndView getPayResult(@PathVariable String merchantOrderNo) {
ModelAndView mv = new ModelAndView("pay_success");
String payResultTip = null;
Order foundOrder = orderService.findOrderByMerchantOrderNo(merchantOrderNo);
payResultTip = foundOrder.getStatus();
mv.addObject("payResult", payResultTip);
mv.addObject("capitalAmount", accountService.getCapitalAccountByUserId(foundOrder.getPayerUserId()));
mv.addObject("redPacketAmount", accountService.getRedPacketAccountByUserId(foundOrder.getPayerUserId()));
return mv;
}
private PlaceOrderRequest buildRequest(String redPacketPayAmount, long shopId, long payerUserId, long productId) {
BigDecimal redPacketPayAmountInBigDecimal = new BigDecimal(redPacketPayAmount);
if (redPacketPayAmountInBigDecimal.compareTo(BigDecimal.ZERO) < 0) {
throw new InvalidParameterException("invalid red packet amount :" + redPacketPayAmount);
}
PlaceOrderRequest request = new PlaceOrderRequest();
request.setPayerUserId(payerUserId);
request.setShopId(shopId);
request.setRedPacketPayAmount(new BigDecimal(redPacketPayAmount));
request.getProductQuantities().add(new ImmutablePair(productId, 1));
return request;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/java/io/tyloo/sample/http/order/web/controller/vo/PlaceOrderRequest.java
================================================
package io.tyloo.sample.http.order.web.controller.vo;
import org.apache.commons.lang3.tuple.Pair;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
public class PlaceOrderRequest {
private long payerUserId;
private long shopId;
private BigDecimal redPacketPayAmount;
private List> productQuantities = new ArrayList>();
public long getPayerUserId() {
return payerUserId;
}
public void setPayerUserId(long payerUserId) {
this.payerUserId = payerUserId;
}
public long getShopId() {
return shopId;
}
public void setShopId(long shopId) {
this.shopId = shopId;
}
public BigDecimal getRedPacketPayAmount() {
return redPacketPayAmount;
}
public void setRedPacketPayAmount(BigDecimal redPacketPayAmount) {
this.redPacketPayAmount = redPacketPayAmount;
}
public List> getProductQuantities() {
return productQuantities;
}
public void setProductQuantities(List> productQuantities) {
this.productQuantities = productQuantities;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/config/spring/local/appcontext-service-consumer.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/config/spring/local/appcontext-service-tcc.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/config/spring/local/appcontext-web-servlet.xml
================================================
json=application/json
html=text/html
org.springframework.web.servlet.view.freemarker.FreeMarkerView
/WEB-INF/ftl/
5
UTF-8
UTF-8
yyyy-MM-dd HH:mm:ss
HH:mm:ss
0.####
true,false
true
auto_detect
UTF-8
true
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/log/log4j.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/sample-dubbo-order.properties
================================================
zookeeper.address=127.0.0.1:2181
zookeeper.session.timeout=1800000
zookeeper.connect.timeout=30000
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/resources/tccjdbc.properties
================================================
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_ORD?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
tcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=300000
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/ftl/index.ftl
================================================
[#ftl ]
tcc transacton dubbo sample order
sample说明:
打开下面商品列表链接,选择一个商品购买,输入红包支付金额,进行支付,系统将使用红包+资金账户转账支付。
支付成功后,各个project会打印如下日志:
sample-dubbo-order:
order try make payment called
order confirm make payment called
sample-dubbo-capital:
capital try record called
capital confirm record called
sample-dubbo-redpacket:
red packet try record called
red packet confirm record called
商品列表链接
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/ftl/pay_success.ftl
================================================
[#ftl ]
支付结果
支付状态:${payResult}
剩余可用账户余额: ${capitalAmount?string("0.00")}元
剩余可用红包余额: ${redPacketAmount?string("0.00")}元
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/ftl/product_detail.ftl
================================================
[#ftl ]
订单详情
名称: ${product.productName}
价格: ${product.price?string("0.00")}元
可用账户余额: ${capitalAmount?string("0.00")}元
可用红包余额: ${redPacketAmount?string("0.00")}元
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/ftl/shop.ftl
================================================
[#ftl ]
商品列表
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-order/src/main/webapp/WEB-INF/web.xml
================================================
Sample Http Order
webAppRootKey
tyloo-http-order
contextConfigLocation
classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml
log4jConfigLocation
classpath:log/log4j.xml
log4jRefreshInterval
60000
org.springframework.web.util.Log4jConfigListener
org.springframework.web.context.ContextLoaderListener
characterEncodingFilter
Character Encoding Filter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
characterEncodingFilter
/*
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:config/spring/local/appcontext-web-servlet.xml
1
springmvc
/
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/pom.xml
================================================
tyloo-http-sample
io.tyloo
1.1.0
4.0.0
tyloo-http-redpacket
war
io.tyloo
tyloo-http-redpacket-api
${project.version}
io.tyloo
tyloo-sample-redpacket
${project.version}
io.tyloo
tyloo-spring
${project.version}
org.springframework
spring-context
log4j
log4j
com.mchange
c3p0
mysql
mysql-connector-java
org.mybatis
mybatis-spring
org.mybatis
mybatis
org.aspectj
aspectjrt
org.aspectj
aspectjweaver
org.springframework
spring-web
org.springframework
spring-webmvc
org.springframework
spring-context
org.springframework
spring-beans
org.springframework
spring-jdbc
org.apache.commons
commons-lang3
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/java/io/tyloo/sample/http/redpacket/service/RedPacketAccountServiceImpl.java
================================================
package io.tyloo.sample.http.redpacket.service;
import io.tyloo.sample.http.redpacket.api.RedPacketAccountService;
import io.tyloo.sample.redpacket.domain.repository.RedPacketAccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import java.math.BigDecimal;
public class RedPacketAccountServiceImpl implements RedPacketAccountService {
@Autowired
RedPacketAccountRepository redPacketAccountRepository;
@Override
public BigDecimal getRedPacketAccountByUserId(long userId) {
return redPacketAccountRepository.findByUserId(userId).getBalanceAmount();
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/java/io/tyloo/sample/http/redpacket/service/RedPacketTradeOrderServiceImpl.java
================================================
package io.tyloo.sample.http.redpacket.service;
import io.tyloo.sample.http.redpacket.api.RedPacketTradeOrderService;
import io.tyloo.sample.http.redpacket.api.dto.RedPacketTradeOrderDto;
import io.tyloo.sample.redpacket.domain.entity.RedPacketAccount;
import io.tyloo.sample.redpacket.domain.entity.TradeOrder;
import io.tyloo.sample.redpacket.domain.repository.RedPacketAccountRepository;
import io.tyloo.sample.redpacket.domain.repository.TradeOrderRepository;
import org.apache.commons.lang3.time.DateFormatUtils;
import io.tyloo.api.Tyloo;
import io.tyloo.api.TransactionContext;
import io.tyloo.context.MethodTransactionContextEditor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.transaction.annotation.Transactional;
import java.util.Calendar;
public class RedPacketTradeOrderServiceImpl implements RedPacketTradeOrderService {
@Autowired
RedPacketAccountRepository redPacketAccountRepository;
@Autowired
TradeOrderRepository tradeOrderRepository;
@Override
@Tyloo(confirmMethod = "confirmRecord", cancelMethod = "cancelRecord", transactionContextEditor = MethodTransactionContextEditor.class)
@Transactional
public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("red packet try record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder foundTradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
//check if the trade order has need recorded.
//if record, then this method call return success directly.
if (foundTradeOrder == null) {
TradeOrder tradeOrder = new TradeOrder(
tradeOrderDto.getSelfUserId(),
tradeOrderDto.getOppositeUserId(),
tradeOrderDto.getMerchantOrderNo(),
tradeOrderDto.getAmount()
);
try {
tradeOrderRepository.insert(tradeOrder);
RedPacketAccount transferFromAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
transferFromAccount.transferFrom(tradeOrderDto.getAmount());
redPacketAccountRepository.save(transferFromAccount);
} catch (DataIntegrityViolationException e) {
}
}
return "success";
}
@Transactional
public void confirmRecord(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("red packet confirm record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
if (null != tradeOrder && "DRAFT".equals(tradeOrder.getStatus())) {
tradeOrder.confirm();
tradeOrderRepository.update(tradeOrder);
RedPacketAccount transferToAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getOppositeUserId());
transferToAccount.transferTo(tradeOrderDto.getAmount());
redPacketAccountRepository.save(transferToAccount);
}
}
@Transactional
public void cancelRecord(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto) {
try {
Thread.sleep(1000l);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("red packet cancel record called. time seq:" + DateFormatUtils.format(Calendar.getInstance(), "yyyy-MM-dd HH:mm:ss"));
TradeOrder tradeOrder = tradeOrderRepository.findByMerchantOrderNo(tradeOrderDto.getMerchantOrderNo());
if (null != tradeOrder && "DRAFT".equals(tradeOrder.getStatus())) {
tradeOrder.cancel();
tradeOrderRepository.update(tradeOrder);
RedPacketAccount capitalAccount = redPacketAccountRepository.findByUserId(tradeOrderDto.getSelfUserId());
capitalAccount.cancelTransfer(tradeOrderDto.getAmount());
redPacketAccountRepository.save(capitalAccount);
}
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/config/spring/local/appcontext-service-provider.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/config/spring/local/appcontext-service-tcc.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/config/spring/local/appcontext-web-servlet.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/log/log4j.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/resources/tccjdbc.properties
================================================
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_RED?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
tcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=30000
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/webapp/WEB-INF/web.xml
================================================
Sample Dubbo Redpacket
webAppRootKey
tyloo-http-redpacket
contextConfigLocation
classpath*:config/spring/local/appcontext-*.xml,classpath:tyloo.xml
log4jConfigLocation
classpath:log/log4j.xml
log4jRefreshInterval
60000
org.springframework.web.util.Log4jConfigListener
org.springframework.web.context.ContextLoaderListener
characterEncodingFilter
Character Encoding Filter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceEncoding
true
characterEncodingFilter
/*
springmvc
org.springframework.web.servlet.DispatcherServlet
contextConfigLocation
classpath:config/spring/local/appcontext-web-servlet.xml
1
springmvc
/
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket/src/main/webapp/index.jsp
================================================
hello tcc transacton http sample red packet
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket-api/pom.xml
================================================
tyloo-http-sample
io.tyloo
1.1.0
4.0.0
tyloo-http-redpacket-api
io.tyloo
tyloo-api
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket-api/src/main/java/io/tyloo/sample/http/redpacket/api/RedPacketAccountService.java
================================================
package io.tyloo.sample.http.redpacket.api;
import java.math.BigDecimal;
public interface RedPacketAccountService {
BigDecimal getRedPacketAccountByUserId(long userId);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket-api/src/main/java/io/tyloo/sample/http/redpacket/api/RedPacketTradeOrderService.java
================================================
package io.tyloo.sample.http.redpacket.api;
import io.tyloo.sample.http.redpacket.api.dto.RedPacketTradeOrderDto;
import io.tyloo.api.TransactionContext;
public interface RedPacketTradeOrderService {
public String record(TransactionContext transactionContext, RedPacketTradeOrderDto tradeOrderDto);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-http-sample/tyloo-http-redpacket-api/src/main/java/io/tyloo/sample/http/redpacket/api/dto/RedPacketTradeOrderDto.java
================================================
package io.tyloo.sample.http.redpacket.api.dto;
import java.io.Serializable;
import java.math.BigDecimal;
public class RedPacketTradeOrderDto implements Serializable {
private static final long serialVersionUID = 4747014387277477558L;
private long selfUserId;
private long oppositeUserId;
private String orderTitle;
private String merchantOrderNo;
private BigDecimal amount;
public long getSelfUserId() {
return selfUserId;
}
public void setSelfUserId(long selfUserId) {
this.selfUserId = selfUserId;
}
public long getOppositeUserId() {
return oppositeUserId;
}
public void setOppositeUserId(long oppositeUserId) {
this.oppositeUserId = oppositeUserId;
}
public String getOrderTitle() {
return orderTitle;
}
public void setOrderTitle(String orderTitle) {
this.orderTitle = orderTitle;
}
public String getMerchantOrderNo() {
return merchantOrderNo;
}
public void setMerchantOrderNo(String merchantOrderNo) {
this.merchantOrderNo = merchantOrderNo;
}
public BigDecimal getAmount() {
return amount;
}
public void setAmount(BigDecimal amount) {
this.amount = amount;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/pom.xml
================================================
tyloo-tutorial-sample
io.tyloo
1.1.0
4.0.0
tyloo-sample-domain
pom
tyloo-sample-common
tyloo-sample-order
tyloo-sample-captial
tyloo-sample-redpacket
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/pom.xml
================================================
tyloo-sample-domain
io.tyloo
1.1.0
4.0.0
tyloo-sample-captial
io.tyloo
tyloo-sample-common
${project.version}
org.springframework
spring-context
log4j
log4j
com.mchange
c3p0
mysql
mysql-connector-java
org.mybatis
mybatis-spring
org.mybatis
mybatis
org.aspectj
aspectjrt
org.aspectj
aspectjweaver
org.springframework
spring-jdbc
org.springframework
spring-beans
org.apache.commons
commons-lang3
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/domain/entity/CapitalAccount.java
================================================
package io.tyloo.sample.capital.domain.entity;
import io.tyloo.sample.exception.InsufficientBalanceException;
import java.math.BigDecimal;
public class CapitalAccount {
private long id;
private long userId;
private BigDecimal balanceAmount;
private BigDecimal transferAmount = BigDecimal.ZERO;
public long getUserId() {
return userId;
}
public BigDecimal getBalanceAmount() {
return balanceAmount;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void transferFrom(BigDecimal amount) {
this.balanceAmount = this.balanceAmount.subtract(amount);
if (BigDecimal.ZERO.compareTo(this.balanceAmount) > 0) {
throw new InsufficientBalanceException();
}
transferAmount = transferAmount.add(amount.negate());
}
public void transferTo(BigDecimal amount) {
this.balanceAmount = this.balanceAmount.add(amount);
transferAmount = transferAmount.add(amount);
}
public void cancelTransfer(BigDecimal amount) {
transferTo(amount);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/domain/entity/TradeOrder.java
================================================
package io.tyloo.sample.capital.domain.entity;
import java.math.BigDecimal;
public class TradeOrder {
private long id;
private long selfUserId;
private long oppositeUserId;
private String merchantOrderNo;
private BigDecimal amount;
private String status = "DRAFT";
private long version = 1l;
public TradeOrder() {
}
public TradeOrder(long selfUserId, long oppositeUserId, String merchantOrderNo, BigDecimal amount) {
this.selfUserId = selfUserId;
this.oppositeUserId = oppositeUserId;
this.merchantOrderNo = merchantOrderNo;
this.amount = amount;
}
public long getId() {
return id;
}
public long getSelfUserId() {
return selfUserId;
}
public long getOppositeUserId() {
return oppositeUserId;
}
public String getMerchantOrderNo() {
return merchantOrderNo;
}
public BigDecimal getAmount() {
return amount;
}
public String getStatus() {
return status;
}
public void confirm() {
this.status = "CONFIRM";
}
public void cancel() {
this.status = "CANCEL";
}
public long getVersion() {
return version;
}
public void updateVersion() {
this.version = version + 1;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/domain/repository/CapitalAccountRepository.java
================================================
package io.tyloo.sample.capital.domain.repository;
import io.tyloo.sample.capital.domain.entity.CapitalAccount;
import io.tyloo.sample.capital.infrastructure.dao.CapitalAccountDao;
import io.tyloo.sample.exception.InsufficientBalanceException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class CapitalAccountRepository {
@Autowired
CapitalAccountDao capitalAccountDao;
public CapitalAccount findByUserId(long userId) {
return capitalAccountDao.findByUserId(userId);
}
public void save(CapitalAccount capitalAccount) {
int effectCount = capitalAccountDao.update(capitalAccount);
if (effectCount < 1) {
throw new InsufficientBalanceException();
}
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/domain/repository/TradeOrderRepository.java
================================================
package io.tyloo.sample.capital.domain.repository;
import io.tyloo.sample.capital.domain.entity.TradeOrder;
import io.tyloo.sample.capital.infrastructure.dao.TradeOrderDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Repository;
@Repository
public class TradeOrderRepository {
@Autowired
TradeOrderDao tradeOrderDao;
public void insert(TradeOrder tradeOrder) {
tradeOrderDao.insert(tradeOrder);
}
public void update(TradeOrder tradeOrder) {
tradeOrder.updateVersion();
int effectCount = tradeOrderDao.update(tradeOrder);
if (effectCount < 1) {
throw new OptimisticLockingFailureException("update trade order failed");
}
}
public TradeOrder findByMerchantOrderNo(String merchantOrderNo) {
return tradeOrderDao.findByMerchantOrderNo(merchantOrderNo);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/infrastructure/dao/CapitalAccountDao.java
================================================
package io.tyloo.sample.capital.infrastructure.dao;
import io.tyloo.sample.capital.domain.entity.CapitalAccount;
public interface CapitalAccountDao {
CapitalAccount findByUserId(long userId);
int update(CapitalAccount capitalAccount);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/java/io/tyloo/sample/capital/infrastructure/dao/TradeOrderDao.java
================================================
package io.tyloo.sample.capital.infrastructure.dao;
import io.tyloo.sample.capital.domain.entity.TradeOrder;
public interface TradeOrderDao {
int insert(TradeOrder tradeOrder);
int update(TradeOrder tradeOrder);
TradeOrder findByMerchantOrderNo(String merchantOrderNo);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/config/spring/local/appcontext-service-dao.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/config/spring/local/appcontext-service-datasource.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/config/sqlmap/main/sample-capitalaccount.xml
================================================
SELECT
CAPITAL_ACCOUNT_ID,
BALANCE_AMOUNT,
USER_ID
FROM
CAP_CAPITAL_ACCOUNT
INSERT INTO CAP_CAPITAL_ACCOUNT
(
BALANCE_AMOUNT,
USER_ID
)
VALUES
(
#{balanceAmount},
#{userId}
)
UPDATE
CAP_CAPITAL_ACCOUNT
SET
BALANCE_AMOUNT = BALANCE_AMOUNT+#{transferAmount}
WHERE CAPITAL_ACCOUNT_ID = #{id} AND BALANCE_AMOUNT+#{transferAmount}>=0
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/config/sqlmap/main/sample-tradeorder.xml
================================================
SELECT
ID,
SELF_USER_ID,
OPPOSITE_USER_ID,
MERCHANT_ORDER_NO,
AMOUNT,
STATUS,
VERSION
FROM
CAP_TRADE_ORDER
INSERT INTO CAP_TRADE_ORDER
(
SELF_USER_ID,
OPPOSITE_USER_ID,
MERCHANT_ORDER_NO,
AMOUNT,
STATUS,
VERSION
)
VALUES
(
#{selfUserId},
#{oppositeUserId},
#{merchantOrderNo},
#{amount},
#{status},
#{version}
)
UPDATE
CAP_TRADE_ORDER
SET
STATUS = #{status},
VERSION = #{version}
WHERE ID = #{id} AND VERSION = #{version}-1
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/jdbc.properties
================================================
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_CAP?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
tcc.jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=30000
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-captial/src/main/resources/log/log4j.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-common/pom.xml
================================================
tyloo-sample-domain
io.tyloo
1.1.0
4.0.0
tyloo-sample-common
jar
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-common/src/main/java/io/tyloo/sample/exception/InsufficientBalanceException.java
================================================
package io.tyloo.sample.exception;
public class InsufficientBalanceException extends RuntimeException {
private static final long serialVersionUID = 6689953065473521009L;
public InsufficientBalanceException() {
}
public InsufficientBalanceException(String message) {
super(message);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/pom.xml
================================================
tyloo-sample-domain
io.tyloo
1.1.0
4.0.0
tyloo-sample-order
io.tyloo
tyloo-sample-common
${project.version}
org.springframework
spring-context
log4j
log4j
com.mchange
c3p0
mysql
mysql-connector-java
org.mybatis
mybatis-spring
org.mybatis
mybatis
org.aspectj
aspectjrt
org.aspectj
aspectjweaver
org.springframework
spring-jdbc
org.springframework
spring-beans
org.apache.commons
commons-lang3
cn.hutool
hutool-all
5.2.5
compile
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/entity/Order.java
================================================
package io.tyloo.sample.order.domain.entity;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import cn.hutool.core.lang.UUID;
public class Order implements Serializable {
private static final long serialVersionUID = -5908730245224893590L;
private long id;
private long payerUserId;
private long payeeUserId;
private BigDecimal redPacketPayAmount;
private BigDecimal capitalPayAmount;
private String status = "DRAFT";
private String merchantOrderNo;
private long version = 1l;
private List orderLines = new ArrayList();
public Order() {
}
public Order(long payerUserId, long payeeUserId) {
this.payerUserId = payerUserId;
this.payeeUserId = payeeUserId;
this.merchantOrderNo = UUID.randomUUID().toString().replace("-","");
}
public long getPayerUserId() {
return payerUserId;
}
public long getPayeeUserId() {
return payeeUserId;
}
public BigDecimal getTotalAmount() {
BigDecimal totalAmount = BigDecimal.ZERO;
for (OrderLine orderLine : orderLines) {
totalAmount = totalAmount.add(orderLine.getTotalAmount());
}
return totalAmount;
}
public void addOrderLine(OrderLine orderLine) {
this.orderLines.add(orderLine);
}
public List getOrderLines() {
return Collections.unmodifiableList(this.orderLines);
}
public void pay(BigDecimal redPacketPayAmount, BigDecimal capitalPayAmount) {
this.redPacketPayAmount = redPacketPayAmount;
this.capitalPayAmount = capitalPayAmount;
this.status = "PAYING";
}
public BigDecimal getRedPacketPayAmount() {
return redPacketPayAmount;
}
public BigDecimal getCapitalPayAmount() {
return capitalPayAmount;
}
public String getMerchantOrderNo() {
return merchantOrderNo;
}
public void setMerchantOrderNo(String merchantOrderNo) {
this.merchantOrderNo = merchantOrderNo;
}
public long getId() {
return id;
}
public String getStatus() {
return status;
}
public void confirm() {
this.status = "CONFIRMED";
}
public void cancelPayment() {
this.status = "PAY_FAILED";
}
public long getVersion() {
return version;
}
public void updateVersion() {
version++;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/entity/OrderLine.java
================================================
package io.tyloo.sample.order.domain.entity;
import java.io.Serializable;
import java.math.BigDecimal;
public class OrderLine implements Serializable {
private static final long serialVersionUID = 2300754647209250837L;
private long id;
private long productId;
private int quantity;
private BigDecimal unitPrice;
public OrderLine() {
}
public OrderLine(Long productId, Integer quantity,BigDecimal unitPrice) {
this.productId = productId;
this.quantity = quantity;
this.unitPrice = unitPrice;
}
public long getProductId() {
return productId;
}
public int getQuantity() {
return quantity;
}
public BigDecimal getUnitPrice() {
return unitPrice;
}
public BigDecimal getTotalAmount() {
return unitPrice.multiply(BigDecimal.valueOf(quantity));
}
public long getId() {
return id;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/entity/Product.java
================================================
package io.tyloo.sample.order.domain.entity;
import java.io.Serializable;
import java.math.BigDecimal;
public class Product implements Serializable{
private long productId;
private long shopId;
private String productName;
private BigDecimal price;
public Product() {
}
public Product(long productId, long shopId, String productName, BigDecimal price) {
this.productId = productId;
this.shopId = shopId;
this.productName = productName;
this.price = price;
}
public long getProductId() {
return productId;
}
public long getShopId() {
return shopId;
}
public String getProductName() {
return productName;
}
public BigDecimal getPrice() {
return price;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/entity/Shop.java
================================================
package io.tyloo.sample.order.domain.entity;
public class Shop {
private long id;
private long ownerUserId;
public long getOwnerUserId() {
return ownerUserId;
}
public long getId() {
return id;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/factory/OrderFactory.java
================================================
package io.tyloo.sample.order.domain.factory;
import io.tyloo.sample.order.domain.entity.Order;
import io.tyloo.sample.order.domain.entity.OrderLine;
import io.tyloo.sample.order.domain.repository.ProductRepository;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component
public class OrderFactory {
@Autowired
ProductRepository productRepository;
public Order buildOrder(long payerUserId, long payeeUserId, List> productQuantities) {
Order order = new Order(payerUserId, payeeUserId);
for (Pair pair : productQuantities) {
long productId = pair.getLeft();
order.addOrderLine(new OrderLine(productId, pair.getRight(), productRepository.findById(productId).getPrice()));
}
return order;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/repository/OrderRepository.java
================================================
package io.tyloo.sample.order.domain.repository;
import io.tyloo.sample.order.domain.entity.Order;
import io.tyloo.sample.order.domain.entity.OrderLine;
import io.tyloo.sample.order.infrastructure.dao.OrderDao;
import io.tyloo.sample.order.infrastructure.dao.OrderLineDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Repository;
/*
*
* @Author:Zh1Cheung zh1cheunglq@gmail.com
* @Date: 18:04 2019/4/19
*
*/
@Repository
public class OrderRepository {
@Autowired
OrderDao orderDao;
@Autowired
OrderLineDao orderLineDao;
public void createOrder(Order order) {
orderDao.insert(order);
for (OrderLine orderLine : order.getOrderLines()) {
orderLineDao.insert(orderLine);
}
}
public void updateOrder(Order order) {
order.updateVersion();
int effectCount = orderDao.update(order);
if (effectCount < 1) {
throw new OptimisticLockingFailureException("update order failed");
}
}
public Order findByMerchantOrderNo(String merchantOrderNo) {
return orderDao.findByMerchantOrderNo(merchantOrderNo);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/repository/ProductRepository.java
================================================
package io.tyloo.sample.order.domain.repository;
import io.tyloo.sample.order.domain.entity.Product;
import io.tyloo.sample.order.infrastructure.dao.ProductDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public class ProductRepository {
@Autowired
ProductDao productDao;
public Product findById(long productId){
return productDao.findById(productId);
}
public List findByShopId(long shopId){
return productDao.findByShopId(shopId);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/repository/ShopRepository.java
================================================
package io.tyloo.sample.order.domain.repository;
import io.tyloo.sample.order.domain.entity.Shop;
import io.tyloo.sample.order.infrastructure.dao.ShopDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class ShopRepository {
@Autowired
ShopDao shopDao;
public Shop findById(long id) {
return shopDao.findById(id);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/domain/service/OrderServiceImpl.java
================================================
package io.tyloo.sample.order.domain.service;
import io.tyloo.sample.order.domain.entity.Order;
import io.tyloo.sample.order.domain.factory.OrderFactory;
import io.tyloo.sample.order.domain.repository.OrderRepository;
import org.apache.commons.lang3.tuple.Pair;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class OrderServiceImpl {
@Autowired
OrderRepository orderRepository;
@Autowired
OrderFactory orderFactory;
@Transactional
public Order createOrder(long payerUserId, long payeeUserId, List> productQuantities) {
Order order = orderFactory.buildOrder(payerUserId, payeeUserId, productQuantities);
orderRepository.createOrder(order);
return order;
}
public Order findOrderByMerchantOrderNo(String orderNo) {
return orderRepository.findByMerchantOrderNo(orderNo);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/infrastructure/dao/OrderDao.java
================================================
package io.tyloo.sample.order.infrastructure.dao;
import io.tyloo.sample.order.domain.entity.Order;
public interface OrderDao {
public int insert(Order order);
public int update(Order order);
Order findByMerchantOrderNo(String merchantOrderNo);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/infrastructure/dao/OrderLineDao.java
================================================
package io.tyloo.sample.order.infrastructure.dao;
import io.tyloo.sample.order.domain.entity.OrderLine;
public interface OrderLineDao {
void insert(OrderLine orderLine);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/infrastructure/dao/ProductDao.java
================================================
package io.tyloo.sample.order.infrastructure.dao;
import io.tyloo.sample.order.domain.entity.Product;
import java.util.List;
public interface ProductDao {
Product findById(long productId);
List findByShopId(long shopId);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/java/io/tyloo/sample/order/infrastructure/dao/ShopDao.java
================================================
package io.tyloo.sample.order.infrastructure.dao;
import io.tyloo.sample.order.domain.entity.Shop;
public interface ShopDao {
Shop findById(long id);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/spring/local/appcontext-service-dao.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/spring/local/appcontext-service-datasource.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/sqlmap/main/sample-order.xml
================================================
SELECT
ORDER_ID,
PAYER_USER_ID,
PAYEE_USER_ID,
RED_PACKET_PAY_AMOUNT,
CAPITAL_PAY_AMOUNT,
STATUS,
MERCHANT_ORDER_NO,
VERSION
FROM
ORD_ORDER
INSERT INTO ORD_ORDER
(
PAYER_USER_ID,
PAYEE_USER_ID,
RED_PACKET_PAY_AMOUNT,
CAPITAL_PAY_AMOUNT,
STATUS,
MERCHANT_ORDER_NO,
VERSION
)
VALUES
(
#{payerUserId},
#{payeeUserId},
#{redPacketPayAmount},
#{capitalPayAmount},
#{status},
#{merchantOrderNo},
#{version}
)
UPDATE
ORD_ORDER
SET
STATUS = #{status},
RED_PACKET_PAY_AMOUNT = #{redPacketPayAmount},
CAPITAL_PAY_AMOUNT = #{capitalPayAmount},
VERSION = #{version}
WHERE ORDER_ID = #{id} AND VERSION=#{version}-1
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/sqlmap/main/sample-orderline.xml
================================================
SELECT
ORDER_LINE_ID,
PRODUCT_ID,
QUANTITY,
UNIT_PRICE
FROM
TCC_ORDER_LINE
INSERT INTO ORD_ORDER_LINE
(
PRODUCT_ID,
QUANTITY,
UNIT_PRICE
)
VALUES
(
#{productId},
#{quantity},
#{unitPrice}
)
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/sqlmap/main/sample-product.xml
================================================
SELECT
PRODUCT_ID,
SHOP_ID,
PRODUCT_NAME,
PRICE
FROM
ORD_PRODUCT
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/config/sqlmap/main/sample-shop.xml
================================================
SELECT
SHOP_ID,
OWNER_USER_ID
FROM
ORD_SHOP
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/jdbc.properties
================================================
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/TCC_ORD?autoReconnect=true&useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=3000
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/log/log4j.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-order/src/main/resources/sample-order.properties
================================================
zookeeper.address=127.0.0.1:2181
zookeeper.session.timeout=1800000
zookeeper.connect.timeout=30000
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/pom.xml
================================================
tyloo-sample-domain
io.tyloo
1.1.0
4.0.0
tyloo-sample-redpacket
io.tyloo
tyloo-sample-common
${project.version}
org.springframework
spring-context
log4j
log4j
com.mchange
c3p0
mysql
mysql-connector-java
org.mybatis
mybatis-spring
org.mybatis
mybatis
org.aspectj
aspectjrt
org.aspectj
aspectjweaver
org.springframework
spring-jdbc
org.springframework
spring-beans
org.apache.commons
commons-lang3
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/domain/entity/RedPacketAccount.java
================================================
package io.tyloo.sample.redpacket.domain.entity;
import io.tyloo.sample.exception.InsufficientBalanceException;
import java.math.BigDecimal;
public class RedPacketAccount {
private long id;
private long userId;
private BigDecimal balanceAmount;
public long getUserId() {
return userId;
}
public BigDecimal getBalanceAmount() {
return balanceAmount;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void transferFrom(BigDecimal amount) {
this.balanceAmount = this.balanceAmount.subtract(amount);
if (BigDecimal.ZERO.compareTo(this.balanceAmount) > 0) {
throw new InsufficientBalanceException();
}
}
public void transferTo(BigDecimal amount) {
this.balanceAmount = this.balanceAmount.add(amount);
}
public void cancelTransfer(BigDecimal amount) {
transferTo(amount);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/domain/entity/TradeOrder.java
================================================
package io.tyloo.sample.redpacket.domain.entity;
import java.math.BigDecimal;
public class TradeOrder {
private long id;
private long selfUserId;
private long oppositeUserId;
private String merchantOrderNo;
private BigDecimal amount;
private String status = "DRAFT";
private long version = 1l;
public TradeOrder() {
}
public TradeOrder(long selfUserId, long oppositeUserId, String merchantOrderNo, BigDecimal amount) {
this.selfUserId = selfUserId;
this.oppositeUserId = oppositeUserId;
this.merchantOrderNo = merchantOrderNo;
this.amount = amount;
}
public long getId() {
return id;
}
public long getSelfUserId() {
return selfUserId;
}
public long getOppositeUserId() {
return oppositeUserId;
}
public String getMerchantOrderNo() {
return merchantOrderNo;
}
public BigDecimal getAmount() {
return amount;
}
public String getStatus() {
return status;
}
public void confirm() {
this.status = "CONFIRM";
}
public void cancel() {
this.status = "CANCEL";
}
public long getVersion() {
return version;
}
public void updateVersion() {
version = version + 1;
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/domain/repository/RedPacketAccountRepository.java
================================================
package io.tyloo.sample.redpacket.domain.repository;
import io.tyloo.sample.exception.InsufficientBalanceException;
import io.tyloo.sample.redpacket.domain.entity.RedPacketAccount;
import io.tyloo.sample.redpacket.infrastructure.dao.RedPacketAccountDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
@Repository
public class RedPacketAccountRepository {
@Autowired
RedPacketAccountDao redPacketAccountDao;
public RedPacketAccount findByUserId(long userId) {
return redPacketAccountDao.findByUserId(userId);
}
public void save(RedPacketAccount redPacketAccount) {
int effectCount = redPacketAccountDao.update(redPacketAccount);
if (effectCount < 1) {
throw new InsufficientBalanceException();
}
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/domain/repository/TradeOrderRepository.java
================================================
package io.tyloo.sample.redpacket.domain.repository;
import io.tyloo.sample.redpacket.domain.entity.TradeOrder;
import io.tyloo.sample.redpacket.infrastructure.dao.TradeOrderDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.OptimisticLockingFailureException;
import org.springframework.stereotype.Repository;
@Repository
public class TradeOrderRepository {
@Autowired
TradeOrderDao tradeOrderDao;
public void insert(TradeOrder tradeOrder) {
tradeOrderDao.insert(tradeOrder);
}
public void update(TradeOrder tradeOrder) {
tradeOrder.updateVersion();
int effectCount = tradeOrderDao.update(tradeOrder);
if (effectCount < 1) {
throw new OptimisticLockingFailureException("update trade order failed");
}
}
public TradeOrder findByMerchantOrderNo(String merchantOrderNo) {
return tradeOrderDao.findByMerchantOrderNo(merchantOrderNo);
}
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/infrastructure/dao/RedPacketAccountDao.java
================================================
package io.tyloo.sample.redpacket.infrastructure.dao;
import io.tyloo.sample.redpacket.domain.entity.RedPacketAccount;
public interface RedPacketAccountDao {
RedPacketAccount findByUserId(long userId);
int update(RedPacketAccount redPacketAccount);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/java/io/tyloo/sample/redpacket/infrastructure/dao/TradeOrderDao.java
================================================
package io.tyloo.sample.redpacket.infrastructure.dao;
import io.tyloo.sample.redpacket.domain.entity.TradeOrder;
public interface TradeOrderDao {
void insert(TradeOrder tradeOrder);
int update(TradeOrder tradeOrder);
TradeOrder findByMerchantOrderNo(String merchantOrderNo);
}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/config/spring/local/appcontext-service-dao.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/config/spring/local/appcontext-service-datasource.xml
================================================
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/config/sqlmap/main/sample-redpacketaccount.xml
================================================
SELECT
RED_PACKET_ACCOUNT_ID,
BALANCE_AMOUNT,
USER_ID
FROM
RED_RED_PACKET_ACCOUNT
INSERT INTO RED_RED_PACKET_ACCOUNT
(
BALANCE_AMOUNT,
USER_ID
)
VALUES
(
#{balanceAmount},
#{userId}
)
UPDATE
RED_RED_PACKET_ACCOUNT
SET
BALANCE_AMOUNT = #{balanceAmount}
WHERE RED_PACKET_ACCOUNT_ID = #{id}
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/config/sqlmap/main/sample-tradeorder.xml
================================================
SELECT
ID,
SELF_USER_ID,
OPPOSITE_USER_ID,
MERCHANT_ORDER_NO,
AMOUNT,
STATUS,
VERSION
FROM
RED_TRADE_ORDER
INSERT INTO RED_TRADE_ORDER
(
SELF_USER_ID,
OPPOSITE_USER_ID,
MERCHANT_ORDER_NO,
AMOUNT,
STATUS,
VERSION
)
VALUES
(
#{selfUserId},
#{oppositeUserId},
#{merchantOrderNo},
#{amount},
#{status},
#{version}
)
UPDATE
RED_TRADE_ORDER
SET
STATUS = #{status},
VERSION = #{version}
WHERE ID = #{id} AND VERSION = #{version}-1
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/jdbc.properties
================================================
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/TCC_RED?useUnicode=true&characterEncoding=UTF-8&allowMultiQueries=true
jdbc.username=root
jdbc.password=root
c3p0.initialPoolSize=10
c3p0.minPoolSize=10
c3p0.maxPoolSize=30
c3p0.acquireIncrement=3
c3p0.maxIdleTime=1800
c3p0.checkoutTimeout=30000
================================================
FILE: tyloo-tutorial-sample/tyloo-sample-domain/tyloo-sample-redpacket/src/main/resources/log/log4j.xml
================================================
================================================
FILE: tyloo-unit-test/pom.xml
================================================
io.tyloo
tyloo
1.1.0
4.0.0
tyloo-unit-test
jar
io.tyloo
tyloo-spring
${project.version}
junit
junit
org.springframework
spring-context
org.springframework
spring-test
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/client/AccountRecordServiceProxy.java
================================================
package io.tyloo.unittest.client;
import io.tyloo.api.TransactionContext;
import io.tyloo.unittest.thirdservice.AccountRecordService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import static java.util.concurrent.Executors.*;
@Service
public class AccountRecordServiceProxy {
@Autowired
private AccountRecordService accountRecordService;
private ExecutorService executorService = newFixedThreadPool(100);
public void record(final TransactionContext transactionContext, final long accountId, final int amount) {
// Future future = this.executorService
// .submit(new Callable() {
// @Override
// public Boolean call() throws Exception {
// accountRecordService.record(transactionContext, accountId, amount);
// return true;
// }
// });
//
// handleResult(future);
accountRecordService.record(transactionContext, accountId, amount);
}
private void handleResult(Future future) {
while (!future.isDone()) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
future.get();
} catch (InterruptedException | ExecutionException e) {
throw new Error(e);
}
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/client/AccountServiceProxy.java
================================================
package io.tyloo.unittest.client;
import io.tyloo.api.TransactionContext;
import io.tyloo.unittest.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.*;
@Service
public class AccountServiceProxy {
@Autowired
AccountService accountService;
private ExecutorService executorService = Executors.newFixedThreadPool(100);
public void transferFromWithMultipleTier(final TransactionContext transactionContext, final long accountId, final int amount) {
Future future = this.executorService
.submit(new Callable() {
@Override
public Boolean call() throws Exception {
accountService.transferFromWithMultipleTier(transactionContext, accountId, amount);
return true;
}
});
handleResult(future);
}
public void transferToWithMultipleTier(final TransactionContext transactionContext, final long accountId, final int amount) {
// Future future = this.executorService
// .submit(new Callable() {
// @Override
// public Boolean call() throws Exception {
// accountService.transferToWithMultipleTier(transactionContext, accountId, amount);
// return true;
// }
// });
//
// handleResult(future);
accountService.transferToWithMultipleTier(transactionContext, accountId, amount);
}
public void performanceTuningTransferTo(TransactionContext transactionContext) {
}
public void transferTo(final TransactionContext transactionContext, final long accountId, final int amount) {
// Future future = this.executorService
// .submit(new Callable() {
// @Override
// public Boolean call() throws Exception {
// accountService.transferTo(transactionContext, accountId, amount);
// return true;
// }
// });
//
// handleResult(future);
accountService.transferTo(transactionContext, accountId, amount);
}
public void transferTo(final long accountId, final int amount) {
// Future future = this.executorService
// .submit(new Callable() {
// @Override
// public Boolean call() throws Exception {
// accountService.transferToWithNoTransactionContext(accountId, amount);
// return true;
// }
// });
//
// handleResult(future);
accountService.transferToWithNoTransactionContext(accountId, amount);
}
public void transferFrom(final TransactionContext transactionContext, final long accountId, final int amount) {
// Future future = this.executorService
// .submit(new Callable() {
// @Override
// public Boolean call() throws Exception {
// accountService.transferFrom(transactionContext, accountId, amount);
// return true;
// }
// });
//
// handleResult(future);
accountService.transferFrom(transactionContext, accountId, amount);
}
private void handleResult(Future future) {
while (!future.isDone()) {
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
future.get();
} catch (InterruptedException e) {
throw new Error(e);
} catch (ExecutionException e) {
throw new Error(e);
}
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/client/TransferService.java
================================================
package io.tyloo.unittest.client;
import io.tyloo.api.Tyloo;
import io.tyloo.api.Propagation;
import io.tyloo.unittest.entity.AccountStatus;
import io.tyloo.unittest.entity.SubAccount;
import io.tyloo.unittest.repository.SubAccountRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class TransferService {
@Autowired
AccountServiceProxy accountService;
@Autowired
SubAccountRepository subAccountRepository;
public TransferService() {
}
@Tyloo
@Transactional
public void performenceTuningTransfer() {
accountService.performanceTuningTransferTo(null);
}
@Tyloo(propagation = Propagation.MANDATORY)
public void transferWithMandatoryPropagation(long fromAccountId, long toAccountId, int amount) {
System.out.println("transfer called");
SubAccount subAccount = subAccountRepository.findById(fromAccountId);
subAccount.setStatus(AccountStatus.TRANSFERING.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);
accountService.transferTo(null, toAccountId, amount);
}
@Tyloo(confirmMethod = "transferConfirm", cancelMethod = "transferCancel")
@Transactional
public void transfer(long fromAccountId, long toAccountId, int amount) {
System.out.println("transfer called");
SubAccount subAccount = subAccountRepository.findById(fromAccountId);
subAccount.setStatus(AccountStatus.TRANSFERING.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);
accountService.transferTo(null, toAccountId, amount);
}
@Tyloo(confirmMethod = "transferConfirm", cancelMethod = "transferCancel")
public void transferWithMultipleTier(long fromAccountId, long toAccountId, int amount) {
System.out.println("transferWithMultipleTier called");
SubAccount subAccount = subAccountRepository.findById(fromAccountId);
subAccount.setStatus(AccountStatus.TRANSFERING.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);
accountService.transferToWithMultipleTier(null, toAccountId, amount);
}
@Tyloo(confirmMethod = "transferWithMultipleConsumerConfirm", cancelMethod = "transferWithMultipleConsumerCancel")
@Transactional
public void transferWithMultipleConsumer(long fromAccountId, long toAccountId, int amount) {
System.out.println("transferWithMultipleConsumer called");
accountService.transferFrom(null, fromAccountId, amount);
accountService.transferTo(null, toAccountId, amount);
}
@Tyloo
public void transferWithOnlyTryAndMultipleConsumer(long fromAccountId, long toAccountId, int amount) {
System.out.println("transferWithOnlyTryAndMultipleConsumer called");
accountService.transferFrom(null, fromAccountId, amount);
accountService.transferTo(null, toAccountId, amount);
}
@Tyloo
public void transferWithNoTransactionContext(long fromAccountId, long toAccountId, int amount) {
System.out.println("transferWithNoTransactionContext called");
accountService.transferTo(toAccountId, amount);
}
public void transferConfirm(long fromAccountId, long toAccountId, int amount) {
System.out.println("transferConfirm called");
SubAccount subAccount = subAccountRepository.findById(fromAccountId);
subAccount.setStatus(AccountStatus.NORMAL.getId());
}
public void transferCancel(long fromAccountId, long toAccountId, int amount) {
System.out.println("transferCancel called");
SubAccount subAccount = subAccountRepository.findById(fromAccountId);
if (subAccount.getStatus() == AccountStatus.TRANSFERING.getId()) {
subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);
}
subAccount.setStatus(AccountStatus.NORMAL.getId());
}
public void transferWithMultipleConsumerConfirm(long fromAccountId, long toAccountId, int amount) {
System.out.println("transferWithMultipleConsumerConfirm called");
}
public void transferWithMultipleConsumerCancel(long fromAccountId, long toAccountId, int amount) {
System.out.println("transferWithMultipleConsumerCancel called");
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/entity/AccountRecord.java
================================================
package io.tyloo.unittest.entity;
public class AccountRecord {
private long accountId;
private volatile int balanceAmount;
private volatile int statusId = AccountStatus.NORMAL.getId();
public AccountRecord(long accountId, int balanceAmount) {
this.accountId = accountId;
this.balanceAmount = balanceAmount;
}
public long getAccountId() {
return accountId;
}
public int getBalanceAmount() {
return balanceAmount;
}
public void setStatusId(int statusId) {
this.statusId = statusId;
}
public void setBalanceAmount(int balanceAmount) {
this.balanceAmount = balanceAmount;
}
public int getStatusId() {
return statusId;
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/entity/AccountStatus.java
================================================
package io.tyloo.unittest.entity;
public enum AccountStatus {
NORMAL(1),
TRANSFERING(2);
private int id;
AccountStatus(int id) {
this.id = id;
}
public int getId() {
return id;
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/entity/SubAccount.java
================================================
package io.tyloo.unittest.entity;
public class SubAccount {
private long id;
private volatile int balanceAmount;
private volatile int status = AccountStatus.NORMAL.getId();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public int getBalanceAmount() {
return balanceAmount;
}
public void setBalanceAmount(int balanceAmount) {
this.balanceAmount = balanceAmount;
}
public SubAccount() {
}
public SubAccount(long id, int balanceAmount) {
this.id = id;
this.balanceAmount = balanceAmount;
}
public void setStatus(int status) {
this.status = status;
}
public int getStatus() {
return status;
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/entity/UserShardingId.java
================================================
package io.tyloo.unittest.entity;
import java.io.Serializable;
public abstract class UserShardingId implements Serializable {
private static final long serialVersionUID = -8923642703284688507L;
private Long id;
private Long userId;
public UserShardingId() {
}
public UserShardingId(Long userId) {
this.userId = userId;
}
public UserShardingId(Long id, Long userId) {
this.id = id;
this.userId = userId;
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Long getUserId() {
return userId;
}
public void setUserId(Long userId) {
this.userId = userId;
}
@Override
public boolean equals(Object obj) {
if (null == obj) {
return false;
}
if (this == obj) {
return true;
}
if (!getClass().equals(obj.getClass())) {
return false;
}
UserShardingId that = (UserShardingId) obj;
boolean isIdEquals = false;
boolean isUserIdEquals = false;
if ((this.id == null && that.id == null) || (this.id != null && this.id.equals(that.id))) {
isIdEquals = true;
}
if ((this.userId == null && that.userId == null) || (this.userId != null && this.userId.equals(that.userId))) {
isUserIdEquals = true;
}
return isIdEquals && isUserIdEquals;
}
@Override
public int hashCode() {
int hashCode = 17;
hashCode += this.id == null ? 0 : this.id * 31;
hashCode += this.userId == null ? 0 : this.userId * 31;
return hashCode;
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/repository/AccountRecordRepository.java
================================================
package io.tyloo.unittest.repository;
import io.tyloo.unittest.entity.AccountRecord;
import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.Map;
@Repository
public class AccountRecordRepository {
private Map accountRecordMap = new HashMap();
{
accountRecordMap.put(1L, new AccountRecord(1, 0));
accountRecordMap.put(2L, new AccountRecord(2, 0));
accountRecordMap.put(3L, new AccountRecord(3, 0));
}
public AccountRecord findById(Long id) {
return accountRecordMap.get(id);
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/repository/SubAccountRepository.java
================================================
package io.tyloo.unittest.repository;
import io.tyloo.unittest.entity.SubAccount;
import org.springframework.stereotype.Repository;
import java.util.HashMap;
import java.util.Map;
@Repository
public class SubAccountRepository {
private Map subAccountMap = new HashMap();
{
subAccountMap.put(1L, new SubAccount(1, 100));
subAccountMap.put(2L, new SubAccount(2, 200));
subAccountMap.put(3L, new SubAccount(3, 300));
}
public SubAccount findById(Long id) {
return subAccountMap.get(id);
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/service/AccountService.java
================================================
package io.tyloo.unittest.service;
import io.tyloo.api.TransactionContext;
public interface AccountService {
void transferTo(TransactionContext transactionContext, long accountId, int amount);
void transferToConfirm(TransactionContext transactionContext, long accountId, int amount);
void transferToCancel(TransactionContext transactionContext, long accountId, int amount);
void transferToWithNoTransactionContext(long accountId, int amount);
void transferToConfirmWithNoTransactionContext(long accountId, int amount);
void transferToCancelWithNoTransactionContext(long accountId, int amount);
void transferFrom(TransactionContext transactionContext, long accountId, int amount);
void transferFromConfirm(TransactionContext transactionContext, long accountId, int amount);
void transferFromCancel(TransactionContext transactionContext, long accountId, int amount);
void transferToWithMultipleTier(TransactionContext transactionContext, long accountId, int amount);
void transferFromWithMultipleTier(TransactionContext transactionContext, long accountId, int amount);
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/service/AccountServiceImpl.java
================================================
package io.tyloo.unittest.service;
import io.tyloo.api.Tyloo;
import io.tyloo.api.Propagation;
import io.tyloo.api.TransactionContext;
import io.tyloo.unittest.client.AccountRecordServiceProxy;
import io.tyloo.unittest.entity.AccountStatus;
import io.tyloo.unittest.entity.SubAccount;
import io.tyloo.unittest.repository.SubAccountRepository;
import io.tyloo.unittest.utils.UnitTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
AccountRecordServiceProxy accountRecordServiceProxy;
@Autowired
SubAccountRepository subAccountRepository;
@Override
@Tyloo(confirmMethod = "transferFromConfirm", cancelMethod = "transferFromCancel")
public void transferFrom(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("transferFrom called");
SubAccount subAccount = subAccountRepository.findById(accountId);
subAccount.setStatus(AccountStatus.TRANSFERING.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);
accountRecordServiceProxy.record(null, accountId, amount);
}
@Override
@Tyloo(propagation = Propagation.REQUIRED, confirmMethod = "transferToConfirm", cancelMethod = "transferToCancel")
public void transferTo(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("transferTo called");
SubAccount subAccount = subAccountRepository.findById(accountId);
subAccount.setStatus(AccountStatus.TRANSFERING.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);
}
@Override
@Tyloo(confirmMethod = "transferFromConfirm", cancelMethod = "transferFromCancel")
public void transferFromWithMultipleTier(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("transferFromWithMultipleTier called");
SubAccount subAccount = subAccountRepository.findById(accountId);
subAccount.setStatus(AccountStatus.TRANSFERING.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);
accountRecordServiceProxy.record(null, accountId, amount);
}
@Override
@Tyloo(confirmMethod = "transferToConfirm", cancelMethod = "transferToCancel")
public void transferToWithMultipleTier(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("transferToWithMultipleTier called");
SubAccount subAccount = subAccountRepository.findById(accountId);
subAccount.setStatus(AccountStatus.TRANSFERING.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);
accountRecordServiceProxy.record(null, accountId, amount);
}
@Override
@Tyloo(propagation = Propagation.REQUIRES_NEW, confirmMethod = "transferToConfirmWithNoTransactionContext", cancelMethod = "transferToCancelWithNoTransactionContext")
public void transferToWithNoTransactionContext(long accountId, int amount) {
System.out.println("transferToWithNoTransactionContext called");
SubAccount subAccount = subAccountRepository.findById(accountId);
subAccount.setStatus(AccountStatus.TRANSFERING.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);
accountRecordServiceProxy.record(null, accountId, amount);
}
public void transferFromConfirm(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("transferFromConfirm called");
SubAccount subAccount = subAccountRepository.findById(accountId);
subAccount.setStatus(AccountStatus.NORMAL.getId());
}
public void transferFromCancel(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("transferFromCancel called");
SubAccount subAccount = subAccountRepository.findById(accountId);
if (subAccount.getStatus() == AccountStatus.TRANSFERING.getId()) {
subAccount.setStatus(AccountStatus.NORMAL.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() + amount);
}
}
@Override
public void transferToConfirm(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("transferToConfirm called");
if (UnitTest.CONFIRMING_EXCEPTION) {
throw new RuntimeException("transferToConfirm confirm failed.");
}
SubAccount subAccount = subAccountRepository.findById(accountId);
subAccount.setStatus(AccountStatus.NORMAL.getId());
}
@Override
public void transferToCancel(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("transferToCancel called");
SubAccount subAccount = subAccountRepository.findById(accountId);
if (subAccount.getStatus() == AccountStatus.TRANSFERING.getId()) {
subAccount.setStatus(AccountStatus.NORMAL.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);
}
}
public void transferToConfirmWithNoTransactionContext(long accountId, int amount) {
System.out.println("transferToConfirmWithNoTransactionContext called");
SubAccount subAccount = subAccountRepository.findById(accountId);
subAccount.setStatus(AccountStatus.NORMAL.getId());
}
public void transferToCancelWithNoTransactionContext(long accountId, int amount) {
System.out.println("transferToCancelWithNoTransactionContext called");
SubAccount subAccount = subAccountRepository.findById(accountId);
if (subAccount.getStatus() == AccountStatus.TRANSFERING.getId()) {
subAccount.setStatus(AccountStatus.NORMAL.getId());
subAccount.setBalanceAmount(subAccount.getBalanceAmount() - amount);
}
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/thirdservice/AccountRecordService.java
================================================
package io.tyloo.unittest.thirdservice;
import io.tyloo.api.TransactionContext;
public interface AccountRecordService {
public void record(TransactionContext transactionContext, long accountId, int amount);
void recordConfirm(TransactionContext transactionContext, long accountId, int amount);
void recordCancel(TransactionContext transactionContext, long accountId, int amount);
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/thirdservice/AccountRecordServiceImpl.java
================================================
package io.tyloo.unittest.thirdservice;
import io.tyloo.api.Tyloo;
import io.tyloo.api.TransactionContext;
import io.tyloo.unittest.entity.AccountRecord;
import io.tyloo.unittest.entity.AccountStatus;
import io.tyloo.unittest.repository.AccountRecordRepository;
import io.tyloo.unittest.utils.UnitTest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class AccountRecordServiceImpl implements AccountRecordService {
@Autowired
AccountRecordRepository accountRecordRepository;
@Override
@Tyloo(confirmMethod = "recordConfirm", cancelMethod = "recordCancel")
public void record(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("record");
AccountRecord accountRecord = accountRecordRepository.findById(accountId);
accountRecord.setBalanceAmount(amount);
accountRecord.setStatusId(AccountStatus.TRANSFERING.getId());
if (UnitTest.TRYING_EXCEPTION) {
throw new RuntimeException("record try failed.");
}
}
@Override
public void recordConfirm(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("recordConfirm");
AccountRecord accountRecord = accountRecordRepository.findById(accountId);
accountRecord.setStatusId(AccountStatus.NORMAL.getId());
}
@Override
public void recordCancel(TransactionContext transactionContext, long accountId, int amount) {
System.out.println("recordCancel");
if (UnitTest.TRYING_EXCEPTION) {
throw new RuntimeException("record cancel failed.");
}
AccountRecord accountRecord = accountRecordRepository.findById(accountId);
accountRecord.setBalanceAmount(accountRecord.getBalanceAmount() - amount);
accountRecord.setStatusId(AccountStatus.NORMAL.getId());
}
}
================================================
FILE: tyloo-unit-test/src/main/java/io/tyloo/unittest/utils/UnitTest.java
================================================
package io.tyloo.unittest.utils;
public class UnitTest {
public static volatile boolean CONFIRMING_EXCEPTION = false;
public static volatile boolean TRYING_EXCEPTION = false;
}
================================================
FILE: tyloo-unit-test/src/main/resources/tyloo-unit-test.xml
================================================
================================================
FILE: tyloo-unit-test/src/test/java/io/tyloo/unit/test/AbstractTestCase.java
================================================
package io.tyloo.unit.test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = {
"classpath:/tyloo-unit-test.xml","classpath:/tyloo.xml"})
public abstract class AbstractTestCase {
}
================================================
FILE: tyloo-unit-test/src/test/java/io/tyloo/unit/test/PerformanceTest.java
================================================
package io.tyloo.unit.test;
import io.tyloo.Participant;
import io.tyloo.Transaction;
import io.tyloo.common.TransactionType;
import io.tyloo.serializer.KryoPoolSerializer;
import io.tyloo.serializer.ObjectSerializer;
import io.tyloo.unittest.client.TransferService;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.*;
public class PerformanceTest extends AbstractTestCase {
@Autowired
private TransferService transferService;
@Test
public void performanceTest() {
long currentTime = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
transferService.performenceTuningTransfer();
}
long thenTime = System.currentTimeMillis();
System.out.println(thenTime - currentTime);
}
@Test
public void serializeTest() {
ObjectSerializer objectSerializer = new KryoPoolSerializer();
long currentTime = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
//
Transaction transaction = new Transaction(TransactionType.ROOT);
transaction.getAttachments().put("abc", new Participant());
byte[] bytes = objectSerializer.serialize(transaction);
Transaction transaction1 = (Transaction) objectSerializer.deserialize(bytes);
if (transaction.getVersion() != transaction1.getVersion()) {
throw new Error();
}
}
long thenTime = System.currentTimeMillis();
System.out.println(thenTime - currentTime);
}
@Test
public void testThreadPool() throws ExecutionException, InterruptedException {
ExecutorService executorService = new ThreadPoolExecutor(1, 2,
30L, TimeUnit.MILLISECONDS,
new SynchronousQueue());
Long startTime = System.currentTimeMillis();
List futures = new ArrayList();
for (int i = 0; i <= 1; i++) {
futures.add(executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println(Thread.currentThread().getName() + " done");
}
}));
}
for (Future future : futures) {
future.get();
}
System.out.println("cost time:" + (System.currentTimeMillis() - startTime));
}
}
================================================
FILE: tyloo-unit-test/src/test/java/io/tyloo/unit/test/ReflectionTest.java
================================================
package io.tyloo.unit.test;
import io.tyloo.Transaction;
import io.tyloo.serializer.JacksonJsonSerializer;
import io.tyloo.unittest.client.TransferService;
import io.tyloo.unittest.entity.SubAccount;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import java.lang.reflect.Method;
public class ReflectionTest extends AbstractTestCase {
@Autowired
private TransferService transferService;
@Test
public void test1() throws NoSuchMethodException {
transferService.performenceTuningTransfer();
Method transferMethod = TransferService.class.getMethod("transfer", long.class, long.class, int.class);
}
@Test
public void testJacksonSerializer() {
String json = "[\"Transaction\",{\"xid\":[\"TransactionXid\",{\"formatId\":1,\"globalTransactionId\":\"TQ5/1W2USTWZHjwV7JajxA==\",\"branchQualifier\":\"Kcjo0GaHTc6hIPpf3B7ARA==\"}],\"status\":\"TRYING\",\"transactionType\":\"ROOT\",\"retriedCount\":0,\"createTime\":[\"java.util.Date\",1575457808693],\"lastUpdateTime\":[\"java.util.Date\",1575457822864],\"version\":2,\"participants\":[\"java.util.ArrayList\",[[\"Participant\",{\"xid\":[\"TransactionXid\",{\"formatId\":1,\"globalTransactionId\":\"TQ5/1W2USTWZHjwV7JajxA==\",\"branchQualifier\":\"H1KzapA5SL+L2mu3D5tyRw==\"}],\"confirmInvocationContext\":[\"InvocationContext\",{\"targetClass\":\"TransferService\",\"methodName\":\"transferConfirm\",\"parameterTypes\":[\"long\",\"long\",\"int\"],\"args\":[\"[Ljava.lang.Object;\",[[\"java.lang.Long\",1],[\"java.lang.Long\",2],50]]}],\"cancelInvocationContext\":[\"InvocationContext\",{\"targetClass\":\"TransferService\",\"methodName\":\"transferCancel\",\"parameterTypes\":[\"long\",\"long\",\"int\"],\"args\":[\"[Ljava.lang.Object;\",[[\"java.lang.Long\",1],[\"java.lang.Long\",2],50]]}]}]]],\"attachments\":[\"java.util.concurrent.ConcurrentHashMap\",{}]}]";
JacksonJsonSerializer jacksonJsonSerializer = new JacksonJsonSerializer();
Transaction transaction = jacksonJsonSerializer.deserialize(json.getBytes());
SubAccount subAccount = new SubAccount(1l, 10);
byte[] bytes = jacksonJsonSerializer.serialize(transaction);
json = new String(bytes);
transaction = jacksonJsonSerializer.deserialize(bytes);
Assert.assertTrue(transaction != null);
}
}
================================================
FILE: tyloo-unit-test/src/test/java/io/tyloo/unit/test/TransferServiceTest.java
================================================
package io.tyloo.unit.test;
import io.tyloo.SystemException;
import io.tyloo.recover.TylooTransactionRecovery;
import io.tyloo.unittest.client.TransferService;
import io.tyloo.unittest.entity.AccountRecord;
import io.tyloo.unittest.entity.AccountStatus;
import io.tyloo.unittest.entity.SubAccount;
import io.tyloo.unittest.repository.AccountRecordRepository;
import io.tyloo.unittest.repository.SubAccountRepository;
import io.tyloo.unittest.utils.UnitTest;
import org.junit.Assert;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
public class TransferServiceTest extends AbstractTestCase {
@Autowired
private TransferService transferService;
@Autowired
SubAccountRepository subAccountRepository;
@Autowired
AccountRecordRepository accountRecordRepository;
@Autowired
TylooTransactionRecovery tylooTransactionRecovery;
@Test
public void testTransfer() throws InterruptedException {
//given
buildAccount();
//when
transferService.transfer(1, 2, 50);
//then
SubAccount subAccountFrom = subAccountRepository.findById(1L);
SubAccount subAccountTo = subAccountRepository.findById(2L);
Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountFrom.getBalanceAmount() == 50);
Assert.assertTrue(subAccountTo.getBalanceAmount() == 250);
}
@Test
public void testTransferWithMandatoryPropagtion() throws InterruptedException {
//given
buildAccount();
//when
try {
transferService.transferWithMandatoryPropagation(1, 2, 50);
} catch (SystemException e) {
Assert.assertTrue(e.getMessage().startsWith("no active tyloo transaction while propagation is mandatory for method"));
}
}
@Test
public void testTransferWithMultipleTier() {
//given
buildAccount();
//when
transferService.transferWithMultipleTier(1, 2, 50);
//then
SubAccount subAccountFrom = subAccountRepository.findById(1L);
SubAccount subAccountTo = subAccountRepository.findById(2L);
Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountFrom.getBalanceAmount() == 50);
Assert.assertTrue(subAccountTo.getBalanceAmount() == 250);
AccountRecord accountRecordTo = accountRecordRepository.findById(2L);
Assert.assertTrue(accountRecordTo.getBalanceAmount() == 50);
Assert.assertTrue(accountRecordTo.getStatusId() == AccountStatus.NORMAL.getId());
}
@Test
public void testTransferWithMultiplerConsumer() {
//given
buildAccount();
//when
transferService.transferWithMultipleConsumer(1, 2, 70);
//then
SubAccount subAccountFrom = subAccountRepository.findById(1L);
SubAccount subAccountTo = subAccountRepository.findById(2L);
Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountFrom.getBalanceAmount() == 30);
Assert.assertTrue(subAccountTo.getBalanceAmount() == 270);
}
@Test
public void testTransferWithOnlyTryAndMultipleConsumer() {
//given
buildAccount();
//when
transferService.transferWithOnlyTryAndMultipleConsumer(1, 2, 70);
//then
SubAccount subAccountFrom = subAccountRepository.findById(1L);
SubAccount subAccountTo = subAccountRepository.findById(2L);
Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountFrom.getBalanceAmount() == 30);
Assert.assertTrue(subAccountTo.getBalanceAmount() == 270);
}
@Test
public void testTransferWithNoTransactionContext() {
//given
buildAccount();
//when
transferService.transferWithNoTransactionContext(1, 2, 70);
//then
SubAccount subAccountTo = subAccountRepository.findById(2L);
Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountTo.getBalanceAmount() == 270);
}
@Test
public void testTryingRecovery() {
//given
UnitTest.TRYING_EXCEPTION = true;
try {
//given
buildAccount();
//when
transferService.transferWithMultipleConsumer(1, 2, 70);
} catch (Throwable e) {
}
System.out.println("begin recovery");
//when
UnitTest.TRYING_EXCEPTION = false;
//then
AccountRecord accountRecord = accountRecordRepository.findById(1L);
Assert.assertTrue(accountRecord.getBalanceAmount() == 70);
try {
//waiting the auto recovery schedule
Thread.sleep(1000 * 10);
} catch (InterruptedException e) {
throw new Error(e);
}
Assert.assertTrue(accountRecord.getBalanceAmount() == 0);
}
@Test
public void testConfirmingRecovery() {
//given
UnitTest.CONFIRMING_EXCEPTION = true;
try {
//given
buildAccount();
//when
transferService.transferWithMultipleConsumer(1, 2, 70);
} catch (Throwable e) {
}
System.out.println("begin recovery");
//when
UnitTest.CONFIRMING_EXCEPTION = false;
//then
AccountRecord accountRecord = accountRecordRepository.findById(1L);
Assert.assertTrue(accountRecord.getBalanceAmount() == 70);
try {
//waiting the auto recovery schedule
Thread.sleep(1000 * 10L);
} catch (InterruptedException e) {
throw new Error(e);
}
Assert.assertTrue(accountRecord.getBalanceAmount() == 70);
SubAccount subAccountFrom = subAccountRepository.findById(1L);
SubAccount subAccountTo = subAccountRepository.findById(2L);
Assert.assertTrue(subAccountFrom.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountTo.getStatus() == AccountStatus.NORMAL.getId());
Assert.assertTrue(subAccountFrom.getBalanceAmount() == 30);
Assert.assertTrue(subAccountTo.getBalanceAmount() == 270);
}
private void buildAccount() {
SubAccount subAccountFrom = subAccountRepository.findById(1L);
subAccountFrom.setBalanceAmount(100);
subAccountFrom.setStatus(AccountStatus.NORMAL.getId());
SubAccount subAccountTo = subAccountRepository.findById(2L);
subAccountTo.setBalanceAmount(200);
subAccountTo.setStatus(AccountStatus.NORMAL.getId());
AccountRecord accountRecordFrom = accountRecordRepository.findById(1L);
accountRecordFrom.setBalanceAmount(0);
accountRecordFrom.setStatusId(AccountStatus.NORMAL.getId());
AccountRecord accountRecordTo = accountRecordRepository.findById(2L);
accountRecordTo.setBalanceAmount(0);
accountRecordTo.setStatusId(AccountStatus.NORMAL.getId());
}
}
================================================
FILE: tyloo-unit-test/src/test/resources/log4j.properties
================================================
# Set root logger level to DEBUG and its only appender to stdout.
log4j.rootLogger=INFO, stdout
# stdout is set to be a ConsoleAppender.
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
# stdout uses PatternLayout.
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
#log4j.appender.stdout.Target=System.out
log4j.logger.com.ibatis=debug,stdout
log4j.logger.com.ibatis.common.jdbc.SimpleDataSource=debug,stdout
log4j.logger.com.ibatis.common.jdbc.ScriptRunner=debug,stdout
log4j.logger.com.ibatis.sqlmap.engine.impl.SqlMapClientDelegate=debug,stdout
log4j.logger.java.sql.Connection=debug,stdout
log4j.logger.java.sql.Statement=debug,stdout
log4j.logger.java.sql.PreparedStatement=debug,stdout